Procházet zdrojové kódy

Use `As` and `ImplicitAs` interfaces for conversions. (#4209)

Add these interfaces to the core library. For now, they're two separate
interfaces because we don't yet support one interface extending another.

This collapses a lot of the layering in check: for example, the call
building logic depends on implicit conversions, conversions now depend
on the overloaded operator machinery, and that machinery depends on
building calls.

In passing, improve the diagnostics for failing to find a name required
from the prelude. Also convert all the transitively-called code from
`NodeId` to `LocId` given the latter is what the conversion machinery
has available.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Richard Smith před 1 rokem
rodič
revize
187a3608df
100 změnil soubory, kde provedl 1389 přidání a 369 odebrání
  1. 2 0
      common/check_internal.cpp
  2. 1 0
      core/prelude/operators.carbon
  3. 14 0
      core/prelude/operators/as.carbon
  4. 6 52
      toolchain/check/BUILD
  5. 17 20
      toolchain/check/call.cpp
  6. 2 3
      toolchain/check/call.h
  7. 11 16
      toolchain/check/context.cpp
  8. 9 7
      toolchain/check/context.h
  9. 24 17
      toolchain/check/convert.cpp
  10. 4 4
      toolchain/check/deduce.cpp
  11. 1 1
      toolchain/check/deduce.h
  12. 6 6
      toolchain/check/handle_operator.cpp
  13. 74 61
      toolchain/check/member_access.cpp
  14. 9 6
      toolchain/check/member_access.h
  15. 30 67
      toolchain/check/operator.cpp
  16. 17 7
      toolchain/check/operator.h
  17. 1 0
      toolchain/check/testdata/alias/fail_bool_value.carbon
  18. 1 0
      toolchain/check/testdata/alias/fail_builtins.carbon
  19. 1 0
      toolchain/check/testdata/alias/fail_control_flow.carbon
  20. 2 1
      toolchain/check/testdata/alias/no_prelude/fail_aliased_name_in_diag.carbon
  21. 1 0
      toolchain/check/testdata/array/array_in_place.carbon
  22. 1 0
      toolchain/check/testdata/array/array_vs_tuple.carbon
  23. 1 0
      toolchain/check/testdata/array/assign_return_value.carbon
  24. 1 0
      toolchain/check/testdata/array/assign_var.carbon
  25. 1 0
      toolchain/check/testdata/array/base.carbon
  26. 1 0
      toolchain/check/testdata/array/canonicalize_index.carbon
  27. 1 0
      toolchain/check/testdata/array/fail_bound_negative.carbon
  28. 93 5
      toolchain/check/testdata/array/fail_bound_overflow.carbon
  29. 1 0
      toolchain/check/testdata/array/fail_incomplete_element.carbon
  30. 93 4
      toolchain/check/testdata/array/fail_invalid_type.carbon
  31. 1 0
      toolchain/check/testdata/array/fail_out_of_bound.carbon
  32. 1 0
      toolchain/check/testdata/array/fail_out_of_bound_non_literal.carbon
  33. 152 57
      toolchain/check/testdata/array/fail_type_mismatch.carbon
  34. 1 0
      toolchain/check/testdata/array/function_param.carbon
  35. 1 0
      toolchain/check/testdata/array/generic_empty.carbon
  36. 1 0
      toolchain/check/testdata/array/index_not_literal.carbon
  37. 1 0
      toolchain/check/testdata/array/nine_elements.carbon
  38. 100 5
      toolchain/check/testdata/as/adapter_conversion.carbon
  39. 1 0
      toolchain/check/testdata/as/as_type.carbon
  40. 1 0
      toolchain/check/testdata/as/basic.carbon
  41. 109 20
      toolchain/check/testdata/as/fail_no_conversion.carbon
  42. 95 7
      toolchain/check/testdata/as/fail_not_type.carbon
  43. 1 0
      toolchain/check/testdata/as/identity.carbon
  44. 303 0
      toolchain/check/testdata/as/overloaded.carbon
  45. 1 0
      toolchain/check/testdata/basics/builtin_types.carbon
  46. 1 0
      toolchain/check/testdata/basics/fail_bad_run.carbon
  47. 1 0
      toolchain/check/testdata/basics/fail_bad_run_2.carbon
  48. 91 2
      toolchain/check/testdata/basics/fail_non_type_as_type.carbon
  49. 1 0
      toolchain/check/testdata/basics/fail_numeric_literal_overflow.carbon
  50. 1 0
      toolchain/check/testdata/basics/fail_qualifier_unsupported.carbon
  51. 2 0
      toolchain/check/testdata/basics/multifile.carbon
  52. 1 0
      toolchain/check/testdata/basics/numeric_literals.carbon
  53. 1 0
      toolchain/check/testdata/basics/parens.carbon
  54. 1 0
      toolchain/check/testdata/basics/run.carbon
  55. 1 0
      toolchain/check/testdata/basics/run_i32.carbon
  56. 4 0
      toolchain/check/testdata/basics/type_literals.carbon
  57. 2 0
      toolchain/check/testdata/builtins/bool/make_type.carbon
  58. 2 0
      toolchain/check/testdata/builtins/float/add.carbon
  59. 2 0
      toolchain/check/testdata/builtins/float/div.carbon
  60. 2 0
      toolchain/check/testdata/builtins/float/eq.carbon
  61. 1 0
      toolchain/check/testdata/builtins/float/greater.carbon
  62. 1 0
      toolchain/check/testdata/builtins/float/greater_eq.carbon
  63. 1 0
      toolchain/check/testdata/builtins/float/less.carbon
  64. 1 0
      toolchain/check/testdata/builtins/float/less_eq.carbon
  65. 3 0
      toolchain/check/testdata/builtins/float/make_type.carbon
  66. 2 0
      toolchain/check/testdata/builtins/float/mul.carbon
  67. 2 0
      toolchain/check/testdata/builtins/float/negate.carbon
  68. 2 0
      toolchain/check/testdata/builtins/float/neq.carbon
  69. 2 0
      toolchain/check/testdata/builtins/float/sub.carbon
  70. 1 0
      toolchain/check/testdata/builtins/int/and.carbon
  71. 1 0
      toolchain/check/testdata/builtins/int/complement.carbon
  72. 2 0
      toolchain/check/testdata/builtins/int/eq.carbon
  73. 1 0
      toolchain/check/testdata/builtins/int/greater.carbon
  74. 1 0
      toolchain/check/testdata/builtins/int/greater_eq.carbon
  75. 2 0
      toolchain/check/testdata/builtins/int/left_shift.carbon
  76. 1 0
      toolchain/check/testdata/builtins/int/less.carbon
  77. 1 0
      toolchain/check/testdata/builtins/int/less_eq.carbon
  78. 2 0
      toolchain/check/testdata/builtins/int/make_type_32.carbon
  79. 5 0
      toolchain/check/testdata/builtins/int/make_type_signed.carbon
  80. 5 0
      toolchain/check/testdata/builtins/int/make_type_unsigned.carbon
  81. 1 0
      toolchain/check/testdata/builtins/int/neq.carbon
  82. 1 0
      toolchain/check/testdata/builtins/int/or.carbon
  83. 3 0
      toolchain/check/testdata/builtins/int/right_shift.carbon
  84. 3 0
      toolchain/check/testdata/builtins/int/sadd.carbon
  85. 3 0
      toolchain/check/testdata/builtins/int/sdiv.carbon
  86. 3 0
      toolchain/check/testdata/builtins/int/smod.carbon
  87. 2 0
      toolchain/check/testdata/builtins/int/smul.carbon
  88. 3 0
      toolchain/check/testdata/builtins/int/snegate.carbon
  89. 2 0
      toolchain/check/testdata/builtins/int/ssub.carbon
  90. 3 0
      toolchain/check/testdata/builtins/int/uadd.carbon
  91. 3 0
      toolchain/check/testdata/builtins/int/udiv.carbon
  92. 3 0
      toolchain/check/testdata/builtins/int/umod.carbon
  93. 2 0
      toolchain/check/testdata/builtins/int/umul.carbon
  94. 3 0
      toolchain/check/testdata/builtins/int/unegate.carbon
  95. 2 0
      toolchain/check/testdata/builtins/int/usub.carbon
  96. 1 0
      toolchain/check/testdata/builtins/int/xor.carbon
  97. 2 1
      toolchain/check/testdata/builtins/print.carbon
  98. 2 0
      toolchain/check/testdata/class/adapt.carbon
  99. 1 0
      toolchain/check/testdata/class/base.carbon
  100. 1 0
      toolchain/check/testdata/class/base_field.carbon

+ 2 - 0
common/check_internal.cpp

@@ -23,6 +23,8 @@ ExitingStream::~ExitingStream() {
 
 auto ExitingStream::Done() -> void {
   buffer_ << "\n";
+  buffer_.flush();
+
   // Register another signal handler to print the buffered message. This is
   // because we want it at the bottom of output, after LLVM's builtin stack
   // output, rather than the top.

+ 1 - 0
core/prelude/operators.carbon

@@ -5,5 +5,6 @@
 package Core library "prelude/operators";
 
 export import library "prelude/operators/arithmetic";
+export import library "prelude/operators/as";
 export import library "prelude/operators/bitwise";
 export import library "prelude/operators/comparison";

+ 14 - 0
core/prelude/operators/as.carbon

@@ -0,0 +1,14 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+package Core library "prelude/operators/as";
+
+interface As(Dest:! type) {
+  fn Convert[self: Self]() -> Dest;
+}
+
+interface ImplicitAs(Dest:! type) {
+  // TODO: extend As(Dest);
+  fn Convert[self: Self]() -> Dest;
+}

+ 6 - 52
toolchain/check/BUILD

@@ -15,6 +15,7 @@ filegroup(
 cc_library(
     name = "context",
     srcs = [
+        "call.cpp",
         "context.cpp",
         "convert.cpp",
         "decl_name_stack.cpp",
@@ -26,13 +27,16 @@ cc_library(
         "import.cpp",
         "import_ref.cpp",
         "inst_block_stack.cpp",
+        "member_access.cpp",
         "merge.cpp",
         "modifiers.cpp",
         "name_component.cpp",
+        "operator.cpp",
         "return.cpp",
         "subst.cpp",
     ],
     hdrs = [
+        "call.h",
         "context.h",
         "convert.h",
         "decl_introducer_state.h",
@@ -47,9 +51,11 @@ cc_library(
         "import_ref.h",
         "inst_block_stack.h",
         "keyword_modifier_set.h",
+        "member_access.h",
         "merge.h",
         "modifiers.h",
         "name_component.h",
+        "operator.h",
         "param_and_arg_refs_stack.h",
         "pending_block.h",
         "return.h",
@@ -93,12 +99,9 @@ cc_library(
     ]),
     hdrs = ["check.h"],
     deps = [
-        ":call",
         ":context",
         ":impl",
         ":interface",
-        ":member_access",
-        ":operator",
         ":pointer_dereference",
         ":sem_ir_diagnostic_converter",
         "//common:check",
@@ -139,21 +142,6 @@ cc_fuzz_test(
     ],
 )
 
-cc_library(
-    name = "call",
-    srcs = ["call.cpp"],
-    hdrs = ["call.h"],
-    deps = [
-        ":context",
-        "//common:check",
-        "//toolchain/base:kind_switch",
-        "//toolchain/sem_ir:file",
-        "//toolchain/sem_ir:ids",
-        "//toolchain/sem_ir:inst",
-        "//toolchain/sem_ir:inst_kind",
-    ],
-)
-
 cc_library(
     name = "generic_region_stack",
     srcs = ["generic_region_stack.cpp"],
@@ -200,23 +188,6 @@ cc_library(
     ],
 )
 
-cc_library(
-    name = "member_access",
-    srcs = ["member_access.cpp"],
-    hdrs = ["member_access.h"],
-    deps = [
-        ":context",
-        "//common:check",
-        "//toolchain/base:kind_switch",
-        "//toolchain/diagnostics:diagnostic_emitter",
-        "//toolchain/sem_ir:file",
-        "//toolchain/sem_ir:ids",
-        "//toolchain/sem_ir:inst",
-        "//toolchain/sem_ir:inst_kind",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
 cc_library(
     name = "node_stack",
     srcs = ["node_stack.cpp"],
@@ -233,23 +204,6 @@ cc_library(
     ],
 )
 
-cc_library(
-    name = "operator",
-    srcs = ["operator.cpp"],
-    hdrs = ["operator.h"],
-    deps = [
-        ":call",
-        ":context",
-        ":member_access",
-        "//common:check",
-        "//toolchain/parse:node_kind",
-        "//toolchain/sem_ir:file",
-        "//toolchain/sem_ir:ids",
-        "//toolchain/sem_ir:inst",
-        "//toolchain/sem_ir:inst_kind",
-    ],
-)
-
 cc_library(
     name = "pointer_dereference",
     srcs = ["pointer_dereference.cpp"],

+ 17 - 20
toolchain/check/call.cpp

@@ -9,7 +9,6 @@
 #include "toolchain/check/convert.h"
 #include "toolchain/check/deduce.h"
 #include "toolchain/check/function.h"
-#include "toolchain/check/generic.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/typed_insts.h"
@@ -18,7 +17,7 @@ namespace Carbon::Check {
 
 // Performs a call where the callee is the name of a generic class, such as
 // `Vector(i32)`.
-static auto PerformCallToGenericClass(Context& context, Parse::NodeId node_id,
+static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id,
                                       SemIR::InstId callee_id,
                                       SemIR::ClassId class_id,
                                       llvm::ArrayRef<SemIR::InstId> arg_ids)
@@ -32,9 +31,9 @@ static auto PerformCallToGenericClass(Context& context, Parse::NodeId node_id,
 
   // Convert the arguments to match the parameters.
   auto converted_args_id = ConvertCallArgs(
-      context, node_id, /*self_id=*/SemIR::InstId::Invalid, arg_ids,
+      context, loc_id, /*self_id=*/SemIR::InstId::Invalid, arg_ids,
       /*return_storage_id=*/SemIR::InstId::Invalid, class_info, specific_id);
-  return context.AddInst<SemIR::Call>(node_id,
+  return context.AddInst<SemIR::Call>(loc_id,
                                       {.type_id = SemIR::TypeId::TypeType,
                                        .callee_id = callee_id,
                                        .args_id = converted_args_id});
@@ -43,8 +42,7 @@ static auto PerformCallToGenericClass(Context& context, Parse::NodeId node_id,
 // Performs a call where the callee is the name of a generic interface, such as
 // `AddWith(i32)`.
 // TODO: Refactor with PerformCallToGenericClass.
-static auto PerformCallToGenericInterface(Context& context,
-                                          Parse::NodeId node_id,
+static auto PerformCallToGenericInterface(Context& context, SemIR::LocId loc_id,
                                           SemIR::InstId callee_id,
                                           SemIR::InterfaceId interface_id,
                                           llvm::ArrayRef<SemIR::InstId> arg_ids)
@@ -58,18 +56,17 @@ static auto PerformCallToGenericInterface(Context& context,
 
   // Convert the arguments to match the parameters.
   auto converted_args_id = ConvertCallArgs(
-      context, node_id, /*self_id=*/SemIR::InstId::Invalid, arg_ids,
+      context, loc_id, /*self_id=*/SemIR::InstId::Invalid, arg_ids,
       /*return_storage_id=*/SemIR::InstId::Invalid, interface_info,
       specific_id);
-  return context.AddInst<SemIR::Call>(node_id,
+  return context.AddInst<SemIR::Call>(loc_id,
                                       {.type_id = SemIR::TypeId::TypeType,
                                        .callee_id = callee_id,
                                        .args_id = converted_args_id});
 }
 
-auto PerformCall(Context& context, Parse::NodeId node_id,
-                 SemIR::InstId callee_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
-    -> SemIR::InstId {
+auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
+                 llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
   // Identify the function we're calling.
   auto callee_function = GetCalleeFunction(context.sem_ir(), callee_id);
   if (!callee_function.function_id.is_valid()) {
@@ -77,11 +74,11 @@ auto PerformCall(Context& context, Parse::NodeId node_id,
         context.types().GetAsInst(context.insts().Get(callee_id).type_id());
     CARBON_KIND_SWITCH(type_inst) {
       case CARBON_KIND(SemIR::GenericClassType generic_class): {
-        return PerformCallToGenericClass(context, node_id, callee_id,
+        return PerformCallToGenericClass(context, loc_id, callee_id,
                                          generic_class.class_id, arg_ids);
       }
       case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): {
-        return PerformCallToGenericInterface(context, node_id, callee_id,
+        return PerformCallToGenericInterface(context, loc_id, callee_id,
                                              generic_interface.interface_id,
                                              arg_ids);
       }
@@ -90,7 +87,7 @@ auto PerformCall(Context& context, Parse::NodeId node_id,
           CARBON_DIAGNOSTIC(CallToNonCallable, Error,
                             "Value of type `{0}` is not callable.",
                             SemIR::TypeId);
-          context.emitter().Emit(node_id, CallToNonCallable,
+          context.emitter().Emit(loc_id, CallToNonCallable,
                                  context.insts().Get(callee_id).type_id());
         }
         return SemIR::InstId::BuiltinError;
@@ -104,7 +101,7 @@ auto PerformCall(Context& context, Parse::NodeId node_id,
   auto specific_id = SemIR::SpecificId::Invalid;
   if (callable.generic_id.is_valid()) {
     specific_id = DeduceGenericCallArguments(
-        context, node_id, callable.generic_id, callee_function.specific_id,
+        context, loc_id, callable.generic_id, callee_function.specific_id,
         callable.implicit_param_refs_id, callable.param_refs_id,
         callee_function.self_id, arg_ids);
     if (!specific_id.is_valid()) {
@@ -128,7 +125,7 @@ auto PerformCall(Context& context, Parse::NodeId node_id,
       // Tentatively put storage for a temporary in the function's return slot.
       // This will be replaced if necessary when we perform initialization.
       return_storage_id = context.AddInst<SemIR::TemporaryStorage>(
-          node_id, {.type_id = return_info.type_id});
+          loc_id, {.type_id = return_info.type_id});
       break;
     case SemIR::InitRepr::None:
       // For functions with an implicit return type, the return type is the
@@ -148,12 +145,12 @@ auto PerformCall(Context& context, Parse::NodeId node_id,
 
   // Convert the arguments to match the parameters.
   auto converted_args_id = ConvertCallArgs(
-      context, node_id, callee_function.self_id, arg_ids, return_storage_id,
+      context, loc_id, callee_function.self_id, arg_ids, return_storage_id,
       CalleeParamsInfo(callable), specific_id);
   auto call_inst_id =
-      context.AddInst<SemIR::Call>(node_id, {.type_id = return_info.type_id,
-                                             .callee_id = callee_id,
-                                             .args_id = converted_args_id});
+      context.AddInst<SemIR::Call>(loc_id, {.type_id = return_info.type_id,
+                                            .callee_id = callee_id,
+                                            .args_id = converted_args_id});
 
   return call_inst_id;
 }

+ 2 - 3
toolchain/check/call.h

@@ -11,9 +11,8 @@
 namespace Carbon::Check {
 
 // Checks and builds SemIR for a call to `callee_id` with arguments `args_id`.
-auto PerformCall(Context& context, Parse::NodeId node_id,
-                 SemIR::InstId callee_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
-    -> SemIR::InstId;
+auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
+                 llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId;
 
 }  // namespace Carbon::Check
 

+ 11 - 16
toolchain/check/context.cpp

@@ -347,7 +347,7 @@ auto Context::LookupNameInExactScope(SemIRLoc loc, SemIR::NameId name_id,
   return SemIR::InstId::Invalid;
 }
 
-auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
+auto Context::LookupQualifiedName(SemIRLoc loc, SemIR::NameId name_id,
                                   LookupScope scope, bool required)
     -> LookupResult {
   llvm::SmallVector<LookupScope> scopes = {scope};
@@ -362,7 +362,7 @@ auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
     has_error |= name_scope.has_error;
 
     auto scope_result_id =
-        LookupNameInExactScope(node_id, name_id, scope_id, name_scope);
+        LookupNameInExactScope(loc, name_id, scope_id, name_scope);
     if (!scope_result_id.is_valid()) {
       // Nothing found in this scope: also look in its extended scopes.
       auto extended = name_scope.extended_scopes;
@@ -385,7 +385,7 @@ auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
           NameAmbiguousDueToExtend, Error,
           "Ambiguous use of name `{0}` found in multiple extended scopes.",
           SemIR::NameId);
-      emitter_->Emit(node_id, NameAmbiguousDueToExtend, name_id);
+      emitter_->Emit(loc, NameAmbiguousDueToExtend, name_id);
       // TODO: Add notes pointing to the scopes.
       return {.specific_id = SemIR::SpecificId::Invalid,
               .inst_id = SemIR::InstId::BuiltinError};
@@ -397,7 +397,7 @@ auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
 
   if (required && !result.inst_id.is_valid()) {
     if (!has_error) {
-      DiagnoseNameNotFound(node_id, name_id);
+      DiagnoseNameNotFound(loc, name_id);
     }
     return {.specific_id = SemIR::SpecificId::Invalid,
             .inst_id = SemIR::InstId::BuiltinError};
@@ -633,10 +633,8 @@ namespace {
 //   complete.
 class TypeCompleter {
  public:
-  TypeCompleter(
-      Context& context,
-      std::optional<llvm::function_ref<auto()->Context::DiagnosticBuilder>>
-          diagnoser)
+  TypeCompleter(Context& context,
+                std::optional<Context::BuildDiagnosticFn> diagnoser)
       : context_(context), diagnoser_(diagnoser) {}
 
   // Attempts to complete the given type. Returns true if it is now complete,
@@ -1019,21 +1017,18 @@ class TypeCompleter {
 
   Context& context_;
   llvm::SmallVector<WorkItem> work_list_;
-  std::optional<llvm::function_ref<auto()->Context::DiagnosticBuilder>>
-      diagnoser_;
+  std::optional<Context::BuildDiagnosticFn> diagnoser_;
 };
 }  // namespace
 
-auto Context::TryToCompleteType(
-    SemIR::TypeId type_id,
-    std::optional<llvm::function_ref<auto()->DiagnosticBuilder>> diagnoser)
+auto Context::TryToCompleteType(SemIR::TypeId type_id,
+                                std::optional<BuildDiagnosticFn> diagnoser)
     -> bool {
   return TypeCompleter(*this, diagnoser).Complete(type_id);
 }
 
-auto Context::TryToDefineType(
-    SemIR::TypeId type_id,
-    std::optional<llvm::function_ref<auto()->DiagnosticBuilder>> diagnoser)
+auto Context::TryToDefineType(SemIR::TypeId type_id,
+                              std::optional<BuildDiagnosticFn> diagnoser)
     -> bool {
   if (!TryToCompleteType(type_id, diagnoser)) {
     return false;

+ 9 - 7
toolchain/check/context.h

@@ -51,6 +51,11 @@ class Context {
  public:
   using DiagnosticEmitter = Carbon::DiagnosticEmitter<SemIRLoc>;
   using DiagnosticBuilder = DiagnosticEmitter::DiagnosticBuilder;
+  // A function that forms a diagnostic for some kind of problem. The
+  // DiagnosticBuilder is returned rather than emitted so that the caller can
+  // add contextual notes as appropriate.
+  using BuildDiagnosticFn =
+      llvm::function_ref<auto()->Context::DiagnosticBuilder>;
 
   // Stores references for work.
   explicit Context(const Lex::TokenizedBuffer& tokens,
@@ -172,7 +177,7 @@ class Context {
 
   // Performs a qualified name lookup in a specified scope and in scopes that
   // it extends, returning the referenced instruction.
-  auto LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
+  auto LookupQualifiedName(SemIRLoc loc, SemIR::NameId name_id,
                            LookupScope scope, bool required = true)
       -> LookupResult;
 
@@ -275,8 +280,7 @@ class Context {
   // describe the reason why the type is not complete.
   auto TryToCompleteType(
       SemIR::TypeId type_id,
-      std::optional<llvm::function_ref<auto()->DiagnosticBuilder>> diagnoser =
-          std::nullopt) -> bool;
+      std::optional<BuildDiagnosticFn> diagnoser = std::nullopt) -> bool;
 
   // Attempts to complete and define the type `type_id`. Returns `true` if the
   // type is defined, or `false` if no definition is available. A defined type
@@ -286,14 +290,12 @@ class Context {
   // complete before they are fully defined.
   auto TryToDefineType(
       SemIR::TypeId type_id,
-      std::optional<llvm::function_ref<auto()->DiagnosticBuilder>> diagnoser =
-          std::nullopt) -> bool;
+      std::optional<BuildDiagnosticFn> diagnoser = std::nullopt) -> bool;
 
   // Returns the type `type_id` as a complete type, or produces an incomplete
   // type error and returns an error type. This is a convenience wrapper around
   // TryToCompleteType.
-  auto AsCompleteType(SemIR::TypeId type_id,
-                      llvm::function_ref<auto()->DiagnosticBuilder> diagnoser)
+  auto AsCompleteType(SemIR::TypeId type_id, BuildDiagnosticFn diagnoser)
       -> SemIR::TypeId {
     return TryToCompleteType(type_id, diagnoser) ? type_id
                                                  : SemIR::TypeId::Error;

+ 24 - 17
toolchain/check/convert.cpp

@@ -12,6 +12,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/check/context.h"
+#include "toolchain/check/operator.h"
 #include "toolchain/sem_ir/copy_on_write_block.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/generic.h"
@@ -958,25 +959,31 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
     return expr_id;
   }
 
-  // If the types don't match at this point, we can't perform the conversion.
-  // TODO: Look for an `ImplicitAs` impl, or an `As` impl in the case where
-  // `target.kind == ConversionTarget::ExplicitAs`.
+  // If this is not a builtin conversion, try an `ImplicitAs` conversion.
   SemIR::Inst expr = sem_ir.insts().Get(expr_id);
   if (expr.type_id() != target.type_id) {
-    CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error,
-                      "Cannot implicitly convert from `{0}` to `{1}`.",
-                      SemIR::TypeId, SemIR::TypeId);
-    CARBON_DIAGNOSTIC(ExplicitAsConversionFailure, Error,
-                      "Cannot convert from `{0}` to `{1}` with `as`.",
-                      SemIR::TypeId, SemIR::TypeId);
-    context.emitter()
-        .Build(loc_id,
-               target.kind == ConversionTarget::ExplicitAs
-                   ? ExplicitAsConversionFailure
-                   : ImplicitAsConversionFailure,
-               expr.type_id(), target.type_id)
-        .Emit();
-    return SemIR::InstId::BuiltinError;
+    SemIR::InstId interface_args[] = {
+        context.types().GetInstId(target.type_id)};
+    Operator op = {
+        .interface_name = target.kind == ConversionTarget::ExplicitAs
+                              ? llvm::StringLiteral("As")
+                              : llvm::StringLiteral("ImplicitAs"),
+        .interface_args_ref = interface_args,
+        .op_name = "Convert",
+    };
+    expr_id = BuildUnaryOperator(context, loc_id, op, expr_id, [&] {
+      CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error,
+                        "Cannot implicitly convert from `{0}` to `{1}`.",
+                        SemIR::TypeId, SemIR::TypeId);
+      CARBON_DIAGNOSTIC(ExplicitAsConversionFailure, Error,
+                        "Cannot convert from `{0}` to `{1}` with `as`.",
+                        SemIR::TypeId, SemIR::TypeId);
+      return context.emitter().Build(loc_id,
+                                     target.kind == ConversionTarget::ExplicitAs
+                                         ? ExplicitAsConversionFailure
+                                         : ImplicitAsConversionFailure,
+                                     expr.type_id(), target.type_id);
+    });
   }
 
   // Track that we performed a type conversion, if we did so.

+ 4 - 4
toolchain/check/deduce.cpp

@@ -82,7 +82,7 @@ static auto NoteGenericHere(Context& context, SemIR::GenericId generic_id,
 }
 
 auto DeduceGenericCallArguments(
-    Context& context, Parse::NodeId node_id, SemIR::GenericId generic_id,
+    Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id,
     SemIR::SpecificId enclosing_specific_id,
     [[maybe_unused]] SemIR::InstBlockId implicit_params_id,
     SemIR::InstBlockId params_id, [[maybe_unused]] SemIR::InstId self_id,
@@ -170,8 +170,8 @@ auto DeduceGenericCallArguments(
                                 "Inconsistent deductions for value of generic "
                                 "parameter `{0}`.",
                                 SemIR::NameId);
-              auto diag = context.emitter().Build(
-                  node_id, DeductionInconsistent, entity_name.name_id);
+              auto diag = context.emitter().Build(loc_id, DeductionInconsistent,
+                                                  entity_name.name_id);
               NoteGenericHere(context, generic_id, diag);
               diag.Emit();
               return SemIR::SpecificId::Invalid;
@@ -203,7 +203,7 @@ auto DeduceGenericCallArguments(
                         "Cannot deduce value for generic parameter `{0}`.",
                         SemIR::NameId);
       auto diag = context.emitter().Build(
-          node_id, DeductionIncomplete,
+          loc_id, DeductionIncomplete,
           context.entity_names().Get(entity_name_id).name_id);
       NoteGenericHere(context, generic_id, diag);
       diag.Emit();

+ 1 - 1
toolchain/check/deduce.h

@@ -11,7 +11,7 @@
 namespace Carbon::Check {
 
 // Deduces the generic arguments to use in a call to a generic.
-auto DeduceGenericCallArguments(Context& context, Parse::NodeId node_id,
+auto DeduceGenericCallArguments(Context& context, SemIR::LocId loc_id,
                                 SemIR::GenericId generic_id,
                                 SemIR::SpecificId enclosing_specific_id,
                                 SemIR::InstBlockId implicit_params_id,

+ 6 - 6
toolchain/check/handle_operator.cpp

@@ -94,23 +94,23 @@ auto HandleParseNode(Context& context, Parse::InfixOperatorEqualId node_id)
 
 auto HandleParseNode(Context& context, Parse::InfixOperatorEqualEqualId node_id)
     -> bool {
-  return HandleBinaryOperator(context, node_id, {"Eq", "Equal"});
+  return HandleBinaryOperator(context, node_id, {"Eq", {}, "Equal"});
 }
 
 auto HandleParseNode(Context& context,
                      Parse::InfixOperatorExclaimEqualId node_id) -> bool {
-  return HandleBinaryOperator(context, node_id, {"Eq", "NotEqual"});
+  return HandleBinaryOperator(context, node_id, {"Eq", {}, "NotEqual"});
 }
 
 auto HandleParseNode(Context& context, Parse::InfixOperatorGreaterId node_id)
     -> bool {
-  return HandleBinaryOperator(context, node_id, {"Ordered", "Greater"});
+  return HandleBinaryOperator(context, node_id, {"Ordered", {}, "Greater"});
 }
 
 auto HandleParseNode(Context& context,
                      Parse::InfixOperatorGreaterEqualId node_id) -> bool {
   return HandleBinaryOperator(context, node_id,
-                              {"Ordered", "GreaterOrEquivalent"});
+                              {"Ordered", {}, "GreaterOrEquivalent"});
 }
 
 auto HandleParseNode(Context& context,
@@ -126,13 +126,13 @@ auto HandleParseNode(Context& context,
 
 auto HandleParseNode(Context& context, Parse::InfixOperatorLessId node_id)
     -> bool {
-  return HandleBinaryOperator(context, node_id, {"Ordered", "Less"});
+  return HandleBinaryOperator(context, node_id, {"Ordered", {}, "Less"});
 }
 
 auto HandleParseNode(Context& context, Parse::InfixOperatorLessEqualId node_id)
     -> bool {
   return HandleBinaryOperator(context, node_id,
-                              {"Ordered", "LessOrEquivalent"});
+                              {"Ordered", {}, "LessOrEquivalent"});
 }
 
 auto HandleParseNode(Context& context,

+ 74 - 61
toolchain/check/member_access.cpp

@@ -19,7 +19,7 @@ namespace Carbon::Check {
 
 // Returns the lookup scope corresponding to base_id, or nullopt if not a scope.
 // On invalid scopes, prints a diagnostic and still returns the scope.
-static auto GetAsLookupScope(Context& context, Parse::NodeId node_id,
+static auto GetAsLookupScope(Context& context, SemIR::LocId loc_id,
                              SemIR::ConstantId base_const_id)
     -> std::optional<LookupScope> {
   auto base_id = context.constant_values().GetInstId(base_const_id);
@@ -37,7 +37,7 @@ static auto GetAsLookupScope(Context& context, Parse::NodeId node_id,
                             "Member access into incomplete class `{0}`.",
                             std::string);
           return context.emitter().Build(
-              node_id, QualifiedExprInIncompleteClassScope,
+              loc_id, QualifiedExprInIncompleteClassScope,
               context.sem_ir().StringifyType(base_const_id));
         });
     auto& class_info = context.classes().Get(base_as_class->class_id);
@@ -51,7 +51,7 @@ static auto GetAsLookupScope(Context& context, Parse::NodeId node_id,
                             "Member access into undefined interface `{0}`.",
                             std::string);
           return context.emitter().Build(
-              node_id, QualifiedExprInUndefinedInterfaceScope,
+              loc_id, QualifiedExprInUndefinedInterfaceScope,
               context.sem_ir().StringifyType(base_const_id));
         });
     auto& interface_info =
@@ -155,23 +155,34 @@ static auto LookupInterfaceWitness(Context& context,
 
 // Performs impl lookup for a member name expression. This finds the relevant
 // impl witness and extracts the corresponding impl member.
-static auto PerformImplLookup(Context& context, Parse::NodeId node_id,
-                              SemIR::ConstantId type_const_id,
-                              SemIR::AssociatedEntityType assoc_type,
-                              SemIR::InstId member_id) -> SemIR::InstId {
+static auto PerformImplLookup(
+    Context& context, SemIR::LocId loc_id, SemIR::ConstantId type_const_id,
+    SemIR::AssociatedEntityType assoc_type, SemIR::InstId member_id,
+    std::optional<Context::BuildDiagnosticFn> missing_impl_diagnoser)
+    -> SemIR::InstId {
   auto interface_type =
       context.types().GetAs<SemIR::InterfaceType>(assoc_type.interface_type_id);
   auto& interface = context.interfaces().Get(interface_type.interface_id);
   auto witness_id = LookupInterfaceWitness(context, type_const_id,
                                            assoc_type.interface_type_id);
   if (!witness_id.is_valid()) {
-    CARBON_DIAGNOSTIC(MissingImplInMemberAccess, Error,
-                      "Cannot access member of interface {0} in type {1} "
-                      "that does not implement that interface.",
-                      SemIR::NameId, std::string);
-    context.emitter().Emit(node_id, MissingImplInMemberAccess,
-                           interface.name_id,
-                           context.sem_ir().StringifyType(type_const_id));
+    if (missing_impl_diagnoser) {
+      CARBON_DIAGNOSTIC(MissingImplInMemberAccessNote, Note,
+                        "Type `{1}` does not implement interface `{0}`.",
+                        SemIR::NameId, SemIR::TypeId);
+      (*missing_impl_diagnoser)()
+          .Note(loc_id, MissingImplInMemberAccessNote, interface.name_id,
+                context.GetTypeIdForTypeConstant(type_const_id))
+          .Emit();
+    } else {
+      CARBON_DIAGNOSTIC(MissingImplInMemberAccess, Error,
+                        "Cannot access member of interface `{0}` in type `{1}` "
+                        "that does not implement that interface.",
+                        SemIR::NameId, SemIR::TypeId);
+      context.emitter().Emit(loc_id, MissingImplInMemberAccess,
+                             interface.name_id,
+                             context.GetTypeIdForTypeConstant(type_const_id));
+    }
     return SemIR::InstId::BuiltinError;
   }
 
@@ -197,15 +208,15 @@ static auto PerformImplLookup(Context& context, Parse::NodeId node_id,
       context.sem_ir(), interface_type.specific_id, assoc_type.entity_type_id);
 
   return context.AddInst<SemIR::InterfaceWitnessAccess>(
-      node_id, {.type_id = subst_type_id,
-                .witness_id = witness_id,
-                .index = assoc_entity->index});
+      loc_id, {.type_id = subst_type_id,
+               .witness_id = witness_id,
+               .index = assoc_entity->index});
 }
 
 // Performs a member name lookup into the specified scope, including performing
 // impl lookup if necessary. If the scope is invalid, assume an error has
 // already been diagnosed, and return BuiltinError.
-static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id,
+static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id,
                                     SemIR::InstId /*base_id*/,
                                     SemIR::NameId name_id,
                                     SemIR::ConstantId name_scope_const_id,
@@ -213,7 +224,7 @@ static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id,
   LookupResult result = {.specific_id = SemIR::SpecificId::Invalid,
                          .inst_id = SemIR::InstId::BuiltinError};
   if (lookup_scope.name_scope_id.is_valid()) {
-    result = context.LookupQualifiedName(node_id, name_id, lookup_scope);
+    result = context.LookupQualifiedName(loc_id, name_id, lookup_scope);
   }
 
   // TODO: This duplicates the work that HandleNameAsExpr does. Factor this out.
@@ -227,15 +238,15 @@ static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id,
   if (result.specific_id.is_valid() &&
       context.constant_values().Get(result.inst_id).is_symbolic()) {
     result.inst_id = context.AddInst<SemIR::SpecificConstant>(
-        node_id, {.type_id = type_id,
-                  .inst_id = result.inst_id,
-                  .specific_id = result.specific_id});
+        loc_id, {.type_id = type_id,
+                 .inst_id = result.inst_id,
+                 .specific_id = result.specific_id});
   }
 
   // TODO: Use a different kind of instruction that also references the
   // `base_id` so that `SemIR` consumers can find it.
   auto member_id = context.AddInst<SemIR::NameRef>(
-      node_id,
+      loc_id,
       {.type_id = type_id, .name_id = name_id, .value_id = result.inst_id});
 
   // If member name lookup finds an associated entity name, and the scope is not
@@ -247,8 +258,8 @@ static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id,
   if (auto assoc_type =
           context.types().TryGetAs<SemIR::AssociatedEntityType>(type_id)) {
     if (ScopeNeedsImplLookup(context, lookup_scope)) {
-      member_id = PerformImplLookup(context, node_id, name_scope_const_id,
-                                    *assoc_type, member_id);
+      member_id = PerformImplLookup(context, loc_id, name_scope_const_id,
+                                    *assoc_type, member_id, std::nullopt);
     }
   }
 
@@ -258,14 +269,14 @@ static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id,
 // Performs the instance binding step in member access. If the found member is a
 // field, forms a class member access. If the found member is an instance
 // method, forms a bound method. Otherwise, the member is returned unchanged.
-static auto PerformInstanceBinding(Context& context, Parse::NodeId node_id,
+static auto PerformInstanceBinding(Context& context, SemIR::LocId loc_id,
                                    SemIR::InstId base_id,
                                    SemIR::InstId member_id) -> SemIR::InstId {
   auto member_type_id = context.insts().Get(member_id).type_id();
   CARBON_KIND_SWITCH(context.types().GetAsInst(member_type_id)) {
     case CARBON_KIND(SemIR::UnboundElementType unbound_element_type): {
       // Convert the base to the type of the element if necessary.
-      base_id = ConvertToValueOrRefOfType(context, node_id, base_id,
+      base_id = ConvertToValueOrRefOfType(context, loc_id, base_id,
                                           unbound_element_type.class_type_id);
 
       // Find the specified element, which could be either a field or a base
@@ -276,9 +287,9 @@ static auto PerformInstanceBinding(Context& context, Parse::NodeId node_id,
           << " of unbound element type";
       auto index = GetClassElementIndex(context, element_id);
       auto access_id = context.AddInst<SemIR::ClassElementAccess>(
-          node_id, {.type_id = unbound_element_type.element_type_id,
-                    .base_id = base_id,
-                    .index = index});
+          loc_id, {.type_id = unbound_element_type.element_type_id,
+                   .base_id = base_id,
+                   .index = index});
       if (SemIR::GetExprCategory(context.sem_ir(), base_id) ==
               SemIR::ExprCategory::Value &&
           SemIR::GetExprCategory(context.sem_ir(), access_id) !=
@@ -295,10 +306,10 @@ static auto PerformInstanceBinding(Context& context, Parse::NodeId node_id,
     case CARBON_KIND(SemIR::FunctionType fn_type): {
       if (IsInstanceMethod(context.sem_ir(), fn_type.function_id)) {
         return context.AddInst<SemIR::BoundMethod>(
-            node_id, {.type_id = context.GetBuiltinType(
-                          SemIR::BuiltinInstKind::BoundMethodType),
-                      .object_id = base_id,
-                      .function_id = member_id});
+            loc_id, {.type_id = context.GetBuiltinType(
+                         SemIR::BuiltinInstKind::BoundMethodType),
+                     .object_id = base_id,
+                     .function_id = member_id});
       }
       [[fallthrough]];
     }
@@ -310,7 +321,7 @@ static auto PerformInstanceBinding(Context& context, Parse::NodeId node_id,
 
 // Validates that the index (required to be an IntLiteral) is valid within the
 // tuple size. Returns the index on success, or nullptr on failure.
-static auto ValidateTupleIndex(Context& context, Parse::NodeId node_id,
+static auto ValidateTupleIndex(Context& context, SemIR::LocId loc_id,
                                SemIR::Inst operand_inst,
                                SemIR::IntLiteral index_inst, int size)
     -> const llvm::APInt* {
@@ -320,7 +331,7 @@ static auto ValidateTupleIndex(Context& context, Parse::NodeId node_id,
         TupleIndexOutOfBounds, Error,
         "Tuple element index `{0}` is past the end of type `{1}`.", TypedInt,
         SemIR::TypeId);
-    context.emitter().Emit(node_id, TupleIndexOutOfBounds,
+    context.emitter().Emit(loc_id, TupleIndexOutOfBounds,
                            {.type = index_inst.type_id, .value = index_val},
                            operand_inst.type_id());
     return nullptr;
@@ -328,15 +339,15 @@ static auto ValidateTupleIndex(Context& context, Parse::NodeId node_id,
   return &index_val;
 }
 
-auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
+auto PerformMemberAccess(Context& context, SemIR::LocId loc_id,
                          SemIR::InstId base_id, SemIR::NameId name_id)
     -> SemIR::InstId {
   // If the base is a name scope, such as a class or namespace, perform lookup
   // into that scope.
   if (auto base_const_id = context.constant_values().Get(base_id);
       base_const_id.is_constant()) {
-    if (auto lookup_scope = GetAsLookupScope(context, node_id, base_const_id)) {
-      return LookupMemberNameInScope(context, node_id, base_id, name_id,
+    if (auto lookup_scope = GetAsLookupScope(context, loc_id, base_const_id)) {
+      return LookupMemberNameInScope(context, loc_id, base_id, name_id,
                                      base_const_id, *lookup_scope);
     }
   }
@@ -359,7 +370,7 @@ auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
   auto base_type_const_id = context.types().GetConstantId(base_type_id);
 
   // Find the scope corresponding to the base type.
-  auto lookup_scope = GetAsLookupScope(context, node_id, base_type_const_id);
+  auto lookup_scope = GetAsLookupScope(context, loc_id, base_type_const_id);
   if (!lookup_scope) {
     // The base type is not a name scope. Try some fallback options.
     if (auto struct_type = context.insts().TryGetAs<SemIR::StructType>(
@@ -372,15 +383,15 @@ auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
           // TODO: Model this as producing a lookup result, and do instance
           // binding separately. Perhaps a struct type should be a name scope.
           return context.AddInst<SemIR::StructAccess>(
-              node_id, {.type_id = field.field_type_id,
-                        .struct_id = base_id,
-                        .index = SemIR::ElementIndex(i)});
+              loc_id, {.type_id = field.field_type_id,
+                       .struct_id = base_id,
+                       .index = SemIR::ElementIndex(i)});
         }
       }
       CARBON_DIAGNOSTIC(QualifiedExprNameNotFound, Error,
                         "Type `{0}` does not have a member `{1}`.",
                         SemIR::TypeId, SemIR::NameId);
-      context.emitter().Emit(node_id, QualifiedExprNameNotFound, base_type_id,
+      context.emitter().Emit(loc_id, QualifiedExprNameNotFound, base_type_id,
                              name_id);
       return SemIR::InstId::BuiltinError;
     }
@@ -389,24 +400,25 @@ auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
       CARBON_DIAGNOSTIC(QualifiedExprUnsupported, Error,
                         "Type `{0}` does not support qualified expressions.",
                         SemIR::TypeId);
-      context.emitter().Emit(node_id, QualifiedExprUnsupported, base_type_id);
+      context.emitter().Emit(loc_id, QualifiedExprUnsupported, base_type_id);
     }
     return SemIR::InstId::BuiltinError;
   }
 
   // Perform lookup into the base type.
-  auto member_id = LookupMemberNameInScope(context, node_id, base_id, name_id,
+  auto member_id = LookupMemberNameInScope(context, loc_id, base_id, name_id,
                                            base_type_const_id, *lookup_scope);
 
   // Perform instance binding if we found an instance member.
-  member_id = PerformInstanceBinding(context, node_id, base_id, member_id);
+  member_id = PerformInstanceBinding(context, loc_id, base_id, member_id);
 
   return member_id;
 }
 
-auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
-                                 SemIR::InstId base_id,
-                                 SemIR::InstId member_expr_id)
+auto PerformCompoundMemberAccess(
+    Context& context, SemIR::LocId loc_id, SemIR::InstId base_id,
+    SemIR::InstId member_expr_id,
+    std::optional<Context::BuildDiagnosticFn> missing_impl_diagnoser)
     -> SemIR::InstId {
   // Materialize a temporary for the base expression if necessary.
   base_id = ConvertToValueOrRefExpr(context, base_id);
@@ -420,31 +432,32 @@ auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
   // performed using the type of the base expression.
   if (auto assoc_type = context.types().TryGetAs<SemIR::AssociatedEntityType>(
           member.type_id())) {
-    member_id = PerformImplLookup(context, node_id, base_type_const_id,
-                                  *assoc_type, member_id);
+    member_id =
+        PerformImplLookup(context, loc_id, base_type_const_id, *assoc_type,
+                          member_id, missing_impl_diagnoser);
   } else if (context.insts().Is<SemIR::TupleType>(
                  context.constant_values().GetInstId(base_type_const_id))) {
-    return PerformTupleIndex(context, node_id, base_id, member_expr_id);
+    return PerformTupleIndex(context, loc_id, base_id, member_expr_id);
   }
 
   // Perform instance binding if we found an instance member.
-  member_id = PerformInstanceBinding(context, node_id, base_id, member_id);
+  member_id = PerformInstanceBinding(context, loc_id, base_id, member_id);
 
   // If we didn't perform impl lookup or instance binding, that's an error
   // because the base expression is not used for anything.
-  if (member_id == member_expr_id) {
+  if (member_id == member_expr_id && member.type_id() != SemIR::TypeId::Error) {
     CARBON_DIAGNOSTIC(CompoundMemberAccessDoesNotUseBase, Error,
                       "Member name of type `{0}` in compound member access is "
                       "not an instance member or an interface member.",
                       SemIR::TypeId);
-    context.emitter().Emit(node_id, CompoundMemberAccessDoesNotUseBase,
+    context.emitter().Emit(loc_id, CompoundMemberAccessDoesNotUseBase,
                            member.type_id());
   }
 
   return member_id;
 }
 
-auto PerformTupleIndex(Context& context, Parse::NodeId node_id,
+auto PerformTupleIndex(Context& context, SemIR::LocId loc_id,
                        SemIR::InstId tuple_inst_id, SemIR::InstId index_inst_id)
     -> SemIR::InstId {
   tuple_inst_id = ConvertToValueOrRefExpr(context, tuple_inst_id);
@@ -457,7 +470,7 @@ auto PerformTupleIndex(Context& context, Parse::NodeId node_id,
                       "Type `{0}` does not support tuple indexing. Only "
                       "tuples can be indexed that way.",
                       SemIR::TypeId);
-    context.emitter().Emit(node_id, TupleIndexOnANonTupleType, tuple_type_id);
+    context.emitter().Emit(loc_id, TupleIndexOnANonTupleType, tuple_type_id);
     return SemIR::InstId::BuiltinError;
   }
 
@@ -473,21 +486,21 @@ auto PerformTupleIndex(Context& context, Parse::NodeId node_id,
     // TODO: Decide what to do if the index is a symbolic constant.
     CARBON_DIAGNOSTIC(TupleIndexNotConstant, Error,
                       "Tuple index must be a constant.");
-    context.emitter().Emit(node_id, TupleIndexNotConstant);
+    context.emitter().Emit(loc_id, TupleIndexNotConstant);
     index_inst_id = SemIR::InstId::BuiltinError;
   } else {
     auto index_literal = context.insts().GetAs<SemIR::IntLiteral>(
         context.constant_values().GetInstId(index_const_id));
     auto type_block = context.type_blocks().Get(tuple_type->elements_id);
     if (const auto* index_val = ValidateTupleIndex(
-            context, node_id, tuple_inst, index_literal, type_block.size())) {
+            context, loc_id, tuple_inst, index_literal, type_block.size())) {
       element_type_id = type_block[index_val->getZExtValue()];
     } else {
       index_inst_id = SemIR::InstId::BuiltinError;
     }
   }
 
-  return context.AddInst<SemIR::TupleIndex>(node_id,
+  return context.AddInst<SemIR::TupleIndex>(loc_id,
                                             {.type_id = element_type_id,
                                              .tuple_id = tuple_inst_id,
                                              .index_id = index_inst_id});

+ 9 - 6
toolchain/check/member_access.h

@@ -12,20 +12,23 @@ namespace Carbon::Check {
 
 // Creates SemIR to perform a member access with base expression `base_id` and
 // member name `name_id`. Returns the result of the access.
-auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
+auto PerformMemberAccess(Context& context, SemIR::LocId loc_id,
                          SemIR::InstId base_id, SemIR::NameId name_id)
     -> SemIR::InstId;
 
 // Creates SemIR to perform a compound member access with base expression
 // `base_id` and member name expression `member_expr_id`. Returns the result of
-// the access.
-auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
-                                 SemIR::InstId base_id,
-                                 SemIR::InstId member_expr_id) -> SemIR::InstId;
+// the access. If specified, `missing_impl_diagnoser()` is used to build an
+// error diagnostic when impl binding fails due to a missing `impl`.
+auto PerformCompoundMemberAccess(
+    Context& context, SemIR::LocId loc_id, SemIR::InstId base_id,
+    SemIR::InstId member_expr_id,
+    std::optional<Context::BuildDiagnosticFn> missing_impl_diagnoser =
+        std::nullopt) -> SemIR::InstId;
 
 // Creates SemIR to perform a tuple index with base expression `tuple_inst_id`
 // and index expression `index_inst_id`. Returns the result of the access.
-auto PerformTupleIndex(Context& context, Parse::NodeId node_id,
+auto PerformTupleIndex(Context& context, SemIR::LocId loc_id,
                        SemIR::InstId tuple_inst_id, SemIR::InstId index_inst_id)
     -> SemIR::InstId;
 

+ 30 - 67
toolchain/check/operator.cpp

@@ -13,95 +13,58 @@
 
 namespace Carbon::Check {
 
-// Returns the name scope of the operator interface for the specified operator
-// from the Core package.
-static auto GetOperatorInterface(Context& context, Parse::AnyExprId node_id,
-                                 Operator op) -> SemIR::NameScopeId {
-  auto interface_id = context.LookupNameInCore(node_id, op.interface_name);
-  if (interface_id == SemIR::InstId::BuiltinError) {
-    return SemIR::NameScopeId::Invalid;
-  }
-
-  // We expect it to be an interface.
-  if (auto interface_inst =
-          context.insts().TryGetAs<SemIR::InterfaceType>(interface_id)) {
-    return context.interfaces().Get(interface_inst->interface_id).scope_id;
-  }
-  return SemIR::NameScopeId::Invalid;
-}
-
 // Returns the `Op` function for the specified operator.
-static auto GetOperatorOpFunction(Context& context, Parse::AnyExprId node_id,
+static auto GetOperatorOpFunction(Context& context, SemIR::LocId loc_id,
                                   Operator op) -> SemIR::InstId {
-  auto interface_scope_id = GetOperatorInterface(context, node_id, op);
-  if (!interface_scope_id.is_valid()) {
-    return SemIR::InstId::Invalid;
-  }
-
-  // TODO: For a parameterized interface, find the corresponding specific.
-  LookupScope scope = {.name_scope_id = interface_scope_id,
-                       .specific_id = SemIR::SpecificId::Invalid};
-
-  // Lookup `Interface.Op`.
-  auto op_ident_id = context.identifiers().Add(op.op_name);
-  auto op_result = context.LookupQualifiedName(
-      node_id, SemIR::NameId::ForIdentifier(op_ident_id), scope,
-      /*required=*/false);
-  if (!op_result.inst_id.is_valid()) {
-    return SemIR::InstId::Invalid;
+  // Look up the interface, and pass it any generic arguments.
+  auto interface_id = context.LookupNameInCore(loc_id, op.interface_name);
+  if (!op.interface_args_ref.empty()) {
+    interface_id =
+        PerformCall(context, loc_id, interface_id, op.interface_args_ref);
   }
 
-  // Look through import_refs and aliases.
-  auto op_const_id = GetConstantValueInSpecific(
-      context.sem_ir(), op_result.specific_id, op_result.inst_id);
-  auto op_id = context.constant_values().GetInstId(op_const_id);
-
-  // We expect it to be an associated function.
-  if (context.insts().Is<SemIR::AssociatedEntity>(op_id)) {
-    return op_id;
-  }
-  return SemIR::InstId::Invalid;
+  // Look up the interface member.
+  auto op_name_id =
+      SemIR::NameId::ForIdentifier(context.identifiers().Add(op.op_name));
+  return PerformMemberAccess(context, loc_id, interface_id, op_name_id);
 }
 
-auto BuildUnaryOperator(Context& context, Parse::AnyExprId node_id, Operator op,
-                        SemIR::InstId operand_id) -> SemIR::InstId {
-  auto op_fn = GetOperatorOpFunction(context, node_id, op);
-  if (!op_fn.is_valid()) {
-    context.TODO(node_id,
-                 "missing or invalid operator interface, also avoid duplicate "
-                 "diagnostic if prelude is unavailable");
-    return SemIR::InstId::BuiltinError;
-  }
+auto BuildUnaryOperator(
+    Context& context, SemIR::LocId loc_id, Operator op,
+    SemIR::InstId operand_id,
+    std::optional<Context::BuildDiagnosticFn> missing_impl_diagnoser)
+    -> SemIR::InstId {
+  // Look up the operator function.
+  auto op_fn = GetOperatorOpFunction(context, loc_id, op);
 
   // Form `operand.(Op)`.
-  auto bound_op_id =
-      PerformCompoundMemberAccess(context, node_id, operand_id, op_fn);
+  auto bound_op_id = PerformCompoundMemberAccess(context, loc_id, operand_id,
+                                                 op_fn, missing_impl_diagnoser);
   if (bound_op_id == SemIR::InstId::BuiltinError) {
     return SemIR::InstId::BuiltinError;
   }
 
   // Form `bound_op()`.
-  return PerformCall(context, node_id, bound_op_id, {});
+  return PerformCall(context, loc_id, bound_op_id, {});
 }
 
-auto BuildBinaryOperator(Context& context, Parse::AnyExprId node_id,
-                         Operator op, SemIR::InstId lhs_id,
-                         SemIR::InstId rhs_id) -> SemIR::InstId {
-  auto op_fn = GetOperatorOpFunction(context, node_id, op);
-  if (!op_fn.is_valid()) {
-    context.TODO(node_id, "missing or invalid operator interface");
-    return SemIR::InstId::BuiltinError;
-  }
+auto BuildBinaryOperator(
+    Context& context, SemIR::LocId loc_id, Operator op, SemIR::InstId lhs_id,
+    SemIR::InstId rhs_id,
+    std::optional<Context::BuildDiagnosticFn> missing_impl_diagnoser)
+    -> SemIR::InstId {
+  // Look up the operator function.
+  auto op_fn = GetOperatorOpFunction(context, loc_id, op);
 
   // Form `lhs.(Op)`.
-  auto bound_op_id =
-      PerformCompoundMemberAccess(context, node_id, lhs_id, op_fn);
+  auto bound_op_id = PerformCompoundMemberAccess(context, loc_id, lhs_id, op_fn,
+                                                 missing_impl_diagnoser);
   if (bound_op_id == SemIR::InstId::BuiltinError) {
     return SemIR::InstId::BuiltinError;
   }
 
   // Form `bound_op(rhs)`.
-  return PerformCall(context, node_id, bound_op_id, {rhs_id});
+  return PerformCall(context, loc_id, bound_op_id, {rhs_id});
 }
 
 }  // namespace Carbon::Check

+ 17 - 7
toolchain/check/operator.h

@@ -13,19 +13,29 @@ namespace Carbon::Check {
 
 struct Operator {
   llvm::StringLiteral interface_name;
+  llvm::ArrayRef<SemIR::InstId> interface_args_ref = {};
   llvm::StringLiteral op_name = "Op";
 };
 
 // Checks and builds SemIR for a unary operator expression. For example,
-// `$operand` or `operand$`.
-auto BuildUnaryOperator(Context& context, Parse::AnyExprId node_id, Operator op,
-                        SemIR::InstId operand_id) -> SemIR::InstId;
+// `*operand` or `operand*`. If specified, `missing_impl_diagnoser` is used to
+// build a custom error diagnostic for the case where impl lookup for the
+// operator fails.
+auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
+                        SemIR::InstId operand_id,
+                        std::optional<Context::BuildDiagnosticFn>
+                            missing_impl_diagnoser = std::nullopt)
+    -> SemIR::InstId;
 
 // Checks and builds SemIR for a binary operator expression. For example,
-// `lhs_id $ rhs_id`.
-auto BuildBinaryOperator(Context& context, Parse::AnyExprId node_id,
-                         Operator op, SemIR::InstId lhs_id,
-                         SemIR::InstId rhs_id) -> SemIR::InstId;
+// `lhs_id * rhs_id`. If specified, `missing_impl_diagnoser` is used to build a
+// custom error diagnostic for the case where impl lookup for the operator
+// fails.
+auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
+                         SemIR::InstId lhs_id, SemIR::InstId rhs_id,
+                         std::optional<Context::BuildDiagnosticFn>
+                             missing_impl_diagnoser = std::nullopt)
+    -> SemIR::InstId;
 
 }  // namespace Carbon::Check
 

+ 1 - 0
toolchain/check/testdata/alias/fail_bool_value.carbon

@@ -30,6 +30,7 @@ let a_test: bool = a;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/alias/fail_builtins.carbon

@@ -37,6 +37,7 @@ alias b = bool;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/alias/fail_control_flow.carbon

@@ -34,6 +34,7 @@ alias a = true or false;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 1
toolchain/check/testdata/alias/no_prelude/fail_aliased_name_in_diag.carbon

@@ -14,7 +14,7 @@ class D {}
 alias c = C;
 var d: D = {};
 
-// CHECK:STDERR: fail_aliased_name_in_diag.carbon:[[@LINE+3]]:1: ERROR: Cannot implicitly convert from `D` to `C`.
+// CHECK:STDERR: fail_aliased_name_in_diag.carbon:[[@LINE+3]]:1: ERROR: Package `Core` implicitly referenced here, but not found.
 // CHECK:STDERR: let c_var: c = d;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~
 let c_var: c = d;
@@ -65,6 +65,7 @@ let c_var: c = d;
 // CHECK:STDOUT:   %.loc15_14: init %D = converted %.loc15_13.1, %.loc15_13.2 [template = constants.%struct]
 // CHECK:STDOUT:   assign file.%d.var, %.loc15_14
 // CHECK:STDOUT:   %d.ref: ref %D = name_ref d, file.%d
+// CHECK:STDOUT:   %.loc20: %C = converted %d.ref, <error> [template = <error>]
 // CHECK:STDOUT:   %c_var: %C = bind_name c_var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 0
toolchain/check/testdata/array/array_in_place.carbon

@@ -42,6 +42,7 @@ fn G() {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/array_vs_tuple.carbon

@@ -42,6 +42,7 @@ fn G() {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/assign_return_value.carbon

@@ -40,6 +40,7 @@ fn Run() {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/assign_var.carbon

@@ -36,6 +36,7 @@ var b: [i32; 3] = a;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/base.carbon

@@ -52,6 +52,7 @@ var c: [(); 5] = ((), (), (), (), (),);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/canonicalize_index.carbon

@@ -38,6 +38,7 @@ let b: [i32; 3]* = &a;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/fail_bound_negative.carbon

@@ -34,6 +34,7 @@ var a: [i32; Negate(1)];
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 93 - 5
toolchain/check/testdata/array/fail_bound_overflow.carbon

@@ -17,7 +17,10 @@
 // CHECK:STDERR:
 var a: [i32; 39999999999999999993];
 
-// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+7]]:9: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+10]]:9: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: var b: [1; 39999999999999999993];
+// CHECK:STDERR:         ^
+// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+7]]:9: Type `i32` does not implement interface `ImplicitAs`.
 // CHECK:STDERR: var b: [1; 39999999999999999993];
 // CHECK:STDERR:         ^
 // CHECK:STDERR:
@@ -33,20 +36,44 @@ var b: [1; 39999999999999999993];
 // CHECK:STDOUT:   %.1: type = tuple_type () [template]
 // CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
 // CHECK:STDOUT:   %.2: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %ImplicitAs.type: type = generic_interface_type @ImplicitAs [template]
+// CHECK:STDOUT:   %ImplicitAs: %ImplicitAs.type = struct_value () [template]
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic]
+// CHECK:STDOUT:   %.3: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Self.1: @ImplicitAs.%.1 (%.3) = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Self.2: %.3 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.4: type = assoc_entity_type %.3, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.5: %.4 = assoc_entity element0, imports.%import_ref.6 [symbolic]
+// CHECK:STDOUT:   %.6: type = interface_type @ImplicitAs, @ImplicitAs(type) [template]
+// CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @ImplicitAs(type) [template]
+// CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type %.6, %Convert.type.2 [template]
+// CHECK:STDOUT:   %.8: %.7 = assoc_entity element0, imports.%import_ref.6 [template]
+// CHECK:STDOUT:   %.9: %.4 = assoc_entity element0, imports.%import_ref.7 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     .Int32 = %import_ref
+// CHECK:STDOUT:     .Int32 = %import_ref.1
+// CHECK:STDOUT:     .ImplicitAs = %import_ref.2
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.2: %ImplicitAs.type = import_ref Core//prelude/operators/as, inst+37, loaded [template = constants.%ImplicitAs]
+// CHECK:STDOUT:   %import_ref.3 = import_ref Core//prelude/operators/as, inst+42, unloaded
+// CHECK:STDOUT:   %import_ref.4: @ImplicitAs.%.2 (%.4) = import_ref Core//prelude/operators/as, inst+59, loaded [symbolic = @ImplicitAs.%.3 (constants.%.9)]
+// CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.7 = import_ref Core//prelude/operators/as, inst+52, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -62,11 +89,72 @@ var b: [1; 39999999999999999993];
 // CHECK:STDOUT:   %.loc18_34: type = array_type <error>, i32 [template = <error>]
 // CHECK:STDOUT:   %a.var: ref <error> = var a
 // CHECK:STDOUT:   %a: ref <error> = bind_name a, %a.var
-// CHECK:STDOUT:   %.loc27_9: i32 = int_literal 1 [template = constants.%.2]
-// CHECK:STDOUT:   %.loc27_32: type = array_type <error>, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc30_9.1: i32 = int_literal 1 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc30_9.2: init type = call constants.%ImplicitAs(type) [template = constants.%.6]
+// CHECK:STDOUT:   %.loc30_9.3: %.7 = specific_constant imports.%import_ref.4, @ImplicitAs(type) [template = constants.%.8]
+// CHECK:STDOUT:   %Convert.ref: %.7 = name_ref Convert, %.loc30_9.3 [template = constants.%.8]
+// CHECK:STDOUT:   %.loc30_9.4: type = converted %.loc30_9.1, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc30_32: type = array_type <error>, <error> [template = <error>]
 // CHECK:STDOUT:   %b.var: ref <error> = var b
 // CHECK:STDOUT:   %b: ref <error> = bind_name b, %b.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @ImplicitAs(constants.%Dest: type) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.3)]
+// CHECK:STDOUT:   %Self: %.3 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
+// CHECK:STDOUT:   %Convert: @ImplicitAs.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @ImplicitAs.%.1 (%.3), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.2 (constants.%.4)]
+// CHECK:STDOUT:   %.3: @ImplicitAs.%.2 (%.4) = assoc_entity element0, imports.%import_ref.6 [symbolic = %.3 (constants.%.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.3
+// CHECK:STDOUT:     .Convert = imports.%import_ref.4
+// CHECK:STDOUT:     witness = (imports.%import_ref.5)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Convert(constants.%Dest: type, constants.%Self.1: @ImplicitAs.%.1 (%.3)) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.3)]
+// CHECK:STDOUT:   %Self: %.3 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self: @Convert.%Self (%Self.2)]() -> @Convert.%Dest (%Dest);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@ImplicitAs.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@Convert.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert(constants.%Dest, constants.%Self.1) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT:   %.1 => constants.%.3
+// CHECK:STDOUT:   %Self => constants.%Self.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(type) {
+// CHECK:STDOUT:   %Dest => type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.6
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
+// CHECK:STDOUT:   %Convert => constants.%Convert.2
+// CHECK:STDOUT:   %.2 => constants.%.7
+// CHECK:STDOUT:   %.3 => constants.%.8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/array/fail_incomplete_element.carbon

@@ -36,6 +36,7 @@ var p: Incomplete* = &a[0];
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 93 - 4
toolchain/check/testdata/array/fail_invalid_type.carbon

@@ -8,7 +8,10 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/fail_invalid_type.carbon
 
-// CHECK:STDERR: fail_invalid_type.carbon:[[@LINE+3]]:9: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: fail_invalid_type.carbon:[[@LINE+6]]:9: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: var a: [1; 1];
+// CHECK:STDERR:         ^
+// CHECK:STDERR: fail_invalid_type.carbon:[[@LINE+3]]:9: Type `i32` does not implement interface `ImplicitAs`.
 // CHECK:STDERR: var a: [1; 1];
 // CHECK:STDERR:         ^
 var a: [1; 1];
@@ -17,18 +20,43 @@ var a: [1; 1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %.1: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %ImplicitAs.type: type = generic_interface_type @ImplicitAs [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %ImplicitAs: %ImplicitAs.type = struct_value () [template]
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic]
+// CHECK:STDOUT:   %.3: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Self.1: @ImplicitAs.%.1 (%.3) = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Self.2: %.3 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.4: type = assoc_entity_type %.3, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.5: %.4 = assoc_entity element0, imports.%import_ref.5 [symbolic]
+// CHECK:STDOUT:   %.6: type = interface_type @ImplicitAs, @ImplicitAs(type) [template]
+// CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @ImplicitAs(type) [template]
+// CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type %.6, %Convert.type.2 [template]
+// CHECK:STDOUT:   %.8: %.7 = assoc_entity element0, imports.%import_ref.5 [template]
+// CHECK:STDOUT:   %.9: %.4 = assoc_entity element0, imports.%import_ref.6 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .ImplicitAs = %import_ref.1
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: %ImplicitAs.type = import_ref Core//prelude/operators/as, inst+37, loaded [template = constants.%ImplicitAs]
+// CHECK:STDOUT:   %import_ref.2 = import_ref Core//prelude/operators/as, inst+42, unloaded
+// CHECK:STDOUT:   %import_ref.3: @ImplicitAs.%.2 (%.4) = import_ref Core//prelude/operators/as, inst+59, loaded [symbolic = @ImplicitAs.%.3 (constants.%.9)]
+// CHECK:STDOUT:   %import_ref.4 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+52, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -37,10 +65,71 @@ var a: [1; 1];
 // CHECK:STDOUT:     .a = %a
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %.loc14_9: i32 = int_literal 1 [template = constants.%.1]
-// CHECK:STDOUT:   %.loc14_12: i32 = int_literal 1 [template = constants.%.1]
-// CHECK:STDOUT:   %.loc14_13: type = array_type %.loc14_12, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc17_9.1: i32 = int_literal 1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc17_12: i32 = int_literal 1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc17_9.2: init type = call constants.%ImplicitAs(type) [template = constants.%.6]
+// CHECK:STDOUT:   %.loc17_9.3: %.7 = specific_constant imports.%import_ref.3, @ImplicitAs(type) [template = constants.%.8]
+// CHECK:STDOUT:   %Convert.ref: %.7 = name_ref Convert, %.loc17_9.3 [template = constants.%.8]
+// CHECK:STDOUT:   %.loc17_9.4: type = converted %.loc17_9.1, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc17_13: type = array_type %.loc17_12, <error> [template = <error>]
 // CHECK:STDOUT:   %a.var: ref <error> = var a
 // CHECK:STDOUT:   %a: ref <error> = bind_name a, %a.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @ImplicitAs(constants.%Dest: type) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.3)]
+// CHECK:STDOUT:   %Self: %.3 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
+// CHECK:STDOUT:   %Convert: @ImplicitAs.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @ImplicitAs.%.1 (%.3), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.2 (constants.%.4)]
+// CHECK:STDOUT:   %.3: @ImplicitAs.%.2 (%.4) = assoc_entity element0, imports.%import_ref.5 [symbolic = %.3 (constants.%.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.2
+// CHECK:STDOUT:     .Convert = imports.%import_ref.3
+// CHECK:STDOUT:     witness = (imports.%import_ref.4)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Convert(constants.%Dest: type, constants.%Self.1: @ImplicitAs.%.1 (%.3)) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.3)]
+// CHECK:STDOUT:   %Self: %.3 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self: @Convert.%Self (%Self.2)]() -> @Convert.%Dest (%Dest);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@ImplicitAs.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@Convert.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert(constants.%Dest, constants.%Self.1) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT:   %.1 => constants.%.3
+// CHECK:STDOUT:   %Self => constants.%Self.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(type) {
+// CHECK:STDOUT:   %Dest => type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.6
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
+// CHECK:STDOUT:   %Convert => constants.%Convert.2
+// CHECK:STDOUT:   %.2 => constants.%.7
+// CHECK:STDOUT:   %.3 => constants.%.8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/array/fail_out_of_bound.carbon

@@ -34,6 +34,7 @@ var a: [i32; 1] = (1, 2, 3);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/fail_out_of_bound_non_literal.carbon

@@ -39,6 +39,7 @@ var b: i32 = a[{.index = 3}.index];
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 152 - 57
toolchain/check/testdata/array/fail_type_mismatch.carbon

@@ -8,14 +8,20 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/fail_type_mismatch.carbon
 
-// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+4]]:19: ERROR: Cannot implicitly convert from `String` to `i32`.
+// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+7]]:19: ERROR: Cannot implicitly convert from `String` to `i32`.
+// CHECK:STDERR: var a: [i32; 3] = (1, "Hello", "World");
+// CHECK:STDERR:                   ^~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+4]]:19: Type `String` does not implement interface `ImplicitAs`.
 // CHECK:STDERR: var a: [i32; 3] = (1, "Hello", "World");
 // CHECK:STDERR:                   ^~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 var a: [i32; 3] = (1, "Hello", "World");
 
 var t1: (i32, String, String);
-// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+4]]:19: ERROR: Cannot implicitly convert from `String` to `i32`.
+// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+7]]:19: ERROR: Cannot implicitly convert from `String` to `i32`.
+// CHECK:STDERR: var b: [i32; 3] = t1;
+// CHECK:STDERR:                   ^~
+// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+4]]:19: Type `String` does not implement interface `ImplicitAs`.
 // CHECK:STDERR: var b: [i32; 3] = t1;
 // CHECK:STDERR:                   ^~
 // CHECK:STDERR:
@@ -48,27 +54,51 @@ var d: [i32; 3] = t2;
 // CHECK:STDOUT:   %.8: String = string_literal "World" [template]
 // CHECK:STDOUT:   %.9: type = tuple_type (i32, String, String) [template]
 // CHECK:STDOUT:   %.10: i32 = int_literal 0 [template]
-// CHECK:STDOUT:   %.11: type = tuple_type (type, type, type) [template]
-// CHECK:STDOUT:   %.12: type = tuple_type (i32, %.6, %.6) [template]
-// CHECK:STDOUT:   %.13: type = ptr_type %.12 [template]
-// CHECK:STDOUT:   %.14: i32 = int_literal 2 [template]
-// CHECK:STDOUT:   %.15: type = tuple_type (i32, i32) [template]
-// CHECK:STDOUT:   %.16: type = tuple_type (type, type) [template]
-// CHECK:STDOUT:   %.17: type = ptr_type %.15 [template]
+// CHECK:STDOUT:   %ImplicitAs.type: type = generic_interface_type @ImplicitAs [template]
+// CHECK:STDOUT:   %ImplicitAs: %ImplicitAs.type = struct_value () [template]
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic]
+// CHECK:STDOUT:   %.11: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Self.1: @ImplicitAs.%.1 (%.11) = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Self.2: %.11 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.12: type = assoc_entity_type %.11, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.13: %.12 = assoc_entity element0, imports.%import_ref.6 [symbolic]
+// CHECK:STDOUT:   %.14: type = interface_type @ImplicitAs, @ImplicitAs(i32) [template]
+// CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @ImplicitAs(i32) [template]
+// CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.15: type = assoc_entity_type %.14, %Convert.type.2 [template]
+// CHECK:STDOUT:   %.16: %.15 = assoc_entity element0, imports.%import_ref.6 [template]
+// CHECK:STDOUT:   %.17: %.12 = assoc_entity element0, imports.%import_ref.7 [symbolic]
+// CHECK:STDOUT:   %.18: type = tuple_type (type, type, type) [template]
+// CHECK:STDOUT:   %.19: type = tuple_type (i32, %.6, %.6) [template]
+// CHECK:STDOUT:   %.20: type = ptr_type %.19 [template]
+// CHECK:STDOUT:   %.21: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.22: type = tuple_type (i32, i32) [template]
+// CHECK:STDOUT:   %.23: type = tuple_type (type, type) [template]
+// CHECK:STDOUT:   %.24: type = ptr_type %.22 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     .Int32 = %import_ref
+// CHECK:STDOUT:     .Int32 = %import_ref.1
+// CHECK:STDOUT:     .ImplicitAs = %import_ref.2
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.2: %ImplicitAs.type = import_ref Core//prelude/operators/as, inst+37, loaded [template = constants.%ImplicitAs]
+// CHECK:STDOUT:   %import_ref.3 = import_ref Core//prelude/operators/as, inst+42, unloaded
+// CHECK:STDOUT:   %import_ref.4: @ImplicitAs.%.2 (%.12) = import_ref Core//prelude/operators/as, inst+59, loaded [symbolic = @ImplicitAs.%.3 (constants.%.17)]
+// CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.7 = import_ref Core//prelude/operators/as, inst+52, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -82,79 +112,144 @@ var d: [i32; 3] = t2;
 // CHECK:STDOUT:     .d = %d
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %int.make_type_32.loc15: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc15_14: i32 = int_literal 3 [template = constants.%.2]
-// CHECK:STDOUT:   %.loc15_9.1: type = value_of_initializer %int.make_type_32.loc15 [template = i32]
-// CHECK:STDOUT:   %.loc15_9.2: type = converted %int.make_type_32.loc15, %.loc15_9.1 [template = i32]
-// CHECK:STDOUT:   %.loc15_15: type = array_type %.loc15_14, i32 [template = constants.%.3]
+// CHECK:STDOUT:   %int.make_type_32.loc18: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc18_14: i32 = int_literal 3 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc18_9.1: type = value_of_initializer %int.make_type_32.loc18 [template = i32]
+// CHECK:STDOUT:   %.loc18_9.2: type = converted %int.make_type_32.loc18, %.loc18_9.1 [template = i32]
+// CHECK:STDOUT:   %.loc18_15: type = array_type %.loc18_14, i32 [template = constants.%.3]
 // CHECK:STDOUT:   %a.var: ref %.3 = var a
 // CHECK:STDOUT:   %a: ref %.3 = bind_name a, %a.var
-// CHECK:STDOUT:   %int.make_type_32.loc17: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc17_29.1: %.11 = tuple_literal (%int.make_type_32.loc17, String, String)
-// CHECK:STDOUT:   %.loc17_29.2: type = value_of_initializer %int.make_type_32.loc17 [template = i32]
-// CHECK:STDOUT:   %.loc17_29.3: type = converted %int.make_type_32.loc17, %.loc17_29.2 [template = i32]
-// CHECK:STDOUT:   %.loc17_29.4: type = converted %.loc17_29.1, constants.%.9 [template = constants.%.9]
+// CHECK:STDOUT:   %int.make_type_32.loc20: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc20_29.1: %.18 = tuple_literal (%int.make_type_32.loc20, String, String)
+// CHECK:STDOUT:   %.loc20_29.2: type = value_of_initializer %int.make_type_32.loc20 [template = i32]
+// CHECK:STDOUT:   %.loc20_29.3: type = converted %int.make_type_32.loc20, %.loc20_29.2 [template = i32]
+// CHECK:STDOUT:   %.loc20_29.4: type = converted %.loc20_29.1, constants.%.9 [template = constants.%.9]
 // CHECK:STDOUT:   %t1.var: ref %.9 = var t1
 // CHECK:STDOUT:   %t1: ref %.9 = bind_name t1, %t1.var
-// CHECK:STDOUT:   %int.make_type_32.loc22: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc22_14: i32 = int_literal 3 [template = constants.%.2]
-// CHECK:STDOUT:   %.loc22_9.1: type = value_of_initializer %int.make_type_32.loc22 [template = i32]
-// CHECK:STDOUT:   %.loc22_9.2: type = converted %int.make_type_32.loc22, %.loc22_9.1 [template = i32]
-// CHECK:STDOUT:   %.loc22_15: type = array_type %.loc22_14, i32 [template = constants.%.3]
-// CHECK:STDOUT:   %b.var: ref %.3 = var b
-// CHECK:STDOUT:   %b: ref %.3 = bind_name b, %b.var
 // CHECK:STDOUT:   %int.make_type_32.loc28: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc28_14: i32 = int_literal 3 [template = constants.%.2]
 // CHECK:STDOUT:   %.loc28_9.1: type = value_of_initializer %int.make_type_32.loc28 [template = i32]
 // CHECK:STDOUT:   %.loc28_9.2: type = converted %int.make_type_32.loc28, %.loc28_9.1 [template = i32]
 // CHECK:STDOUT:   %.loc28_15: type = array_type %.loc28_14, i32 [template = constants.%.3]
-// CHECK:STDOUT:   %c.var: ref %.3 = var c
-// CHECK:STDOUT:   %c: ref %.3 = bind_name c, %c.var
-// CHECK:STDOUT:   %int.make_type_32.loc30_10: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %int.make_type_32.loc30_15: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc30_18.1: %.16 = tuple_literal (%int.make_type_32.loc30_10, %int.make_type_32.loc30_15)
-// CHECK:STDOUT:   %.loc30_18.2: type = value_of_initializer %int.make_type_32.loc30_10 [template = i32]
-// CHECK:STDOUT:   %.loc30_18.3: type = converted %int.make_type_32.loc30_10, %.loc30_18.2 [template = i32]
-// CHECK:STDOUT:   %.loc30_18.4: type = value_of_initializer %int.make_type_32.loc30_15 [template = i32]
-// CHECK:STDOUT:   %.loc30_18.5: type = converted %int.make_type_32.loc30_15, %.loc30_18.4 [template = i32]
-// CHECK:STDOUT:   %.loc30_18.6: type = converted %.loc30_18.1, constants.%.15 [template = constants.%.15]
-// CHECK:STDOUT:   %t2.var: ref %.15 = var t2
-// CHECK:STDOUT:   %t2: ref %.15 = bind_name t2, %t2.var
+// CHECK:STDOUT:   %b.var: ref %.3 = var b
+// CHECK:STDOUT:   %b: ref %.3 = bind_name b, %b.var
 // CHECK:STDOUT:   %int.make_type_32.loc34: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc34_14: i32 = int_literal 3 [template = constants.%.2]
 // CHECK:STDOUT:   %.loc34_9.1: type = value_of_initializer %int.make_type_32.loc34 [template = i32]
 // CHECK:STDOUT:   %.loc34_9.2: type = converted %int.make_type_32.loc34, %.loc34_9.1 [template = i32]
 // CHECK:STDOUT:   %.loc34_15: type = array_type %.loc34_14, i32 [template = constants.%.3]
+// CHECK:STDOUT:   %c.var: ref %.3 = var c
+// CHECK:STDOUT:   %c: ref %.3 = bind_name c, %c.var
+// CHECK:STDOUT:   %int.make_type_32.loc36_10: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %int.make_type_32.loc36_15: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc36_18.1: %.23 = tuple_literal (%int.make_type_32.loc36_10, %int.make_type_32.loc36_15)
+// CHECK:STDOUT:   %.loc36_18.2: type = value_of_initializer %int.make_type_32.loc36_10 [template = i32]
+// CHECK:STDOUT:   %.loc36_18.3: type = converted %int.make_type_32.loc36_10, %.loc36_18.2 [template = i32]
+// CHECK:STDOUT:   %.loc36_18.4: type = value_of_initializer %int.make_type_32.loc36_15 [template = i32]
+// CHECK:STDOUT:   %.loc36_18.5: type = converted %int.make_type_32.loc36_15, %.loc36_18.4 [template = i32]
+// CHECK:STDOUT:   %.loc36_18.6: type = converted %.loc36_18.1, constants.%.22 [template = constants.%.22]
+// CHECK:STDOUT:   %t2.var: ref %.22 = var t2
+// CHECK:STDOUT:   %t2: ref %.22 = bind_name t2, %t2.var
+// CHECK:STDOUT:   %int.make_type_32.loc40: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc40_14: i32 = int_literal 3 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc40_9.1: type = value_of_initializer %int.make_type_32.loc40 [template = i32]
+// CHECK:STDOUT:   %.loc40_9.2: type = converted %int.make_type_32.loc40, %.loc40_9.1 [template = i32]
+// CHECK:STDOUT:   %.loc40_15: type = array_type %.loc40_14, i32 [template = constants.%.3]
 // CHECK:STDOUT:   %d.var: ref %.3 = var d
 // CHECK:STDOUT:   %d: ref %.3 = bind_name d, %d.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @ImplicitAs(constants.%Dest: type) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.11)]
+// CHECK:STDOUT:   %Self: %.11 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
+// CHECK:STDOUT:   %Convert: @ImplicitAs.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @ImplicitAs.%.1 (%.11), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.2 (constants.%.12)]
+// CHECK:STDOUT:   %.3: @ImplicitAs.%.2 (%.12) = assoc_entity element0, imports.%import_ref.6 [symbolic = %.3 (constants.%.13)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.3
+// CHECK:STDOUT:     .Convert = imports.%import_ref.4
+// CHECK:STDOUT:     witness = (imports.%import_ref.5)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Convert(constants.%Dest: type, constants.%Self.1: @ImplicitAs.%.1 (%.11)) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.11)]
+// CHECK:STDOUT:   %Self: %.11 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self: @Convert.%Self (%Self.2)]() -> @Convert.%Dest (%Dest);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc15_20: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc15_23: String = string_literal "Hello" [template = constants.%.7]
-// CHECK:STDOUT:   %.loc15_32: String = string_literal "World" [template = constants.%.8]
-// CHECK:STDOUT:   %.loc15_39.1: %.9 = tuple_literal (%.loc15_20, %.loc15_23, %.loc15_32)
-// CHECK:STDOUT:   %.loc15_39.2: i32 = int_literal 0 [template = constants.%.10]
-// CHECK:STDOUT:   %.loc15_39.3: ref i32 = array_index file.%a.var, %.loc15_39.2
-// CHECK:STDOUT:   %.loc15_39.4: init i32 = initialize_from %.loc15_20 to %.loc15_39.3 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc18_20: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc18_23: String = string_literal "Hello" [template = constants.%.7]
+// CHECK:STDOUT:   %.loc18_32: String = string_literal "World" [template = constants.%.8]
+// CHECK:STDOUT:   %.loc18_39.1: %.9 = tuple_literal (%.loc18_20, %.loc18_23, %.loc18_32)
+// CHECK:STDOUT:   %.loc18_39.2: i32 = int_literal 0 [template = constants.%.10]
+// CHECK:STDOUT:   %.loc18_39.3: ref i32 = array_index file.%a.var, %.loc18_39.2
+// CHECK:STDOUT:   %.loc18_39.4: init i32 = initialize_from %.loc18_20 to %.loc18_39.3 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc18_39.5: init type = call constants.%ImplicitAs(i32) [template = constants.%.14]
+// CHECK:STDOUT:   %.loc18_39.6: %.15 = specific_constant imports.%import_ref.4, @ImplicitAs(i32) [template = constants.%.16]
+// CHECK:STDOUT:   %Convert.ref.loc18: %.15 = name_ref Convert, %.loc18_39.6 [template = constants.%.16]
+// CHECK:STDOUT:   %.loc18_39.7: i32 = converted %.loc18_23, <error> [template = <error>]
 // CHECK:STDOUT:   assign file.%a.var, <error>
 // CHECK:STDOUT:   %t1.ref: ref %.9 = name_ref t1, file.%t1
-// CHECK:STDOUT:   %.loc22_19.1: ref i32 = tuple_access %t1.ref, element0
-// CHECK:STDOUT:   %.loc22_19.2: i32 = bind_value %.loc22_19.1
-// CHECK:STDOUT:   %.loc22_19.3: i32 = int_literal 0 [template = constants.%.10]
-// CHECK:STDOUT:   %.loc22_19.4: ref i32 = array_index file.%b.var, %.loc22_19.3
-// CHECK:STDOUT:   %.loc22_19.5: init i32 = initialize_from %.loc22_19.2 to %.loc22_19.4
-// CHECK:STDOUT:   %.loc22_19.6: ref String = tuple_access %t1.ref, element1
+// CHECK:STDOUT:   %.loc28_19.1: ref i32 = tuple_access %t1.ref, element0
+// CHECK:STDOUT:   %.loc28_19.2: i32 = bind_value %.loc28_19.1
+// CHECK:STDOUT:   %.loc28_19.3: i32 = int_literal 0 [template = constants.%.10]
+// CHECK:STDOUT:   %.loc28_19.4: ref i32 = array_index file.%b.var, %.loc28_19.3
+// CHECK:STDOUT:   %.loc28_19.5: init i32 = initialize_from %.loc28_19.2 to %.loc28_19.4
+// CHECK:STDOUT:   %.loc28_19.6: ref String = tuple_access %t1.ref, element1
+// CHECK:STDOUT:   %.loc28_19.7: init type = call constants.%ImplicitAs(i32) [template = constants.%.14]
+// CHECK:STDOUT:   %.loc28_19.8: %.15 = specific_constant imports.%import_ref.4, @ImplicitAs(i32) [template = constants.%.16]
+// CHECK:STDOUT:   %Convert.ref.loc28: %.15 = name_ref Convert, %.loc28_19.8 [template = constants.%.16]
+// CHECK:STDOUT:   %.loc28_19.9: i32 = converted %.loc28_19.6, <error> [template = <error>]
 // CHECK:STDOUT:   assign file.%b.var, <error>
-// CHECK:STDOUT:   %.loc28_20: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc28_23: i32 = int_literal 2 [template = constants.%.14]
-// CHECK:STDOUT:   %.loc28_24: %.15 = tuple_literal (%.loc28_20, %.loc28_23)
+// CHECK:STDOUT:   %.loc34_20: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc34_23: i32 = int_literal 2 [template = constants.%.21]
+// CHECK:STDOUT:   %.loc34_24: %.22 = tuple_literal (%.loc34_20, %.loc34_23)
 // CHECK:STDOUT:   assign file.%c.var, <error>
-// CHECK:STDOUT:   %t2.ref: ref %.15 = name_ref t2, file.%t2
+// CHECK:STDOUT:   %t2.ref: ref %.22 = name_ref t2, file.%t2
 // CHECK:STDOUT:   assign file.%d.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@ImplicitAs.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@Convert.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert(constants.%Dest, constants.%Self.1) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT:   %.1 => constants.%.11
+// CHECK:STDOUT:   %Self => constants.%Self.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(i32) {
+// CHECK:STDOUT:   %Dest => i32
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.14
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
+// CHECK:STDOUT:   %Convert => constants.%Convert.2
+// CHECK:STDOUT:   %.2 => constants.%.15
+// CHECK:STDOUT:   %.3 => constants.%.16
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/array/function_param.carbon

@@ -43,6 +43,7 @@ fn G() -> i32 {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/generic_empty.carbon

@@ -32,6 +32,7 @@ fn G(T:! type) {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/index_not_literal.carbon

@@ -36,6 +36,7 @@ var b: i32 = a[{.index = 2}.index];
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/array/nine_elements.carbon

@@ -39,6 +39,7 @@ var a: [i32; 9] = (1, 2, 3, 4, 5, 6, 7, 8, 9);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 100 - 5
toolchain/check/testdata/as/adapter_conversion.carbon

@@ -97,7 +97,10 @@ class B {
 // We do not try to implicitly convert from the first operand of `as` to the
 // adapted type of the second operand.
 
-// CHECK:STDERR: fail_adapt_init_from_struct.carbon:[[@LINE+3]]:12: ERROR: Cannot convert from `{.x: i32}` to `B` with `as`.
+// CHECK:STDERR: fail_adapt_init_from_struct.carbon:[[@LINE+6]]:12: ERROR: Cannot convert from `{.x: i32}` to `B` with `as`.
+// CHECK:STDERR: var b: B = {.x = 1} as B;
+// CHECK:STDERR:            ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_adapt_init_from_struct.carbon:[[@LINE+3]]:12: Type `{.x: i32}` does not implement interface `As`.
 // CHECK:STDERR: var b: B = {.x = 1} as B;
 // CHECK:STDERR:            ^~~~~~~~~~~~~
 var b: B = {.x = 1} as B;
@@ -128,6 +131,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -260,6 +264,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -332,6 +337,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -422,6 +428,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -523,20 +530,45 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:   %B: type = class_type @B [template]
 // CHECK:STDOUT:   %.4: type = ptr_type %.3 [template]
 // CHECK:STDOUT:   %.5: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %As.type: type = generic_interface_type @As [template]
+// CHECK:STDOUT:   %As: %As.type = struct_value () [template]
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic]
+// CHECK:STDOUT:   %.6: type = interface_type @As, @As(%Dest) [symbolic]
+// CHECK:STDOUT:   %Self.1: @As.%.1 (%.6) = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Self.2: %.6 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @As(%Dest) [symbolic]
+// CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type %.6, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.8: %.7 = assoc_entity element0, imports.%import_ref.6 [symbolic]
+// CHECK:STDOUT:   %.9: type = interface_type @As, @As(%B) [template]
+// CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @As(%B) [template]
+// CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.10: type = assoc_entity_type %.9, %Convert.type.2 [template]
+// CHECK:STDOUT:   %.11: %.10 = assoc_entity element0, imports.%import_ref.6 [template]
+// CHECK:STDOUT:   %.12: %.7 = assoc_entity element0, imports.%import_ref.7 [symbolic]
+// CHECK:STDOUT:   %struct: %.3 = struct_value (%.5) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     .Int32 = %import_ref
+// CHECK:STDOUT:     .Int32 = %import_ref.1
+// CHECK:STDOUT:     .As = %import_ref.2
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.2: %As.type = import_ref Core//prelude/operators/as, inst+4, loaded [template = constants.%As]
+// CHECK:STDOUT:   %import_ref.3 = import_ref Core//prelude/operators/as, inst+10, unloaded
+// CHECK:STDOUT:   %import_ref.4: @As.%.2 (%.7) = import_ref Core//prelude/operators/as, inst+27, loaded [symbolic = @As.%.3 (constants.%.12)]
+// CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+20, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+20, unloaded
+// CHECK:STDOUT:   %import_ref.7 = import_ref Core//prelude/operators/as, inst+20, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -554,6 +586,25 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:   %b: ref %B = bind_name b, %b.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @As(constants.%Dest: type) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @As, @As(%Dest) [symbolic = %.1 (constants.%.6)]
+// CHECK:STDOUT:   %Self: %.6 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @As(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
+// CHECK:STDOUT:   %Convert: @As.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @As.%.1 (%.6), @As.%Convert.type (%Convert.type.1) [symbolic = %.2 (constants.%.7)]
+// CHECK:STDOUT:   %.3: @As.%.2 (%.7) = assoc_entity element0, imports.%import_ref.6 [symbolic = %.3 (constants.%.8)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.3
+// CHECK:STDOUT:     .Convert = imports.%import_ref.4
+// CHECK:STDOUT:     witness = (imports.%import_ref.5)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: class @A {
 // CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc5_10.1: type = value_of_initializer %int.make_type_32 [template = i32]
@@ -575,12 +626,56 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Convert(constants.%Dest: type, constants.%Self.1: @As.%.1 (%.6)) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:   %.1: type = interface_type @As, @As(%Dest) [symbolic = %.1 (constants.%.6)]
+// CHECK:STDOUT:   %Self: %.6 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self: @Convert.%Self (%Self.2)]() -> @Convert.%Dest (%Dest);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc18_18: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc18_19: %.3 = struct_literal (%.loc18_18)
+// CHECK:STDOUT:   %.loc21_18: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc21_19.1: %.3 = struct_literal (%.loc21_18)
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [template = constants.%B]
+// CHECK:STDOUT:   %.loc21_21.1: init type = call constants.%As(constants.%B) [template = constants.%.9]
+// CHECK:STDOUT:   %.loc21_21.2: %.10 = specific_constant imports.%import_ref.4, @As(constants.%B) [template = constants.%.11]
+// CHECK:STDOUT:   %Convert.ref: %.10 = name_ref Convert, %.loc21_21.2 [template = constants.%.11]
+// CHECK:STDOUT:   %struct: %.3 = struct_value (%.loc21_18) [template = constants.%struct]
+// CHECK:STDOUT:   %.loc21_19.2: %.3 = converted %.loc21_19.1, %struct [template = constants.%struct]
+// CHECK:STDOUT:   %.loc21_21.3: %B = converted %.loc21_19.1, <error> [template = <error>]
 // CHECK:STDOUT:   assign file.%b.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(constants.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(@As.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(@Convert.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert(constants.%Dest, constants.%Self.1) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT:   %.1 => constants.%.6
+// CHECK:STDOUT:   %Self => constants.%Self.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(constants.%B) {
+// CHECK:STDOUT:   %Dest => constants.%B
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.9
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
+// CHECK:STDOUT:   %Convert => constants.%Convert.2
+// CHECK:STDOUT:   %.2 => constants.%.10
+// CHECK:STDOUT:   %.3 => constants.%.11
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/as/as_type.carbon

@@ -27,6 +27,7 @@ let t: type = (i32, i32) as type;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/as/basic.carbon

@@ -30,6 +30,7 @@ fn Main() -> i32 {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 109 - 20
toolchain/check/testdata/as/fail_no_conversion.carbon

@@ -8,7 +8,10 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/fail_no_conversion.carbon
 
-// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+3]]:21: ERROR: Cannot convert from `i32` to `(i32, i32)` with `as`.
+// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+6]]:21: ERROR: Cannot convert from `i32` to `(i32, i32)` with `as`.
+// CHECK:STDERR: let n: (i32, i32) = 1 as (i32, i32);
+// CHECK:STDERR:                     ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+3]]:21: Type `i32` does not implement interface `As`.
 // CHECK:STDERR: let n: (i32, i32) = 1 as (i32, i32);
 // CHECK:STDERR:                     ^~~~~~~~~~~~~~~
 let n: (i32, i32) = 1 as (i32, i32);
@@ -23,20 +26,44 @@ let n: (i32, i32) = 1 as (i32, i32);
 // CHECK:STDOUT:   %.3: type = tuple_type (i32, i32) [template]
 // CHECK:STDOUT:   %.4: type = ptr_type %.3 [template]
 // CHECK:STDOUT:   %.5: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %As.type: type = generic_interface_type @As [template]
+// CHECK:STDOUT:   %As: %As.type = struct_value () [template]
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic]
+// CHECK:STDOUT:   %.6: type = interface_type @As, @As(%Dest) [symbolic]
+// CHECK:STDOUT:   %Self.1: @As.%.1 (%.6) = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Self.2: %.6 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @As(%Dest) [symbolic]
+// CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type %.6, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.8: %.7 = assoc_entity element0, imports.%import_ref.6 [symbolic]
+// CHECK:STDOUT:   %.9: type = interface_type @As, @As(%.3) [template]
+// CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @As(%.3) [template]
+// CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.10: type = assoc_entity_type %.9, %Convert.type.2 [template]
+// CHECK:STDOUT:   %.11: %.10 = assoc_entity element0, imports.%import_ref.6 [template]
+// CHECK:STDOUT:   %.12: %.7 = assoc_entity element0, imports.%import_ref.7 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     .Int32 = %import_ref
+// CHECK:STDOUT:     .Int32 = %import_ref.1
+// CHECK:STDOUT:     .As = %import_ref.2
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.2: %As.type = import_ref Core//prelude/operators/as, inst+4, loaded [template = constants.%As]
+// CHECK:STDOUT:   %import_ref.3 = import_ref Core//prelude/operators/as, inst+10, unloaded
+// CHECK:STDOUT:   %import_ref.4: @As.%.2 (%.7) = import_ref Core//prelude/operators/as, inst+27, loaded [symbolic = @As.%.3 (constants.%.12)]
+// CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+20, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+20, unloaded
+// CHECK:STDOUT:   %import_ref.7 = import_ref Core//prelude/operators/as, inst+20, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -45,30 +72,92 @@ let n: (i32, i32) = 1 as (i32, i32);
 // CHECK:STDOUT:     .n = @__global_init.%n
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %int.make_type_32.loc14_9: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %int.make_type_32.loc14_14: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc14_17.1: %.2 = tuple_literal (%int.make_type_32.loc14_9, %int.make_type_32.loc14_14)
-// CHECK:STDOUT:   %.loc14_17.2: type = value_of_initializer %int.make_type_32.loc14_9 [template = i32]
-// CHECK:STDOUT:   %.loc14_17.3: type = converted %int.make_type_32.loc14_9, %.loc14_17.2 [template = i32]
-// CHECK:STDOUT:   %.loc14_17.4: type = value_of_initializer %int.make_type_32.loc14_14 [template = i32]
-// CHECK:STDOUT:   %.loc14_17.5: type = converted %int.make_type_32.loc14_14, %.loc14_17.4 [template = i32]
-// CHECK:STDOUT:   %.loc14_17.6: type = converted %.loc14_17.1, constants.%.3 [template = constants.%.3]
+// CHECK:STDOUT:   %int.make_type_32.loc17_9: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %int.make_type_32.loc17_14: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc17_17.1: %.2 = tuple_literal (%int.make_type_32.loc17_9, %int.make_type_32.loc17_14)
+// CHECK:STDOUT:   %.loc17_17.2: type = value_of_initializer %int.make_type_32.loc17_9 [template = i32]
+// CHECK:STDOUT:   %.loc17_17.3: type = converted %int.make_type_32.loc17_9, %.loc17_17.2 [template = i32]
+// CHECK:STDOUT:   %.loc17_17.4: type = value_of_initializer %int.make_type_32.loc17_14 [template = i32]
+// CHECK:STDOUT:   %.loc17_17.5: type = converted %int.make_type_32.loc17_14, %.loc17_17.4 [template = i32]
+// CHECK:STDOUT:   %.loc17_17.6: type = converted %.loc17_17.1, constants.%.3 [template = constants.%.3]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @As(constants.%Dest: type) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @As, @As(%Dest) [symbolic = %.1 (constants.%.6)]
+// CHECK:STDOUT:   %Self: %.6 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @As(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
+// CHECK:STDOUT:   %Convert: @As.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @As.%.1 (%.6), @As.%Convert.type (%Convert.type.1) [symbolic = %.2 (constants.%.7)]
+// CHECK:STDOUT:   %.3: @As.%.2 (%.7) = assoc_entity element0, imports.%import_ref.6 [symbolic = %.3 (constants.%.8)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.3
+// CHECK:STDOUT:     .Convert = imports.%import_ref.4
+// CHECK:STDOUT:     witness = (imports.%import_ref.5)
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Convert(constants.%Dest: type, constants.%Self.1: @As.%.1 (%.6)) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:   %.1: type = interface_type @As, @As(%Dest) [symbolic = %.1 (constants.%.6)]
+// CHECK:STDOUT:   %Self: %.6 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self: @Convert.%Self (%Self.2)]() -> @Convert.%Dest (%Dest);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc14_21: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %int.make_type_32.loc14_27: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %int.make_type_32.loc14_32: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc14_35.1: %.2 = tuple_literal (%int.make_type_32.loc14_27, %int.make_type_32.loc14_32)
-// CHECK:STDOUT:   %.loc14_35.2: type = value_of_initializer %int.make_type_32.loc14_27 [template = i32]
-// CHECK:STDOUT:   %.loc14_35.3: type = converted %int.make_type_32.loc14_27, %.loc14_35.2 [template = i32]
-// CHECK:STDOUT:   %.loc14_35.4: type = value_of_initializer %int.make_type_32.loc14_32 [template = i32]
-// CHECK:STDOUT:   %.loc14_35.5: type = converted %int.make_type_32.loc14_32, %.loc14_35.4 [template = i32]
-// CHECK:STDOUT:   %.loc14_35.6: type = converted %.loc14_35.1, constants.%.3 [template = constants.%.3]
+// CHECK:STDOUT:   %.loc17_21: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %int.make_type_32.loc17_27: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %int.make_type_32.loc17_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc17_35.1: %.2 = tuple_literal (%int.make_type_32.loc17_27, %int.make_type_32.loc17_32)
+// CHECK:STDOUT:   %.loc17_35.2: type = value_of_initializer %int.make_type_32.loc17_27 [template = i32]
+// CHECK:STDOUT:   %.loc17_35.3: type = converted %int.make_type_32.loc17_27, %.loc17_35.2 [template = i32]
+// CHECK:STDOUT:   %.loc17_35.4: type = value_of_initializer %int.make_type_32.loc17_32 [template = i32]
+// CHECK:STDOUT:   %.loc17_35.5: type = converted %int.make_type_32.loc17_32, %.loc17_35.4 [template = i32]
+// CHECK:STDOUT:   %.loc17_35.6: type = converted %.loc17_35.1, constants.%.3 [template = constants.%.3]
+// CHECK:STDOUT:   %.loc17_23.1: init type = call constants.%As(constants.%.3) [template = constants.%.9]
+// CHECK:STDOUT:   %.loc17_23.2: %.10 = specific_constant imports.%import_ref.4, @As(constants.%.3) [template = constants.%.11]
+// CHECK:STDOUT:   %Convert.ref: %.10 = name_ref Convert, %.loc17_23.2 [template = constants.%.11]
+// CHECK:STDOUT:   %.loc17_23.3: %.3 = converted %.loc17_21, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc17_23.4: i32 = tuple_access %.loc17_23.3, element0 [template = <error>]
 // CHECK:STDOUT:   %n: %.3 = bind_name n, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(constants.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(@As.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(@Convert.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert(constants.%Dest, constants.%Self.1) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT:   %.1 => constants.%.6
+// CHECK:STDOUT:   %Self => constants.%Self.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(constants.%.3) {
+// CHECK:STDOUT:   %Dest => constants.%.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.9
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
+// CHECK:STDOUT:   %Convert => constants.%Convert.2
+// CHECK:STDOUT:   %.2 => constants.%.10
+// CHECK:STDOUT:   %.3 => constants.%.11
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 95 - 7
toolchain/check/testdata/as/fail_not_type.carbon

@@ -8,7 +8,10 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/fail_not_type.carbon
 
-// CHECK:STDERR: fail_not_type.carbon:[[@LINE+3]]:19: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: fail_not_type.carbon:[[@LINE+6]]:19: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: let n: i32 = 1 as 2;
+// CHECK:STDERR:                   ^
+// CHECK:STDERR: fail_not_type.carbon:[[@LINE+3]]:19: Type `i32` does not implement interface `ImplicitAs`.
 // CHECK:STDERR: let n: i32 = 1 as 2;
 // CHECK:STDERR:                   ^
 let n: i32 = 1 as 2;
@@ -21,20 +24,44 @@ let n: i32 = 1 as 2;
 // CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
 // CHECK:STDOUT:   %.2: i32 = int_literal 1 [template]
 // CHECK:STDOUT:   %.3: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %ImplicitAs.type: type = generic_interface_type @ImplicitAs [template]
+// CHECK:STDOUT:   %ImplicitAs: %ImplicitAs.type = struct_value () [template]
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic]
+// CHECK:STDOUT:   %.4: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Self.1: @ImplicitAs.%.1 (%.4) = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Self.2: %.4 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.5: type = assoc_entity_type %.4, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.6: %.5 = assoc_entity element0, imports.%import_ref.6 [symbolic]
+// CHECK:STDOUT:   %.7: type = interface_type @ImplicitAs, @ImplicitAs(type) [template]
+// CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @ImplicitAs(type) [template]
+// CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.8: type = assoc_entity_type %.7, %Convert.type.2 [template]
+// CHECK:STDOUT:   %.9: %.8 = assoc_entity element0, imports.%import_ref.6 [template]
+// CHECK:STDOUT:   %.10: %.5 = assoc_entity element0, imports.%import_ref.7 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     .Int32 = %import_ref
+// CHECK:STDOUT:     .Int32 = %import_ref.1
+// CHECK:STDOUT:     .ImplicitAs = %import_ref.2
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.2: %ImplicitAs.type = import_ref Core//prelude/operators/as, inst+37, loaded [template = constants.%ImplicitAs]
+// CHECK:STDOUT:   %import_ref.3 = import_ref Core//prelude/operators/as, inst+42, unloaded
+// CHECK:STDOUT:   %import_ref.4: @ImplicitAs.%.2 (%.5) = import_ref Core//prelude/operators/as, inst+59, loaded [symbolic = @ImplicitAs.%.3 (constants.%.10)]
+// CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.7 = import_ref Core//prelude/operators/as, inst+52, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -44,17 +71,78 @@ let n: i32 = 1 as 2;
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc14_8.1: type = value_of_initializer %int.make_type_32 [template = i32]
-// CHECK:STDOUT:   %.loc14_8.2: type = converted %int.make_type_32, %.loc14_8.1 [template = i32]
+// CHECK:STDOUT:   %.loc17_8.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:   %.loc17_8.2: type = converted %int.make_type_32, %.loc17_8.1 [template = i32]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @ImplicitAs(constants.%Dest: type) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.4)]
+// CHECK:STDOUT:   %Self: %.4 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
+// CHECK:STDOUT:   %Convert: @ImplicitAs.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @ImplicitAs.%.1 (%.4), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.2 (constants.%.5)]
+// CHECK:STDOUT:   %.3: @ImplicitAs.%.2 (%.5) = assoc_entity element0, imports.%import_ref.6 [symbolic = %.3 (constants.%.6)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.3
+// CHECK:STDOUT:     .Convert = imports.%import_ref.4
+// CHECK:STDOUT:     witness = (imports.%import_ref.5)
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Convert(constants.%Dest: type, constants.%Self.1: @ImplicitAs.%.1 (%.4)) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.4)]
+// CHECK:STDOUT:   %Self: %.4 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self: @Convert.%Self (%Self.2)]() -> @Convert.%Dest (%Dest);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc14_14: i32 = int_literal 1 [template = constants.%.2]
-// CHECK:STDOUT:   %.loc14_19: i32 = int_literal 2 [template = constants.%.3]
+// CHECK:STDOUT:   %.loc17_14: i32 = int_literal 1 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc17_19.1: i32 = int_literal 2 [template = constants.%.3]
+// CHECK:STDOUT:   %.loc17_19.2: init type = call constants.%ImplicitAs(type) [template = constants.%.7]
+// CHECK:STDOUT:   %.loc17_19.3: %.8 = specific_constant imports.%import_ref.4, @ImplicitAs(type) [template = constants.%.9]
+// CHECK:STDOUT:   %Convert.ref: %.8 = name_ref Convert, %.loc17_19.3 [template = constants.%.9]
+// CHECK:STDOUT:   %.loc17_19.4: type = converted %.loc17_19.1, <error> [template = <error>]
 // CHECK:STDOUT:   %n: i32 = bind_name n, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@ImplicitAs.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@Convert.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert(constants.%Dest, constants.%Self.1) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT:   %.1 => constants.%.4
+// CHECK:STDOUT:   %Self => constants.%Self.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(type) {
+// CHECK:STDOUT:   %Dest => type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.7
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
+// CHECK:STDOUT:   %Convert => constants.%Convert.2
+// CHECK:STDOUT:   %.2 => constants.%.8
+// CHECK:STDOUT:   %.3 => constants.%.9
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/as/identity.carbon

@@ -52,6 +52,7 @@ fn Initializing() {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 303 - 0
toolchain/check/testdata/as/overloaded.carbon

@@ -0,0 +1,303 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/overloaded.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/overloaded.carbon
+
+class X {
+  var n: i32;
+}
+
+impl i32 as Core.As(X) {
+  fn Convert[self: i32]() -> X { return {.n = self}; }
+}
+
+impl X as Core.As(i32) {
+  fn Convert[self: X]() -> i32 { return self.n; }
+}
+
+let n: i32 = ((4 as i32) as X) as i32;
+
+// CHECK:STDOUT: --- overloaded.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %X: type = class_type @X [template]
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = unbound_element_type %X, i32 [template]
+// CHECK:STDOUT:   %.3: type = struct_type {.n: i32} [template]
+// CHECK:STDOUT:   %As.type: type = generic_interface_type @As [template]
+// CHECK:STDOUT:   %As: %As.type = struct_value () [template]
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic]
+// CHECK:STDOUT:   %.4: type = interface_type @As, @As(%Dest) [symbolic]
+// CHECK:STDOUT:   %Self.1: @As.%.1 (%.4) = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Self.2: %.4 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert.1, @As(%Dest) [symbolic]
+// CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.5: type = assoc_entity_type %.4, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.6: %.5 = assoc_entity element0, imports.%import_ref.6 [symbolic]
+// CHECK:STDOUT:   %.7: type = interface_type @As, @As(%X) [template]
+// CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert.2 [template]
+// CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %Convert.type.3: type = fn_type @Convert.1, @As(%X) [template]
+// CHECK:STDOUT:   %Convert.3: %Convert.type.3 = struct_value () [template]
+// CHECK:STDOUT:   %.8: type = assoc_entity_type %.7, %Convert.type.3 [template]
+// CHECK:STDOUT:   %.9: %.8 = assoc_entity element0, imports.%import_ref.6 [template]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (%Convert.2) [template]
+// CHECK:STDOUT:   %.11: type = ptr_type %.3 [template]
+// CHECK:STDOUT:   %.12: type = interface_type @As, @As(i32) [template]
+// CHECK:STDOUT:   %Convert.type.4: type = fn_type @Convert.3 [template]
+// CHECK:STDOUT:   %Convert.4: %Convert.type.4 = struct_value () [template]
+// CHECK:STDOUT:   %Convert.type.5: type = fn_type @Convert.1, @As(i32) [template]
+// CHECK:STDOUT:   %Convert.5: %Convert.type.5 = struct_value () [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type %.12, %Convert.type.5 [template]
+// CHECK:STDOUT:   %.14: %.13 = assoc_entity element0, imports.%import_ref.6 [template]
+// CHECK:STDOUT:   %.15: <witness> = interface_witness (%Convert.4) [template]
+// CHECK:STDOUT:   %.16: i32 = int_literal 4 [template]
+// CHECK:STDOUT:   %.17: %.5 = assoc_entity element0, imports.%import_ref.7 [symbolic]
+// CHECK:STDOUT:   %.18: <bound method> = bound_method %.16, %Convert.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .Int32 = %import_ref.1
+// CHECK:STDOUT:     .As = %import_ref.2
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/operators
+// CHECK:STDOUT:     import Core//prelude/types
+// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
+// CHECK:STDOUT:     import Core//prelude/operators/bitwise
+// CHECK:STDOUT:     import Core//prelude/operators/comparison
+// CHECK:STDOUT:     import Core//prelude/types/bool
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.2: %As.type = import_ref Core//prelude/operators/as, inst+4, loaded [template = constants.%As]
+// CHECK:STDOUT:   %import_ref.3 = import_ref Core//prelude/operators/as, inst+10, unloaded
+// CHECK:STDOUT:   %import_ref.4: @As.%.2 (%.5) = import_ref Core//prelude/operators/as, inst+27, loaded [symbolic = @As.%.3 (constants.%.17)]
+// CHECK:STDOUT:   %import_ref.5: @As.%Convert.type (%Convert.type.1) = import_ref Core//prelude/operators/as, inst+20, loaded [symbolic = @As.%Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+20, unloaded
+// CHECK:STDOUT:   %import_ref.7 = import_ref Core//prelude/operators/as, inst+20, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .X = %X.decl
+// CHECK:STDOUT:     .n = @__global_init.%n
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %X.decl: type = class_decl @X [template = constants.%X] {}
+// CHECK:STDOUT:   %.loc15_22.1: type = value_of_initializer %.loc15_20 [template = constants.%.7]
+// CHECK:STDOUT:   %.loc15_22.2: type = converted %.loc15_20, %.loc15_22.1 [template = constants.%.7]
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %int.make_type_32.loc15: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc15_6.1: type = value_of_initializer %int.make_type_32.loc15 [template = i32]
+// CHECK:STDOUT:     %.loc15_6.2: type = converted %int.make_type_32.loc15, %.loc15_6.1 [template = i32]
+// CHECK:STDOUT:     %Core.ref.loc15: <namespace> = name_ref Core, imports.%Core [template = imports.%Core]
+// CHECK:STDOUT:     %As.ref.loc15: %As.type = name_ref As, imports.%import_ref.2 [template = constants.%As]
+// CHECK:STDOUT:     %X.ref.loc15: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %.loc15_20: init type = call %As.ref.loc15(%X.ref.loc15) [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc19_22.1: type = value_of_initializer %.loc19_18.3 [template = constants.%.12]
+// CHECK:STDOUT:   %.loc19_22.2: type = converted %.loc19_18.3, %.loc19_22.1 [template = constants.%.12]
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %X.ref.loc19: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %Core.ref.loc19: <namespace> = name_ref Core, imports.%Core [template = imports.%Core]
+// CHECK:STDOUT:     %As.ref.loc19: %As.type = name_ref As, imports.%import_ref.2 [template = constants.%As]
+// CHECK:STDOUT:     %int.make_type_32.loc19: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc19_18.1: type = value_of_initializer %int.make_type_32.loc19 [template = i32]
+// CHECK:STDOUT:     %.loc19_18.2: type = converted %int.make_type_32.loc19, %.loc19_18.1 [template = i32]
+// CHECK:STDOUT:     %.loc19_18.3: init type = call %As.ref.loc19(%.loc19_18.2) [template = constants.%.12]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %int.make_type_32.loc23: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc23_8.1: type = value_of_initializer %int.make_type_32.loc23 [template = i32]
+// CHECK:STDOUT:   %.loc23_8.2: type = converted %int.make_type_32.loc23, %.loc23_8.1 [template = i32]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @As(constants.%Dest: type) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @As, @As(%Dest) [symbolic = %.1 (constants.%.4)]
+// CHECK:STDOUT:   %Self: %.4 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:   %Convert.type: type = fn_type @Convert.1, @As(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
+// CHECK:STDOUT:   %Convert: @As.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @As.%.1 (%.4), @As.%Convert.type (%Convert.type.1) [symbolic = %.2 (constants.%.5)]
+// CHECK:STDOUT:   %.3: @As.%.2 (%.5) = assoc_entity element0, imports.%import_ref.6 [symbolic = %.3 (constants.%.6)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.3
+// CHECK:STDOUT:     .Convert = imports.%import_ref.4
+// CHECK:STDOUT:     witness = (imports.%import_ref.5)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: i32 as %.7 {
+// CHECK:STDOUT:   %Convert.decl: %Convert.type.2 = fn_decl @Convert.2 [template = constants.%Convert.2] {
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc16_20.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc16_20.2: type = converted %int.make_type_32, %.loc16_20.1 [template = i32]
+// CHECK:STDOUT:     %self.loc16_14.1: i32 = param self
+// CHECK:STDOUT:     %self.loc16_14.2: i32 = bind_name self, %self.loc16_14.1
+// CHECK:STDOUT:     %X.ref: type = name_ref X, file.%X.decl [template = constants.%X]
+// CHECK:STDOUT:     %return.var: ref %X = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc15: <witness> = interface_witness (%Convert.decl) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Convert = %Convert.decl
+// CHECK:STDOUT:   witness = %.loc15
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: %X as %.12 {
+// CHECK:STDOUT:   %Convert.decl: %Convert.type.4 = fn_decl @Convert.3 [template = constants.%Convert.4] {
+// CHECK:STDOUT:     %X.ref: type = name_ref X, file.%X.decl [template = constants.%X]
+// CHECK:STDOUT:     %self.loc20_14.1: %X = param self
+// CHECK:STDOUT:     %self.loc20_14.2: %X = bind_name self, %self.loc20_14.1
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc20_28.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc20_28.2: type = converted %int.make_type_32, %.loc20_28.1 [template = i32]
+// CHECK:STDOUT:     %return.var: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc19: <witness> = interface_witness (%Convert.decl) [template = constants.%.15]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Convert = %Convert.decl
+// CHECK:STDOUT:   witness = %.loc19
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @X {
+// CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc12_10.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:   %.loc12_10.2: type = converted %int.make_type_32, %.loc12_10.1 [template = i32]
+// CHECK:STDOUT:   %.loc12_8: %.2 = field_decl n, element0 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%X
+// CHECK:STDOUT:   .n = %.loc12_8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Convert.1(constants.%Dest: type, constants.%Self.1: @As.%.1 (%.4)) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:   %.1: type = interface_type @As, @As(%Dest) [symbolic = %.1 (constants.%.4)]
+// CHECK:STDOUT:   %Self: %.4 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self: @Convert.1.%Self (%Self.2)]() -> @Convert.1.%Dest (%Dest);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Convert.2[@impl.1.%self.loc16_14.2: i32]() -> @impl.1.%return.var: %X {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %self.ref: i32 = name_ref self, @impl.1.%self.loc16_14.2
+// CHECK:STDOUT:   %.loc16_51.1: %.3 = struct_literal (%self.ref)
+// CHECK:STDOUT:   %.loc16_51.2: ref i32 = class_element_access @impl.1.%return.var, element0
+// CHECK:STDOUT:   %.loc16_51.3: init i32 = initialize_from %self.ref to %.loc16_51.2
+// CHECK:STDOUT:   %.loc16_51.4: init %X = class_init (%.loc16_51.3), @impl.1.%return.var
+// CHECK:STDOUT:   %.loc16_52: init %X = converted %.loc16_51.1, %.loc16_51.4
+// CHECK:STDOUT:   return %.loc16_52 to @impl.1.%return.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Convert.3[@impl.2.%self.loc20_14.2: %X]() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %self.ref: %X = name_ref self, @impl.2.%self.loc20_14.2
+// CHECK:STDOUT:   %n.ref: %.2 = name_ref n, @X.%.loc12_8 [template = @X.%.loc12_8]
+// CHECK:STDOUT:   %.loc20_45.1: ref i32 = class_element_access %self.ref, element0
+// CHECK:STDOUT:   %.loc20_45.2: i32 = bind_value %.loc20_45.1
+// CHECK:STDOUT:   return %.loc20_45.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc23_16: i32 = int_literal 4 [template = constants.%.16]
+// CHECK:STDOUT:   %int.make_type_32.loc23_21: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc23_21.1: type = value_of_initializer %int.make_type_32.loc23_21 [template = i32]
+// CHECK:STDOUT:   %.loc23_21.2: type = converted %int.make_type_32.loc23_21, %.loc23_21.1 [template = i32]
+// CHECK:STDOUT:   %X.ref: type = name_ref X, file.%X.decl [template = constants.%X]
+// CHECK:STDOUT:   %.loc23_26.1: init type = call constants.%As(constants.%X) [template = constants.%.7]
+// CHECK:STDOUT:   %.loc23_26.2: %.8 = specific_constant imports.%import_ref.4, @As(constants.%X) [template = constants.%.9]
+// CHECK:STDOUT:   %Convert.ref.loc23_26: %.8 = name_ref Convert, %.loc23_26.2 [template = constants.%.9]
+// CHECK:STDOUT:   %.loc23_26.3: %Convert.type.3 = interface_witness_access @impl.1.%.loc15, element0 [template = constants.%Convert.2]
+// CHECK:STDOUT:   %.loc23_26.4: <bound method> = bound_method %.loc23_16, %.loc23_26.3 [template = constants.%.18]
+// CHECK:STDOUT:   %.loc23_26.5: ref %X = temporary_storage
+// CHECK:STDOUT:   %Convert.call.loc23_26: init %X = call %.loc23_26.4(%.loc23_16) to %.loc23_26.5
+// CHECK:STDOUT:   %.loc23_26.6: init %X = converted %.loc23_16, %Convert.call.loc23_26
+// CHECK:STDOUT:   %int.make_type_32.loc23_35: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc23_35.1: type = value_of_initializer %int.make_type_32.loc23_35 [template = i32]
+// CHECK:STDOUT:   %.loc23_35.2: type = converted %int.make_type_32.loc23_35, %.loc23_35.1 [template = i32]
+// CHECK:STDOUT:   %.loc23_32.1: init type = call constants.%As(i32) [template = constants.%.12]
+// CHECK:STDOUT:   %.loc23_32.2: %.13 = specific_constant imports.%import_ref.4, @As(i32) [template = constants.%.14]
+// CHECK:STDOUT:   %Convert.ref.loc23_32: %.13 = name_ref Convert, %.loc23_32.2 [template = constants.%.14]
+// CHECK:STDOUT:   %.loc23_26.7: ref %X = temporary %.loc23_26.5, %.loc23_26.6
+// CHECK:STDOUT:   %.loc23_32.3: %Convert.type.5 = interface_witness_access @impl.2.%.loc19, element0 [template = constants.%Convert.4]
+// CHECK:STDOUT:   %.loc23_32.4: <bound method> = bound_method %.loc23_26.7, %.loc23_32.3
+// CHECK:STDOUT:   %.loc23_26.8: %X = bind_value %.loc23_26.7
+// CHECK:STDOUT:   %Convert.call.loc23_32: init i32 = call %.loc23_32.4(%.loc23_26.8)
+// CHECK:STDOUT:   %.loc23_32.5: init i32 = converted %.loc23_26.6, %Convert.call.loc23_32
+// CHECK:STDOUT:   %.loc23_38.1: i32 = value_of_initializer %.loc23_32.5
+// CHECK:STDOUT:   %.loc23_38.2: i32 = converted %.loc23_32.5, %.loc23_38.1
+// CHECK:STDOUT:   %n: i32 = bind_name n, %.loc23_38.2
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(constants.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(@As.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(@Convert.1.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert.1(constants.%Dest, constants.%Self.1) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT:   %.1 => constants.%.4
+// CHECK:STDOUT:   %Self => constants.%Self.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(constants.%X) {
+// CHECK:STDOUT:   %Dest => constants.%X
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.7
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.3
+// CHECK:STDOUT:   %Convert => constants.%Convert.3
+// CHECK:STDOUT:   %.2 => constants.%.8
+// CHECK:STDOUT:   %.3 => constants.%.9
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert.1(constants.%X, i32) {
+// CHECK:STDOUT:   %Dest => constants.%X
+// CHECK:STDOUT:   %.1 => constants.%.7
+// CHECK:STDOUT:   %Self => i32
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @As(i32) {
+// CHECK:STDOUT:   %Dest => i32
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.12
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.5
+// CHECK:STDOUT:   %Convert => constants.%Convert.5
+// CHECK:STDOUT:   %.2 => constants.%.13
+// CHECK:STDOUT:   %.3 => constants.%.14
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert.1(i32, constants.%X) {
+// CHECK:STDOUT:   %Dest => i32
+// CHECK:STDOUT:   %.1 => constants.%.12
+// CHECK:STDOUT:   %Self => constants.%X
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/basics/builtin_types.carbon

@@ -36,6 +36,7 @@ var test_type: type = i32;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/basics/fail_bad_run.carbon

@@ -32,6 +32,7 @@ fn Run() -> String {}
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/basics/fail_bad_run_2.carbon

@@ -30,6 +30,7 @@ fn Run(n: i32) {}
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 91 - 2
toolchain/check/testdata/basics/fail_non_type_as_type.carbon

@@ -8,7 +8,10 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/fail_non_type_as_type.carbon
 
-// CHECK:STDERR: fail_non_type_as_type.carbon:[[@LINE+3]]:1: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: fail_non_type_as_type.carbon:[[@LINE+6]]:1: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: var x: type = 42;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_non_type_as_type.carbon:[[@LINE+3]]:1: Type `i32` does not implement interface `ImplicitAs`.
 // CHECK:STDERR: var x: type = 42;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~
 var x: type = 42;
@@ -17,18 +20,43 @@ var x: type = 42;
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %.1: i32 = int_literal 42 [template]
+// CHECK:STDOUT:   %ImplicitAs.type: type = generic_interface_type @ImplicitAs [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %ImplicitAs: %ImplicitAs.type = struct_value () [template]
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic]
+// CHECK:STDOUT:   %.3: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Self.1: @ImplicitAs.%.1 (%.3) = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Self.2: %.3 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
+// CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.4: type = assoc_entity_type %.3, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.5: %.4 = assoc_entity element0, imports.%import_ref.5 [symbolic]
+// CHECK:STDOUT:   %.6: type = interface_type @ImplicitAs, @ImplicitAs(type) [template]
+// CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @ImplicitAs(type) [template]
+// CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type %.6, %Convert.type.2 [template]
+// CHECK:STDOUT:   %.8: %.7 = assoc_entity element0, imports.%import_ref.5 [template]
+// CHECK:STDOUT:   %.9: %.4 = assoc_entity element0, imports.%import_ref.6 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .ImplicitAs = %import_ref.1
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: %ImplicitAs.type = import_ref Core//prelude/operators/as, inst+37, loaded [template = constants.%ImplicitAs]
+// CHECK:STDOUT:   %import_ref.2 = import_ref Core//prelude/operators/as, inst+42, unloaded
+// CHECK:STDOUT:   %import_ref.3: @ImplicitAs.%.2 (%.4) = import_ref Core//prelude/operators/as, inst+59, loaded [symbolic = @ImplicitAs.%.3 (constants.%.9)]
+// CHECK:STDOUT:   %import_ref.4 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+52, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+52, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -41,10 +69,71 @@ var x: type = 42;
 // CHECK:STDOUT:   %x: ref type = bind_name x, %x.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @ImplicitAs(constants.%Dest: type) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.3)]
+// CHECK:STDOUT:   %Self: %.3 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
+// CHECK:STDOUT:   %Convert: @ImplicitAs.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @ImplicitAs.%.1 (%.3), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.2 (constants.%.4)]
+// CHECK:STDOUT:   %.3: @ImplicitAs.%.2 (%.4) = assoc_entity element0, imports.%import_ref.5 [symbolic = %.3 (constants.%.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.2
+// CHECK:STDOUT:     .Convert = imports.%import_ref.3
+// CHECK:STDOUT:     witness = (imports.%import_ref.4)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Convert(constants.%Dest: type, constants.%Self.1: @ImplicitAs.%.1 (%.3)) {
+// CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest 0 [symbolic = %Dest (constants.%Dest)]
+// CHECK:STDOUT:   %.1: type = interface_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic = %.1 (constants.%.3)]
+// CHECK:STDOUT:   %Self: %.3 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self: @Convert.%Self (%Self.2)]() -> @Convert.%Dest (%Dest);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc14: i32 = int_literal 42 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc17_15: i32 = int_literal 42 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc17_17.1: init type = call constants.%ImplicitAs(type) [template = constants.%.6]
+// CHECK:STDOUT:   %.loc17_17.2: %.7 = specific_constant imports.%import_ref.3, @ImplicitAs(type) [template = constants.%.8]
+// CHECK:STDOUT:   %Convert.ref: %.7 = name_ref Convert, %.loc17_17.2 [template = constants.%.8]
+// CHECK:STDOUT:   %.loc17_17.3: type = converted %.loc17_15, <error> [template = <error>]
 // CHECK:STDOUT:   assign file.%x.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@ImplicitAs.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(@Convert.%Dest) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Convert(constants.%Dest, constants.%Self.1) {
+// CHECK:STDOUT:   %Dest => constants.%Dest
+// CHECK:STDOUT:   %.1 => constants.%.3
+// CHECK:STDOUT:   %Self => constants.%Self.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitAs(type) {
+// CHECK:STDOUT:   %Dest => type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1 => constants.%.6
+// CHECK:STDOUT:   %Self => constants.%Self.2
+// CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
+// CHECK:STDOUT:   %Convert => constants.%Convert.2
+// CHECK:STDOUT:   %.2 => constants.%.7
+// CHECK:STDOUT:   %.3 => constants.%.8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/basics/fail_numeric_literal_overflow.carbon

@@ -56,6 +56,7 @@ let e: f64 = 5.0e39999999999999999993;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/basics/fail_qualifier_unsupported.carbon

@@ -29,6 +29,7 @@ var y: i32 = x.b;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/basics/multifile.carbon

@@ -32,6 +32,7 @@ fn B() {}
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -66,6 +67,7 @@ fn B() {}
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/basics/numeric_literals.carbon

@@ -74,6 +74,7 @@ fn F() {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/basics/parens.carbon

@@ -28,6 +28,7 @@ var b: i32 = ((2));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/basics/run.carbon

@@ -24,6 +24,7 @@ fn Run() {}
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/basics/run_i32.carbon

@@ -28,6 +28,7 @@ fn Run() -> i32 { return 0; }
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 4 - 0
toolchain/check/testdata/basics/type_literals.carbon

@@ -132,6 +132,7 @@ var test_f128: f128;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -189,6 +190,7 @@ var test_f128: f128;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -257,6 +259,7 @@ var test_f128: f128;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -314,6 +317,7 @@ var test_f128: f128;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/bool/make_type.carbon

@@ -36,6 +36,7 @@ var b: Bool() = false;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -71,6 +72,7 @@ var b: Bool() = false;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/float/add.carbon

@@ -73,6 +73,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -192,6 +193,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/float/div.carbon

@@ -79,6 +79,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -222,6 +223,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/float/eq.carbon

@@ -66,6 +66,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.eq";
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -220,6 +221,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.eq";
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/float/greater.carbon

@@ -65,6 +65,7 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/float/greater_eq.carbon

@@ -65,6 +65,7 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/float/less.carbon

@@ -65,6 +65,7 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/float/less_eq.carbon

@@ -65,6 +65,7 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/float/make_type.carbon

@@ -61,6 +61,7 @@ var dyn: Float(dyn_size);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -110,6 +111,7 @@ var dyn: Float(dyn_size);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -184,6 +186,7 @@ var dyn: Float(dyn_size);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/float/mul.carbon

@@ -73,6 +73,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -192,6 +193,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/float/negate.carbon

@@ -93,6 +93,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -204,6 +205,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/float/neq.carbon

@@ -66,6 +66,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.neq";
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -220,6 +221,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.neq";
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/float/sub.carbon

@@ -73,6 +73,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -192,6 +193,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/and.carbon

@@ -43,6 +43,7 @@ fn RuntimeCall(a: i32, b: i32) -> i32 {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/complement.carbon

@@ -47,6 +47,7 @@ fn RuntimeCall(a: i32) -> i32 {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/int/eq.carbon

@@ -65,6 +65,7 @@ fn WrongResult(a: i32, b: i32) -> i32 = "int.eq";
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -214,6 +215,7 @@ fn WrongResult(a: i32, b: i32) -> i32 = "int.eq";
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/greater.carbon

@@ -64,6 +64,7 @@ fn RuntimeCall(a: i32, b: i32) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/greater_eq.carbon

@@ -64,6 +64,7 @@ fn RuntimeCall(a: i32, b: i32) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/int/left_shift.carbon

@@ -87,6 +87,7 @@ let negative: i32 = LeftShift(1, Negate(1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -203,6 +204,7 @@ let negative: i32 = LeftShift(1, Negate(1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/less.carbon

@@ -64,6 +64,7 @@ fn RuntimeCall(a: i32, b: i32) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/less_eq.carbon

@@ -64,6 +64,7 @@ fn RuntimeCall(a: i32, b: i32) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/int/make_type_32.carbon

@@ -36,6 +36,7 @@ var i: Int() = 0;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -71,6 +72,7 @@ var i: Int() = 0;
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 5 - 0
toolchain/check/testdata/builtins/int/make_type_signed.carbon

@@ -86,6 +86,7 @@ var m: Int(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -143,6 +144,7 @@ var m: Int(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -262,6 +264,7 @@ var m: Int(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -309,6 +312,7 @@ var m: Int(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -371,6 +375,7 @@ var m: Int(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 5 - 0
toolchain/check/testdata/builtins/int/make_type_unsigned.carbon

@@ -86,6 +86,7 @@ var m: UInt(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -143,6 +144,7 @@ var m: UInt(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -262,6 +264,7 @@ var m: UInt(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -309,6 +312,7 @@ var m: UInt(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -371,6 +375,7 @@ var m: UInt(1000000000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/neq.carbon

@@ -56,6 +56,7 @@ fn RuntimeCall(a: i32, b: i32) -> bool {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/or.carbon

@@ -43,6 +43,7 @@ fn RuntimeCall(a: i32, b: i32) -> i32 {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/right_shift.carbon

@@ -88,6 +88,7 @@ let negative: i32 = RightShift(1, Negate(1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -207,6 +208,7 @@ let negative: i32 = RightShift(1, Negate(1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -344,6 +346,7 @@ let negative: i32 = RightShift(1, Negate(1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/sadd.carbon

@@ -113,6 +113,7 @@ let b: i32 = Add(0x7FFFFFFF, 1);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -237,6 +238,7 @@ let b: i32 = Add(0x7FFFFFFF, 1);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -473,6 +475,7 @@ let b: i32 = Add(0x7FFFFFFF, 1);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/sdiv.carbon

@@ -81,6 +81,7 @@ let b: i32 = Div(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -196,6 +197,7 @@ let b: i32 = Div(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -351,6 +353,7 @@ let b: i32 = Div(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/smod.carbon

@@ -84,6 +84,7 @@ let b: i32 = Mod(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -200,6 +201,7 @@ let b: i32 = Mod(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -355,6 +357,7 @@ let b: i32 = Mod(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/int/smul.carbon

@@ -55,6 +55,7 @@ let b: i32 = Mul(0x8000, 0x10000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -166,6 +167,7 @@ let b: i32 = Mul(0x8000, 0x10000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/snegate.carbon

@@ -141,6 +141,7 @@ let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -271,6 +272,7 @@ let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -485,6 +487,7 @@ let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/int/ssub.carbon

@@ -56,6 +56,7 @@ let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -168,6 +169,7 @@ let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/uadd.carbon

@@ -110,6 +110,7 @@ let b: i32 = Add(0x7FFFFFFF, 1);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -234,6 +235,7 @@ let b: i32 = Add(0x7FFFFFFF, 1);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -470,6 +472,7 @@ let b: i32 = Add(0x7FFFFFFF, 1);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/udiv.carbon

@@ -77,6 +77,7 @@ let b: i32 = Div(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -193,6 +194,7 @@ let b: i32 = Div(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -348,6 +350,7 @@ let b: i32 = Div(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/umod.carbon

@@ -79,6 +79,7 @@ let b: i32 = Mod(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -195,6 +196,7 @@ let b: i32 = Mod(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -350,6 +352,7 @@ let b: i32 = Mod(0, 0);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/int/umul.carbon

@@ -52,6 +52,7 @@ let b: i32 = Mul(0x8000, 0x10000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -163,6 +164,7 @@ let b: i32 = Mul(0x8000, 0x10000);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 3 - 0
toolchain/check/testdata/builtins/int/unegate.carbon

@@ -137,6 +137,7 @@ let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -267,6 +268,7 @@ let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -481,6 +483,7 @@ let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1));
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 0
toolchain/check/testdata/builtins/int/usub.carbon

@@ -53,6 +53,7 @@ let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -165,6 +166,7 @@ let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2);
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/builtins/int/xor.carbon

@@ -43,6 +43,7 @@ fn RuntimeCall(a: i32, b: i32) -> i32 {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 2 - 1
toolchain/check/testdata/builtins/print.carbon

@@ -40,12 +40,13 @@ fn Main() {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
-// CHECK:STDOUT:   %import_ref.2: %Print.type.2 = import_ref Core//prelude, inst+43, loaded [template = constants.%Print.2]
+// CHECK:STDOUT:   %import_ref.2: %Print.type.2 = import_ref Core//prelude, inst+45, loaded [template = constants.%Print.2]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {

+ 2 - 0
toolchain/check/testdata/class/adapt.carbon

@@ -66,6 +66,7 @@ fn F(a: AdaptNotExtend) {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool
@@ -146,6 +147,7 @@ fn F(a: AdaptNotExtend) {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/class/base.carbon

@@ -64,6 +64,7 @@ fn Access(d: Derived) -> (i32, i32) {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

+ 1 - 0
toolchain/check/testdata/class/base_field.carbon

@@ -55,6 +55,7 @@ fn Access(p: Derived*) -> i32* {
 // CHECK:STDOUT:     import Core//prelude/operators
 // CHECK:STDOUT:     import Core//prelude/types
 // CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
 // CHECK:STDOUT:     import Core//prelude/operators/bitwise
 // CHECK:STDOUT:     import Core//prelude/operators/comparison
 // CHECK:STDOUT:     import Core//prelude/types/bool

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů