Browse Source

Overloaded operator support. (#3796)

Support is added for all overloaded operator interfaces in the current
design apart from `Assign`, which is going to require some more work to
properly handle, given that primitive assignment currently has a special
implementation for quite a few builtin types.

As we don't have support for generics yet -- in particular, generic
interfaces -- there is no support for `*With` interfaces, but homogenous
interfaces such as `Add` are supported instead.

Factor out building of call expressions so that overloaded operators can
generate calls.

Switch a few places from using specific kinds of NodeId to a general
NodeId. Because overloaded operators and other things like implicit
conversions can result in member access and function calls, those
operations can't require a specific kind of NodeId.

Add import support for associated entities, and fix import support for
interfaces and symbolic bindings. We now import interfaces in two steps,
first importing a forward declaration then a definition, just like we do
for classes. For symbolic bindings, we ensure that each BindSymbolicName
is imported only once, because its ID is used as its symbolic identity.
This is necessary because we (only) support operator interfaces that are
defined in an imported Carbon package for now.

The entire contents of `check/operator.cpp` should probably be
rethought. In particular, doing a lot of name lookups on each operator
is likely to be bad for performance. But this gets us to the point where
overloaded operators are basically working, which seems like a good
place to iterate from.

For now, the tests that the individual operators map to the right
interfaces are mostly generated by a script, but that's just because I'm
expecting a fair bit of churn in how we define the prelude and the
`impl`s -- in particular, when we add support for `AddWith`, we'll need
to update all the tests. The plan is to remove the script once things
settle down.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
Richard Smith 2 năm trước cách đây
mục cha
commit
cf361a83f3
59 tập tin đã thay đổi với 6358 bổ sung511 xóa
  1. 1 1
      .pre-commit-config.yaml
  2. 34 1
      toolchain/check/BUILD
  3. 77 0
      toolchain/check/call.cpp
  4. 20 0
      toolchain/check/call.h
  5. 1 1
      toolchain/check/eval.cpp
  6. 6 66
      toolchain/check/handle_call_expr.cpp
  7. 66 33
      toolchain/check/handle_operator.cpp
  8. 5 1
      toolchain/check/impl.cpp
  9. 192 68
      toolchain/check/import_ref.cpp
  10. 11 13
      toolchain/check/member_access.cpp
  11. 2 3
      toolchain/check/member_access.h
  12. 148 0
      toolchain/check/operator.cpp
  13. 32 0
      toolchain/check/operator.h
  14. 5 0
      toolchain/check/pointer_dereference.cpp
  15. 4 7
      toolchain/check/testdata/array/fail_bound_overflow.carbon
  16. 32 2
      toolchain/check/testdata/index/fail_negative_indexing.carbon
  17. 0 256
      toolchain/check/testdata/interface/fail_todo_import.carbon
  18. 49 15
      toolchain/check/testdata/interface/import.carbon
  19. 2 2
      toolchain/check/testdata/let/fail_generic_import.carbon
  20. 3 3
      toolchain/check/testdata/let/generic_import.carbon
  21. 2 2
      toolchain/check/testdata/let/import.carbon
  22. 0 0
      toolchain/check/testdata/operators/builtin/and.carbon
  23. 0 0
      toolchain/check/testdata/operators/builtin/assignment.carbon
  24. 0 0
      toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon
  25. 0 0
      toolchain/check/testdata/operators/builtin/fail_assignment_to_error.carbon
  26. 0 0
      toolchain/check/testdata/operators/builtin/fail_assignment_to_non_assignable.carbon
  27. 0 0
      toolchain/check/testdata/operators/builtin/fail_redundant_compound_access.carbon
  28. 0 0
      toolchain/check/testdata/operators/builtin/fail_type_mismatch.carbon
  29. 0 0
      toolchain/check/testdata/operators/builtin/fail_type_mismatch_assignment.carbon
  30. 42 0
      toolchain/check/testdata/operators/builtin/fail_type_mismatch_once.carbon
  31. 12 2
      toolchain/check/testdata/operators/builtin/fail_unimplemented_op.carbon
  32. 0 0
      toolchain/check/testdata/operators/builtin/or.carbon
  33. 0 0
      toolchain/check/testdata/operators/builtin/unary_op.carbon
  34. 0 28
      toolchain/check/testdata/operators/fail_type_mismatch_once.carbon
  35. 290 0
      toolchain/check/testdata/operators/overloaded/add.carbon
  36. 43 0
      toolchain/check/testdata/operators/overloaded/binary_op.carbon.tmpl
  37. 290 0
      toolchain/check/testdata/operators/overloaded/bit_and.carbon
  38. 163 0
      toolchain/check/testdata/operators/overloaded/bit_complement.carbon
  39. 290 0
      toolchain/check/testdata/operators/overloaded/bit_or.carbon
  40. 290 0
      toolchain/check/testdata/operators/overloaded/bit_xor.carbon
  41. 158 0
      toolchain/check/testdata/operators/overloaded/dec.carbon
  42. 290 0
      toolchain/check/testdata/operators/overloaded/div.carbon
  43. 494 0
      toolchain/check/testdata/operators/overloaded/eq.carbon
  44. 270 0
      toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon
  45. 328 0
      toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon
  46. 301 0
      toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon
  47. 158 0
      toolchain/check/testdata/operators/overloaded/inc.carbon
  48. 290 0
      toolchain/check/testdata/operators/overloaded/left_shift.carbon
  49. 43 0
      toolchain/check/testdata/operators/overloaded/make_tests.sh
  50. 290 0
      toolchain/check/testdata/operators/overloaded/mod.carbon
  51. 290 0
      toolchain/check/testdata/operators/overloaded/mul.carbon
  52. 163 0
      toolchain/check/testdata/operators/overloaded/negate.carbon
  53. 521 0
      toolchain/check/testdata/operators/overloaded/ordered.carbon
  54. 290 0
      toolchain/check/testdata/operators/overloaded/right_shift.carbon
  55. 290 0
      toolchain/check/testdata/operators/overloaded/sub.carbon
  56. 33 0
      toolchain/check/testdata/operators/overloaded/unary_op.carbon.tmpl
  57. 32 0
      toolchain/check/testdata/operators/overloaded/unary_stmt.carbon.tmpl
  58. 0 3
      toolchain/parse/node_ids.h
  59. 5 4
      toolchain/sem_ir/typed_insts.h

+ 1 - 1
.pre-commit-config.yaml

@@ -180,7 +180,7 @@ repos:
             Exceptions. See /LICENSE for license information.
             SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
           - --custom_format
-          - '\.(carbon|proto|ypp)$'
+          - '\.(carbon|proto|ypp)(\.tmpl)?$'
           - ''
           - '// '
           - ''

+ 34 - 1
toolchain/check/BUILD

@@ -55,7 +55,6 @@ cc_library(
         "decl_name_stack.cpp",
         "eval.cpp",
         "import_ref.cpp",
-        "import_ref.h",
         "inst_block_stack.cpp",
         "modifiers.cpp",
         "return.cpp",
@@ -66,6 +65,7 @@ cc_library(
         "decl_name_stack.h",
         "decl_state.h",
         "eval.h",
+        "import_ref.h",
         "inst_block_stack.h",
         "modifiers.h",
         "param_and_arg_refs_stack.h",
@@ -101,12 +101,14 @@ cc_library(
     ]),
     hdrs = ["check.h"],
     deps = [
+        ":call",
         ":context",
         ":function",
         ":impl",
         ":import",
         ":interface",
         ":member_access",
+        ":operator",
         ":pointer_dereference",
         "//common:check",
         "//common:ostream",
@@ -138,6 +140,20 @@ cc_fuzz_test(
     ],
 )
 
+cc_library(
+    name = "call",
+    srcs = ["call.cpp"],
+    hdrs = ["call.h"],
+    deps = [
+        ":context",
+        "//common:check",
+        "//toolchain/sem_ir:file",
+        "//toolchain/sem_ir:ids",
+        "//toolchain/sem_ir:inst",
+        "//toolchain/sem_ir:inst_kind",
+    ],
+)
+
 cc_library(
     name = "function",
     srcs = ["function.cpp"],
@@ -213,6 +229,23 @@ 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"],

+ 77 - 0
toolchain/check/call.cpp

@@ -0,0 +1,77 @@
+// 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
+
+#include "toolchain/check/call.h"
+
+#include "toolchain/check/context.h"
+#include "toolchain/check/convert.h"
+#include "toolchain/sem_ir/inst.h"
+#include "toolchain/sem_ir/typed_insts.h"
+
+namespace Carbon::Check {
+
+auto PerformCall(Context& context, Parse::NodeId node_id,
+                 SemIR::InstId callee_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
+    -> SemIR::InstId {
+  auto diagnose_not_callable = [&] {
+    auto callee_type_id = context.insts().Get(callee_id).type_id();
+    if (callee_type_id != SemIR::TypeId::Error) {
+      CARBON_DIAGNOSTIC(CallToNonCallable, Error,
+                        "Value of type `{0}` is not callable.", SemIR::TypeId);
+      context.emitter().Emit(node_id, CallToNonCallable, callee_type_id);
+    }
+    return SemIR::InstId::BuiltinError;
+  };
+
+  // For a method call, pick out the `self` value.
+  auto function_callee_id = callee_id;
+  SemIR::InstId self_id = SemIR::InstId::Invalid;
+  if (auto bound_method =
+          context.insts().Get(callee_id).TryAs<SemIR::BoundMethod>()) {
+    self_id = bound_method->object_id;
+    function_callee_id = bound_method->function_id;
+  }
+
+  // Identify the function we're calling.
+  auto function_decl_id = context.constant_values().Get(function_callee_id);
+  if (!function_decl_id.is_constant()) {
+    return diagnose_not_callable();
+  }
+  auto function_decl = context.insts()
+                           .Get(function_decl_id.inst_id())
+                           .TryAs<SemIR::FunctionDecl>();
+  if (!function_decl) {
+    return diagnose_not_callable();
+  }
+  auto function_id = function_decl->function_id;
+  const auto& callable = context.functions().Get(function_id);
+
+  // For functions with an implicit return type, the return type is the empty
+  // tuple type.
+  SemIR::TypeId type_id = callable.return_type_id;
+  if (!type_id.is_valid()) {
+    type_id = context.GetTupleType({});
+  }
+
+  // If there is a return slot, build storage for the result.
+  SemIR::InstId return_storage_id = SemIR::InstId::Invalid;
+  if (callable.return_slot_id.is_valid()) {
+    // 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(
+        {node_id, SemIR::TemporaryStorage{callable.return_type_id}});
+  }
+
+  // Convert the arguments to match the parameters.
+  auto converted_args_id =
+      ConvertCallArgs(context, node_id, self_id, arg_ids, return_storage_id,
+                      function_decl_id.inst_id(),
+                      callable.implicit_param_refs_id, callable.param_refs_id);
+  auto call_inst_id = context.AddInst(
+      {node_id, SemIR::Call{type_id, callee_id, converted_args_id}});
+
+  return call_inst_id;
+}
+
+}  // namespace Carbon::Check

+ 20 - 0
toolchain/check/call.h

@@ -0,0 +1,20 @@
+// 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
+
+#ifndef CARBON_TOOLCHAIN_CHECK_CALL_H_
+#define CARBON_TOOLCHAIN_CHECK_CALL_H_
+
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/ids.h"
+
+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;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_CALL_H_

+ 1 - 1
toolchain/check/eval.cpp

@@ -164,7 +164,7 @@ static auto RebuildAndValidateIfFieldsAreConstant(
   if ((ReplaceFieldWithConstantValue(context, &typed_inst, each_field_id,
                                      &phase) &&
        ...)) {
-    if (!validate_fn(typed_inst)) {
+    if (phase == Phase::UnknownDueToError || !validate_fn(typed_inst)) {
       return SemIR::ConstantId::Error;
     }
     return MakeConstantResult(context, typed_inst, phase);

+ 6 - 66
toolchain/check/handle_call_expr.cpp

@@ -2,9 +2,8 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "llvm/ADT/ScopeExit.h"
+#include "toolchain/check/call.h"
 #include "toolchain/check/context.h"
-#include "toolchain/check/convert.h"
 #include "toolchain/sem_ir/inst.h"
 
 namespace Carbon::Check {
@@ -27,74 +26,15 @@ auto HandleCallExpr(Context& context, Parse::CallExprId node_id) -> bool {
   // Process the final explicit call argument now, but leave the arguments
   // block on the stack until the end of this function.
   context.param_and_arg_refs_stack().EndNoPop(Parse::NodeKind::CallExprStart);
-  auto discard_args_block = llvm::make_scope_exit(
-      [&] { context.param_and_arg_refs_stack().PopAndDiscard(); });
-
   auto [call_expr_node_id, callee_id] =
       context.node_stack().PopWithNodeId<Parse::NodeKind::CallExprStart>();
 
-  auto diagnose_not_callable = [&, call_expr_node_id = call_expr_node_id,
-                                callee_id = callee_id] {
-    auto callee_type_id = context.insts().Get(callee_id).type_id();
-    if (callee_type_id != SemIR::TypeId::Error) {
-      CARBON_DIAGNOSTIC(CallToNonCallable, Error,
-                        "Value of type `{0}` is not callable.", SemIR::TypeId);
-      context.emitter().Emit(call_expr_node_id, CallToNonCallable,
-                             callee_type_id);
-    }
-    context.node_stack().Push(node_id, SemIR::InstId::BuiltinError);
-    return true;
-  };
-
-  // For a method call, pick out the `self` value.
-  auto function_callee_id = callee_id;
-  SemIR::InstId self_id = SemIR::InstId::Invalid;
-  if (auto bound_method =
-          context.insts().Get(callee_id).TryAs<SemIR::BoundMethod>()) {
-    self_id = bound_method->object_id;
-    function_callee_id = bound_method->function_id;
-  }
-
-  // Identify the function we're calling.
-  auto function_decl_id = context.constant_values().Get(function_callee_id);
-  if (!function_decl_id.is_constant()) {
-    return diagnose_not_callable();
-  }
-  auto function_decl = context.insts()
-                           .Get(function_decl_id.inst_id())
-                           .TryAs<SemIR::FunctionDecl>();
-  if (!function_decl) {
-    return diagnose_not_callable();
-  }
-  auto function_id = function_decl->function_id;
-  const auto& callable = context.functions().Get(function_id);
-
-  // For functions with an implicit return type, the return type is the empty
-  // tuple type.
-  SemIR::TypeId type_id = callable.return_type_id;
-  if (!type_id.is_valid()) {
-    type_id = context.GetTupleType({});
-  }
-
-  // If there is a return slot, build storage for the result.
-  SemIR::InstId return_storage_id = SemIR::InstId::Invalid;
-  if (callable.return_slot_id.is_valid()) {
-    // 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(
-        {call_expr_node_id, SemIR::TemporaryStorage{callable.return_type_id}});
-  }
-
-  // Convert the arguments to match the parameters.
-  auto converted_args_id = ConvertCallArgs(
-      context, call_expr_node_id, self_id,
-      context.param_and_arg_refs_stack().PeekCurrentBlockContents(),
-      return_storage_id, function_decl_id.inst_id(),
-      callable.implicit_param_refs_id, callable.param_refs_id);
-  auto call_inst_id = context.AddInst(
-      {call_expr_node_id, SemIR::Call{type_id, callee_id, converted_args_id}});
+  auto call_id = PerformCall(
+      context, call_expr_node_id, callee_id,
+      context.param_and_arg_refs_stack().PeekCurrentBlockContents());
 
-  context.node_stack().Push(node_id, call_inst_id);
+  context.param_and_arg_refs_stack().PopAndDiscard();
+  context.node_stack().Push(node_id, call_id);
   return true;
 }
 

+ 66 - 33
toolchain/check/handle_operator.cpp

@@ -4,20 +4,48 @@
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
+#include "toolchain/check/operator.h"
 #include "toolchain/check/pointer_dereference.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 
 namespace Carbon::Check {
 
+// Common logic for unary operator handlers.
+static auto HandleUnaryOperator(Context& context, Parse::NodeId node_id,
+                                Operator op) -> bool {
+  // TODO: Support implicit conversion from specific node IDs to node ID
+  // categories and change this function to take an `AnyExprId` directly.
+  auto expr_node_id = static_cast<Parse::AnyExprId>(node_id);
+  auto operand_id = context.node_stack().PopExpr();
+  auto result_id = BuildUnaryOperator(context, expr_node_id, op, operand_id);
+  context.node_stack().Push(expr_node_id, result_id);
+  return true;
+}
+
+// Common logic for binary operator handlers.
+static auto HandleBinaryOperator(Context& context, Parse::NodeId node_id,
+                                 Operator op) -> bool {
+  // TODO: Support implicit conversion from specific node IDs to node ID
+  // categories and change this function to take an `AnyExprId` directly.
+  auto expr_node_id = static_cast<Parse::AnyExprId>(node_id);
+  auto rhs_id = context.node_stack().PopExpr();
+  auto lhs_id = context.node_stack().PopExpr();
+  auto result_id =
+      BuildBinaryOperator(context, expr_node_id, op, lhs_id, rhs_id);
+  context.node_stack().Push(expr_node_id, result_id);
+  return true;
+}
+
 auto HandleInfixOperatorAmp(Context& context, Parse::InfixOperatorAmpId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorAmp");
+  // TODO: Facet type intersection may need to be handled directly.
+  return HandleBinaryOperator(context, node_id, {"BitAnd"});
 }
 
 auto HandleInfixOperatorAmpEqual(Context& context,
                                  Parse::InfixOperatorAmpEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorAmpEqual");
+  return HandleBinaryOperator(context, node_id, {"BitAndAssign"});
 }
 
 auto HandleInfixOperatorAs(Context& context, Parse::InfixOperatorAsId node_id)
@@ -33,21 +61,24 @@ auto HandleInfixOperatorAs(Context& context, Parse::InfixOperatorAsId node_id)
 
 auto HandleInfixOperatorCaret(Context& context,
                               Parse::InfixOperatorCaretId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorCaret");
+  return HandleBinaryOperator(context, node_id, {"BitXor"});
 }
 
 auto HandleInfixOperatorCaretEqual(Context& context,
                                    Parse::InfixOperatorCaretEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorCaretEqual");
+  return HandleBinaryOperator(context, node_id, {"BitXorAssign"});
 }
 
 auto HandleInfixOperatorEqual(Context& context,
                               Parse::InfixOperatorEqualId node_id) -> bool {
+  // TODO: Switch to using assignment interface for most assignment. Some cases
+  // may need to be handled directly.
+  //
+  //   return HandleBinaryOperator(context, node_id, {"Assign"});
+
   auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId();
   auto [lhs_node, lhs_id] = context.node_stack().PopExprWithNodeId();
-
-  // TODO: handle complex assignment expression such as `a += 1`.
   if (auto lhs_cat = SemIR::GetExprCategory(context.sem_ir(), lhs_id);
       lhs_cat != SemIR::ExprCategory::DurableRef &&
       lhs_cat != SemIR::ExprCategory::Error) {
@@ -70,128 +101,130 @@ auto HandleInfixOperatorEqual(Context& context,
 auto HandleInfixOperatorEqualEqual(Context& context,
                                    Parse::InfixOperatorEqualEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorEqualEqual");
+  return HandleBinaryOperator(context, node_id, {"Eq", "Equal"});
 }
 
 auto HandleInfixOperatorExclaimEqual(Context& context,
                                      Parse::InfixOperatorExclaimEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorExclaimEqual");
+  return HandleBinaryOperator(context, node_id, {"Eq", "NotEqual"});
 }
 
 auto HandleInfixOperatorGreater(Context& context,
                                 Parse::InfixOperatorGreaterId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorGreater");
+  return HandleBinaryOperator(context, node_id, {"Ordered", "Greater"});
 }
 
 auto HandleInfixOperatorGreaterEqual(Context& context,
                                      Parse::InfixOperatorGreaterEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorGreaterEqual");
+  return HandleBinaryOperator(context, node_id,
+                              {"Ordered", "GreaterOrEquivalent"});
 }
 
 auto HandleInfixOperatorGreaterGreater(
     Context& context, Parse::InfixOperatorGreaterGreaterId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorGreaterGreater");
+  return HandleBinaryOperator(context, node_id, {"RightShift"});
 }
 
 auto HandleInfixOperatorGreaterGreaterEqual(
     Context& context, Parse::InfixOperatorGreaterGreaterEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorGreaterGreaterEqual");
+  return HandleBinaryOperator(context, node_id, {"RightShiftAssign"});
 }
 
 auto HandleInfixOperatorLess(Context& context,
                              Parse::InfixOperatorLessId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorLess");
+  return HandleBinaryOperator(context, node_id, {"Ordered", "Less"});
 }
 
 auto HandleInfixOperatorLessEqual(Context& context,
                                   Parse::InfixOperatorLessEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorLessEqual");
+  return HandleBinaryOperator(context, node_id,
+                              {"Ordered", "LessOrEquivalent"});
 }
 
 auto HandleInfixOperatorLessEqualGreater(
     Context& context, Parse::InfixOperatorLessEqualGreaterId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorLessEqualGreater");
+  return context.TODO(node_id, "remove <=> operator that is not in the design");
 }
 
 auto HandleInfixOperatorLessLess(Context& context,
                                  Parse::InfixOperatorLessLessId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorLessLess");
+  return HandleBinaryOperator(context, node_id, {"LeftShift"});
 }
 
 auto HandleInfixOperatorLessLessEqual(
     Context& context, Parse::InfixOperatorLessLessEqualId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorLessLessEqual");
+  return HandleBinaryOperator(context, node_id, {"LeftShiftAssign"});
 }
 
 auto HandleInfixOperatorMinus(Context& context,
                               Parse::InfixOperatorMinusId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorMinus");
+  return HandleBinaryOperator(context, node_id, {"Sub"});
 }
 
 auto HandleInfixOperatorMinusEqual(Context& context,
                                    Parse::InfixOperatorMinusEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorMinusEqual");
+  return HandleBinaryOperator(context, node_id, {"SubAssign"});
 }
 
 auto HandleInfixOperatorPercent(Context& context,
                                 Parse::InfixOperatorPercentId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorPercent");
+  return HandleBinaryOperator(context, node_id, {"Mod"});
 }
 
 auto HandleInfixOperatorPercentEqual(Context& context,
                                      Parse::InfixOperatorPercentEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorPercentEqual");
+  return HandleBinaryOperator(context, node_id, {"ModAssign"});
 }
 
 auto HandleInfixOperatorPipe(Context& context,
                              Parse::InfixOperatorPipeId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorPipe");
+  return HandleBinaryOperator(context, node_id, {"BitOr"});
 }
 
 auto HandleInfixOperatorPipeEqual(Context& context,
                                   Parse::InfixOperatorPipeEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorPipeEqual");
+  return HandleBinaryOperator(context, node_id, {"BitOrAssign"});
 }
 
 auto HandleInfixOperatorPlus(Context& context,
                              Parse::InfixOperatorPlusId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorPlus");
+  return HandleBinaryOperator(context, node_id, {"Add"});
 }
 
 auto HandleInfixOperatorPlusEqual(Context& context,
                                   Parse::InfixOperatorPlusEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorPlusEqual");
+  return HandleBinaryOperator(context, node_id, {"AddAssign"});
 }
 
 auto HandleInfixOperatorSlash(Context& context,
                               Parse::InfixOperatorSlashId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorSlash");
+  return HandleBinaryOperator(context, node_id, {"Div"});
 }
 
 auto HandleInfixOperatorSlashEqual(Context& context,
                                    Parse::InfixOperatorSlashEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorSlashEqual");
+  return HandleBinaryOperator(context, node_id, {"DivAssign"});
 }
 
 auto HandleInfixOperatorStar(Context& context,
                              Parse::InfixOperatorStarId node_id) -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorStar");
+  return HandleBinaryOperator(context, node_id, {"Mul"});
 }
 
 auto HandleInfixOperatorStarEqual(Context& context,
                                   Parse::InfixOperatorStarEqualId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleInfixOperatorStarEqual");
+  return HandleBinaryOperator(context, node_id, {"MulAssign"});
 }
 
 auto HandlePostfixOperatorStar(Context& context,
@@ -232,7 +265,7 @@ auto HandlePrefixOperatorAmp(Context& context,
 
 auto HandlePrefixOperatorCaret(Context& context,
                                Parse::PrefixOperatorCaretId node_id) -> bool {
-  return context.TODO(node_id, "HandlePrefixOperatorCaret");
+  return HandleUnaryOperator(context, node_id, {"BitComplement"});
 }
 
 auto HandlePrefixOperatorConst(Context& context,
@@ -256,13 +289,13 @@ auto HandlePrefixOperatorConst(Context& context,
 
 auto HandlePrefixOperatorMinus(Context& context,
                                Parse::PrefixOperatorMinusId node_id) -> bool {
-  return context.TODO(node_id, "HandlePrefixOperatorMinus");
+  return HandleUnaryOperator(context, node_id, {"Negate"});
 }
 
 auto HandlePrefixOperatorMinusMinus(Context& context,
                                     Parse::PrefixOperatorMinusMinusId node_id)
     -> bool {
-  return context.TODO(node_id, "HandlePrefixOperatorMinusMinus");
+  return HandleUnaryOperator(context, node_id, {"Dec"});
 }
 
 auto HandlePrefixOperatorNot(Context& context,
@@ -278,7 +311,7 @@ auto HandlePrefixOperatorNot(Context& context,
 auto HandlePrefixOperatorPlusPlus(Context& context,
                                   Parse::PrefixOperatorPlusPlusId node_id)
     -> bool {
-  return context.TODO(node_id, "HandlePrefixOperatorPlusPlus");
+  return HandleUnaryOperator(context, node_id, {"Inc"});
 }
 
 auto HandlePrefixOperatorStar(Context& context,

+ 5 - 1
toolchain/check/impl.cpp

@@ -6,6 +6,7 @@
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/function.h"
+#include "toolchain/check/import_ref.h"
 #include "toolchain/check/subst.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/sem_ir/ids.h"
@@ -86,7 +87,10 @@ static auto BuildInterfaceWitness(
        .replacement_id = context.types().GetConstantId(impl.self_id)}};
 
   for (auto decl_id : assoc_entities) {
-    auto decl = context.insts().Get(decl_id);
+    TryResolveImportRefUnused(context, decl_id);
+    auto const_id = context.constant_values().Get(decl_id);
+    CARBON_CHECK(const_id.is_constant()) << "Non-constant associated entity";
+    auto decl = context.insts().Get(const_id.inst_id());
     if (auto fn_decl = decl.TryAs<SemIR::FunctionDecl>()) {
       auto& fn = context.functions().Get(fn_decl->function_id);
       auto impl_decl_id =

+ 192 - 68
toolchain/check/import_ref.cpp

@@ -23,17 +23,19 @@ namespace Carbon::Check {
 // Work items on work_stack_. At a high level, the loop is:
 //
 // 1. If Work has received a constant, it's considered resolved.
-//    - If made_incomplete_type, resolve unconditionally.
+//    - If made_forward_decl, resolve unconditionally.
 //    - The constant check avoids performance costs of deduplication on add.
 // 2. Resolve the instruction: (TryResolveInst/TryResolveTypedInst)
 //    - For most cases:
-//      A. For types which _can_ be incomplete, when not made_incomplete_type:
-//        i. Start by making an incomplete type to address circular references.
-//        ii. If the imported type is incomplete, return the constant.
-//        iii. Otherwise, set made_incomplete_type and continue resolving.
-//          - Creating an incomplete type will have set the constant, which
-//            influences step (1); setting made_incomplete_type gets us a second
-//            resolve pass when needed.
+//      A. For types which _can_ be forward declared, when not
+//         made_forward_decl:
+//        i. Start by making a forward declared type to address circular
+//           references.
+//        ii. If the imported type is not defined, return the constant.
+//        iii. Otherwise, set made_forward_decl and continue resolving.
+//          - Creating a forward declaration of the type will have set the
+//            constant, which influences step (1); setting made_forward_decl
+//            gets us a second resolve pass when needed.
 //      B. Gather all input constants.
 //        - Gathering constants directly adds unresolved values to work_stack_.
 //      C. If any need to be resolved (HasNewWork), return Invalid; this
@@ -72,11 +74,11 @@ class ImportRefResolver {
       // This should typically be checked before adding it, but a given
       // instruction may be added multiple times before its constant is
       // evaluated.
-      if (!work.made_incomplete_type &&
+      if (!work.made_forward_decl &&
           import_ir_constant_values_.Get(work.inst_id).is_valid()) {
         work_stack_.pop_back();
       } else if (auto new_const_id =
-                     TryResolveInst(work.inst_id, work.made_incomplete_type);
+                     TryResolveInst(work.inst_id, work.made_forward_decl);
                  new_const_id.is_valid()) {
         import_ir_constant_values_.Set(work.inst_id, new_const_id);
         work_stack_.pop_back();
@@ -110,8 +112,8 @@ class ImportRefResolver {
     // The instruction to work on.
     SemIR::InstId inst_id;
 
-    // True if a first pass made an incomplete type.
-    bool made_incomplete_type = false;
+    // True if a first pass made a forward declaration.
+    bool made_forward_decl = false;
   };
 
   // For imported entities, we use an invalid enclosing scope. This will be okay
@@ -161,6 +163,15 @@ class ImportRefResolver {
     for (auto inst_id : param_refs) {
       const_ids.push_back(
           GetLocalConstantId(import_ir_.insts().Get(inst_id).type_id()));
+
+      // If the parameter is a symbolic binding, build the BindSymbolicName
+      // constant.
+      auto bind_id = inst_id;
+      if (auto addr =
+              import_ir_.insts().TryGetAs<SemIR::AddrPattern>(bind_id)) {
+        bind_id = addr->inner_id;
+      }
+      GetLocalConstantId(bind_id);
     }
     return const_ids;
   }
@@ -182,8 +193,10 @@ class ImportRefResolver {
       // TODO: Consider a different parameter handling to simplify import logic.
       auto inst = import_ir_.insts().Get(ref_id);
       auto addr_inst = inst.TryAs<SemIR::AddrPattern>();
+      auto bind_id = ref_id;
       if (addr_inst) {
-        inst = import_ir_.insts().Get(addr_inst->inner_id);
+        bind_id = addr_inst->inner_id;
+        inst = import_ir_.insts().Get(bind_id);
       }
       auto bind_inst = inst.TryAs<SemIR::AnyBindName>();
       if (bind_inst) {
@@ -198,23 +211,32 @@ class ImportRefResolver {
       auto new_param_id = context_.AddInstInNoBlock(
           {Parse::NodeId::Invalid, SemIR::Param{type_id, name_id}});
       if (bind_inst) {
-        auto bind_name_id = context_.bind_names().Add(
-            {.name_id = name_id,
-             .enclosing_scope_id = SemIR::NameScopeId::Invalid});
         switch (bind_inst->kind) {
-          case SemIR::InstKind::BindName:
+          case SemIR::InstKind::BindName: {
+            auto bind_name_id = context_.bind_names().Add(
+                {.name_id = name_id,
+                 .enclosing_scope_id = SemIR::NameScopeId::Invalid});
             new_param_id = context_.AddInstInNoBlock(
                 {Parse::NodeId::Invalid,
                  SemIR::BindName{type_id, bind_name_id, new_param_id}});
             break;
-          case SemIR::InstKind::BindSymbolicName:
-            new_param_id = context_.AddInstInNoBlock(
-                {Parse::NodeId::Invalid,
-                 SemIR::BindSymbolicName{type_id, bind_name_id, new_param_id}});
+          }
+          case SemIR::InstKind::BindSymbolicName: {
+            // The symbolic name will be created on first reference, so might
+            // already exist. Update the value in it to refer to the parameter.
+            auto new_bind_inst =
+                context_.insts().GetAs<SemIR::BindSymbolicName>(
+                    GetLocalConstantId(bind_id).inst_id());
+            new_bind_inst.value_id = new_param_id;
+            // This is not before constant use, but doesn't change the
+            // constant value of the instruction.
+            context_.ReplaceInstBeforeConstantUse(
+                bind_id, {Parse::NodeId::Invalid, new_bind_inst});
             break;
-
-          default:
+          }
+          default: {
             CARBON_FATAL() << "Unexpected kind: " << bind_inst->kind;
+          }
         }
       }
       if (addr_inst) {
@@ -272,23 +294,30 @@ class ImportRefResolver {
   // following TryResolveTypedInst helper functions.
   //
   // TODO: Error is returned when support is missing, but that should go away.
-  auto TryResolveInst(SemIR::InstId inst_id, bool made_incomplete_type)
+  auto TryResolveInst(SemIR::InstId inst_id, bool made_forward_decl)
       -> SemIR::ConstantId {
     if (inst_id.is_builtin()) {
-      CARBON_CHECK(!made_incomplete_type);
+      CARBON_CHECK(!made_forward_decl);
       // Constants for builtins can be directly copied.
       return context_.constant_values().Get(inst_id);
     }
 
     auto inst = import_ir_.insts().Get(inst_id);
 
-    CARBON_CHECK(!made_incomplete_type ||
-                 inst.kind() == SemIR::InstKind::ClassDecl)
-        << "Currently only decls with incomplete types should need "
-           "made_incomplete_type states: "
+    CARBON_CHECK(!made_forward_decl ||
+                 inst.kind() == SemIR::InstKind::ClassDecl ||
+                 inst.kind() == SemIR::InstKind::InterfaceDecl)
+        << "Only types that can be involved in cycles should need "
+           "made_forward_decl state: "
         << inst.kind();
 
     switch (inst.kind()) {
+      case SemIR::InstKind::AssociatedEntity:
+        return TryResolveTypedInst(inst.As<SemIR::AssociatedEntity>());
+
+      case SemIR::InstKind::AssociatedEntityType:
+        return TryResolveTypedInst(inst.As<SemIR::AssociatedEntityType>());
+
       case SemIR::InstKind::BaseDecl:
         return TryResolveTypedInst(inst.As<SemIR::BaseDecl>());
 
@@ -297,7 +326,7 @@ class ImportRefResolver {
 
       case SemIR::InstKind::ClassDecl:
         return TryResolveTypedInst(inst.As<SemIR::ClassDecl>(), inst_id,
-                                   made_incomplete_type);
+                                   made_forward_decl);
 
       case SemIR::InstKind::ClassType:
         return TryResolveTypedInst(inst.As<SemIR::ClassType>());
@@ -312,7 +341,8 @@ class ImportRefResolver {
         return TryResolveTypedInst(inst.As<SemIR::FunctionDecl>());
 
       case SemIR::InstKind::InterfaceDecl:
-        return TryResolveTypedInst(inst.As<SemIR::InterfaceDecl>());
+        return TryResolveTypedInst(inst.As<SemIR::InterfaceDecl>(), inst_id,
+                                   made_forward_decl);
 
       case SemIR::InstKind::InterfaceType:
         return TryResolveTypedInst(inst.As<SemIR::InterfaceType>());
@@ -334,12 +364,7 @@ class ImportRefResolver {
         return TryEvalInst(context_, inst_id, inst);
 
       case SemIR::InstKind::BindSymbolicName:
-        // TODO: This will return a `ConstantId` referring to the `inst_id` from
-        // the import IR. But we need all references to the same
-        // `BindSymbolicName` in the imported IR to resolve to the same constant
-        // value in the local IR, so we can't just create a new local
-        // `BindSymbolicName` here.
-        return TryEvalInst(context_, inst_id, inst);
+        return TryResolveTypedInst(inst.As<SemIR::BindSymbolicName>());
 
       default:
         context_.TODO(
@@ -349,6 +374,46 @@ class ImportRefResolver {
     }
   }
 
+  auto TryResolveTypedInst(SemIR::AssociatedEntity inst) -> SemIR::ConstantId {
+    auto initial_work = work_stack_.size();
+    auto type_const_id = GetLocalConstantId(inst.type_id);
+    if (HasNewWork(initial_work)) {
+      return SemIR::ConstantId::Invalid;
+    }
+
+    // Add a lazy reference to the target declaration.
+    auto decl_id = context_.AddPlaceholderInst(
+        SemIR::ImportRefUnused{import_ir_id_, inst.decl_id});
+
+    auto inst_id = context_.AddInstInNoBlock(
+        {Parse::NodeId::Invalid,
+         SemIR::AssociatedEntity{
+             context_.GetTypeIdForTypeConstant(type_const_id), inst.index,
+             decl_id}});
+    return context_.constant_values().Get(inst_id);
+  }
+
+  auto TryResolveTypedInst(SemIR::AssociatedEntityType inst)
+      -> SemIR::ConstantId {
+    CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+
+    auto initial_work = work_stack_.size();
+    auto entity_type_const_id = GetLocalConstantId(inst.entity_type_id);
+    auto interface_const_id = GetLocalConstantId(
+        import_ir_.interfaces().Get(inst.interface_id).decl_id);
+    if (HasNewWork(initial_work)) {
+      return SemIR::ConstantId::Invalid;
+    }
+
+    auto inst_id = context_.AddInstInNoBlock(SemIR::AssociatedEntityType{
+        SemIR::TypeId::TypeType,
+        context_.insts()
+            .GetAs<SemIR::InterfaceType>(interface_const_id.inst_id())
+            .interface_id,
+        context_.GetTypeIdForTypeConstant(entity_type_const_id)});
+    return context_.constant_values().Get(inst_id);
+  }
+
   auto TryResolveTypedInst(SemIR::BaseDecl inst) -> SemIR::ConstantId {
     auto initial_work = work_stack_.size();
     auto type_const_id = GetLocalConstantId(inst.type_id);
@@ -375,6 +440,25 @@ class ImportRefResolver {
     return value_id;
   }
 
+  auto TryResolveTypedInst(SemIR::BindSymbolicName inst) -> SemIR::ConstantId {
+    auto initial_work = work_stack_.size();
+    auto type_id = GetLocalConstantId(inst.type_id);
+    if (HasNewWork(initial_work)) {
+      return SemIR::ConstantId::Invalid;
+    }
+
+    auto name_id =
+        GetLocalNameId(import_ir_.bind_names().Get(inst.bind_name_id).name_id);
+    auto bind_name_id = context_.bind_names().Add(
+        {.name_id = name_id,
+         .enclosing_scope_id = SemIR::NameScopeId::Invalid});
+    auto new_bind_id = context_.AddInstInNoBlock(
+        {Parse::NodeId::Invalid,
+         SemIR::BindSymbolicName{context_.GetTypeIdForTypeConstant(type_id),
+                                 bind_name_id, SemIR::InstId::Invalid}});
+    return context_.constant_values().Get(new_bind_id);
+  }
+
   // Makes an incomplete class. This is necessary even with classes with a
   // complete declaration, because things such as `Self` may refer back to the
   // type.
@@ -452,13 +536,13 @@ class ImportRefResolver {
   }
 
   auto TryResolveTypedInst(SemIR::ClassDecl inst, SemIR::InstId inst_id,
-                           bool made_incomplete_type) -> SemIR::ConstantId {
+                           bool made_forward_decl) -> SemIR::ConstantId {
     const auto& import_class = import_ir_.classes().Get(inst.class_id);
 
     SemIR::ConstantId class_const_id = SemIR::ConstantId::Invalid;
     // On the first pass, there's no incomplete type; start by adding one for
     // any recursive references.
-    if (!made_incomplete_type) {
+    if (!made_forward_decl) {
       class_const_id = MakeIncompleteClass(inst_id, import_class);
       // If there's only a forward declaration, we're done.
       if (!import_class.is_defined()) {
@@ -467,7 +551,7 @@ class ImportRefResolver {
       // This may not be needed because all constants might be ready, but we do
       // it here so that we don't need to track which work item corresponds to
       // this instruction.
-      work_stack_.back().made_incomplete_type = true;
+      work_stack_.back().made_forward_decl = true;
     }
 
     CARBON_CHECK(import_class.is_defined())
@@ -488,7 +572,7 @@ class ImportRefResolver {
     // On the first pass, we build the incomplete type's constant above. If we
     // get here on a subsequent pass we need to fetch the one we built in the
     // first pass.
-    if (made_incomplete_type) {
+    if (made_forward_decl) {
       CARBON_CHECK(!class_const_id.is_valid())
           << "Shouldn't have a const yet when resuming";
       class_const_id = import_ir_constant_values_.Get(inst_id);
@@ -588,10 +672,11 @@ class ImportRefResolver {
     return context_.constant_values().Get(function_decl_id);
   }
 
-  auto TryResolveTypedInst(SemIR::InterfaceDecl inst) -> SemIR::ConstantId {
-    const auto& import_interface =
-        import_ir_.interfaces().Get(inst.interface_id);
-
+  // Make a declaration of an interface. This is done as a separate step from
+  // importing the interface definition in order to resolve cycles.
+  auto MakeInterfaceDecl(SemIR::InstId inst_id,
+                         const SemIR::Interface& import_interface)
+      -> SemIR::ConstantId {
     auto interface_decl = SemIR::InterfaceDecl{SemIR::TypeId::Invalid,
                                                SemIR::InterfaceId::Invalid,
                                                SemIR::InstBlockId::Empty};
@@ -605,34 +690,73 @@ class ImportRefResolver {
         .decl_id = interface_decl_id,
     };
 
-    // If the interface is defined, we can complete it immediately. No constants
-    // are required. Do this before adding it to interfaces.
-    if (import_interface.is_defined()) {
-      new_interface.scope_id = context_.name_scopes().Add(
-          new_interface.decl_id, SemIR::NameId::Invalid,
-          new_interface.enclosing_scope_id);
-      auto& new_scope = context_.name_scopes().Get(new_interface.scope_id);
-      const auto& import_scope =
-          import_ir_.name_scopes().Get(import_interface.scope_id);
-
-      // Push a block so that we can add scoped instructions to it.
-      context_.inst_block_stack().Push();
-      AddNameScopeImportRefs(import_scope, new_scope);
-      new_interface.associated_entities_id =
-          AddAssociatedEntities(import_interface.associated_entities_id);
-      new_interface.body_block_id = context_.inst_block_stack().Pop();
-      // TODO: Import new_interface.self_param_id.
-
-      CARBON_CHECK(import_scope.extended_scopes.empty())
-          << "Interfaces don't currently have extended scopes to support.";
-    }
-
     // Write the interface ID into the InterfaceDecl.
     interface_decl.interface_id = context_.interfaces().Add(new_interface);
     context_.ReplaceInstBeforeConstantUse(
         interface_decl_id, {Parse::NodeId::Invalid, interface_decl});
 
-    return context_.constant_values().Get(interface_decl_id);
+    // Set the constant value for the imported interface.
+    auto interface_const_id = context_.constant_values().Get(interface_decl_id);
+    import_ir_constant_values_.Set(inst_id, interface_const_id);
+    return interface_const_id;
+  }
+
+  // Imports the definition for an interface that has been imported as a forward
+  // declaration.
+  auto AddInterfaceDefinition(const SemIR::Interface& import_interface,
+                              SemIR::ConstantId interface_const_id,
+                              SemIR::ConstantId self_param_id) -> void {
+    auto& new_interface = context_.interfaces().Get(
+        context_.insts()
+            .GetAs<SemIR::InterfaceType>(interface_const_id.inst_id())
+            .interface_id);
+    new_interface.scope_id = context_.name_scopes().Add(
+        new_interface.decl_id, SemIR::NameId::Invalid,
+        new_interface.enclosing_scope_id);
+    auto& new_scope = context_.name_scopes().Get(new_interface.scope_id);
+    const auto& import_scope =
+        import_ir_.name_scopes().Get(import_interface.scope_id);
+
+    // Push a block so that we can add scoped instructions to it.
+    context_.inst_block_stack().Push();
+    AddNameScopeImportRefs(import_scope, new_scope);
+    new_interface.associated_entities_id =
+        AddAssociatedEntities(import_interface.associated_entities_id);
+    new_interface.body_block_id = context_.inst_block_stack().Pop();
+    new_interface.self_param_id = self_param_id.inst_id();
+
+    CARBON_CHECK(import_scope.extended_scopes.empty())
+        << "Interfaces don't currently have extended scopes to support.";
+  }
+
+  auto TryResolveTypedInst(SemIR::InterfaceDecl inst, SemIR::InstId inst_id,
+                           bool made_forward_decl) -> SemIR::ConstantId {
+    const auto& import_interface =
+        import_ir_.interfaces().Get(inst.interface_id);
+
+    // On the first pass, create a forward declaration of the interface.
+    if (!made_forward_decl) {
+      auto interface_const_id = MakeInterfaceDecl(inst_id, import_interface);
+      if (!import_interface.is_defined()) {
+        return interface_const_id;
+      }
+      // Track that we need another pass. We always will, because the type of
+      // the `Self` binding refers to the interface.
+      work_stack_.back().made_forward_decl = true;
+    }
+
+    auto initial_work = work_stack_.size();
+    auto self_param_id = GetLocalConstantId(import_interface.self_param_id);
+    if (HasNewWork(initial_work)) {
+      return SemIR::ConstantId::Invalid;
+    }
+
+    // Add the interface definition.
+    CARBON_CHECK(import_interface.is_defined())
+        << "Should not need second pass for undefined interface.";
+    auto interface_const_id = import_ir_constant_values_.Get(inst_id);
+    AddInterfaceDefinition(import_interface, interface_const_id, self_param_id);
+    return interface_const_id;
   }
 
   auto TryResolveTypedInst(SemIR::InterfaceType inst) -> SemIR::ConstantId {

+ 11 - 13
toolchain/check/member_access.cpp

@@ -149,7 +149,8 @@ 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, SemIR::ConstantId type_const_id,
+static auto PerformImplLookup(Context& context, Parse::NodeId node_id,
+                              SemIR::ConstantId type_const_id,
                               SemIR::AssociatedEntityType assoc_type,
                               SemIR::InstId member_id) -> SemIR::InstId {
   auto& interface = context.interfaces().Get(assoc_type.interface_id);
@@ -161,7 +162,7 @@ static auto PerformImplLookup(Context& context, SemIR::ConstantId type_const_id,
                       "that does not implement that interface.",
                       SemIR::NameId, std::string);
     context.emitter().Emit(
-        member_id, MissingImplInMemberAccess, interface.name_id,
+        node_id, MissingImplInMemberAccess, interface.name_id,
         context.sem_ir().StringifyTypeExpr(type_const_id.inst_id()));
     return SemIR::InstId::BuiltinError;
   }
@@ -194,8 +195,7 @@ static auto PerformImplLookup(Context& context, SemIR::ConstantId type_const_id,
 // 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::AnyMemberAccessExprId node_id,
+static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id,
                                     SemIR::InstId /*base_id*/,
                                     SemIR::NameId name_id,
                                     SemIR::ConstantId name_scope_const_id,
@@ -219,8 +219,8 @@ static auto LookupMemberNameInScope(Context& context,
   if (auto assoc_type = context.types().TryGetAs<SemIR::AssociatedEntityType>(
           inst.type_id())) {
     if (ScopeNeedsImplLookup(context, name_scope_id)) {
-      member_id = PerformImplLookup(context, name_scope_const_id, *assoc_type,
-                                    member_id);
+      member_id = PerformImplLookup(context, node_id, name_scope_const_id,
+                                    *assoc_type, member_id);
     }
   }
 
@@ -230,8 +230,7 @@ static auto LookupMemberNameInScope(Context& context,
 // 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::AnyMemberAccessExprId node_id,
+static auto PerformInstanceBinding(Context& context, Parse::NodeId node_id,
                                    SemIR::InstId base_id,
                                    SemIR::InstId member_id) -> SemIR::InstId {
   auto member_type_id = context.insts().Get(member_id).type_id();
@@ -295,7 +294,7 @@ static auto PerformInstanceBinding(Context& context,
   return member_id;
 }
 
-auto PerformMemberAccess(Context& context, Parse::AnyMemberAccessExprId node_id,
+auto PerformMemberAccess(Context& context, Parse::NodeId node_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
@@ -370,8 +369,7 @@ auto PerformMemberAccess(Context& context, Parse::AnyMemberAccessExprId node_id,
   return member_id;
 }
 
-auto PerformCompoundMemberAccess(Context& context,
-                                 Parse::AnyMemberAccessExprId node_id,
+auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
                                  SemIR::InstId base_id,
                                  SemIR::InstId member_expr_id)
     -> SemIR::InstId {
@@ -387,8 +385,8 @@ auto PerformCompoundMemberAccess(Context& context,
   // performed using the type of the base expression.
   if (auto assoc_type = context.types().TryGetAs<SemIR::AssociatedEntityType>(
           member.type_id())) {
-    member_id =
-        PerformImplLookup(context, base_type_const_id, *assoc_type, member_id);
+    member_id = PerformImplLookup(context, node_id, base_type_const_id,
+                                  *assoc_type, member_id);
   }
 
   // Perform instance binding if we found an instance member.

+ 2 - 3
toolchain/check/member_access.h

@@ -12,15 +12,14 @@ 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::AnyMemberAccessExprId node_id,
+auto PerformMemberAccess(Context& context, Parse::NodeId node_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::AnyMemberAccessExprId node_id,
+auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
                                  SemIR::InstId base_id,
                                  SemIR::InstId member_expr_id) -> SemIR::InstId;
 

+ 148 - 0
toolchain/check/operator.cpp

@@ -0,0 +1,148 @@
+// 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
+
+#include "toolchain/check/operator.h"
+
+#include "toolchain/check/call.h"
+#include "toolchain/check/context.h"
+#include "toolchain/check/member_access.h"
+#include "toolchain/sem_ir/ids.h"
+#include "toolchain/sem_ir/typed_insts.h"
+
+namespace Carbon::Check {
+
+// Returns the scope of the Core package, or Invalid if it's not found.
+//
+// TODO: Consider tracking the Core package in SemIR so we don't need to use
+// name lookup to find it.
+static auto GetCorePackage(Context& context, Parse::AnyExprId node_id)
+    -> SemIR::NameScopeId {
+  // TODO: If the current package is the `Core` package, return
+  // `SemIR::InstId::Package`.
+
+  auto ident_id = context.identifiers().Lookup("Core");
+  if (!ident_id.is_valid()) {
+    return SemIR::NameScopeId::Invalid;
+  }
+  auto name_id = SemIR::NameId::ForIdentifier(ident_id);
+
+  // Look up `package.Core`.
+  auto package_id = context.LookupQualifiedName(
+      node_id, name_id, SemIR::NameScopeId::Package, /*required=*/false);
+  if (!package_id.is_valid()) {
+    return SemIR::NameScopeId::Invalid;
+  }
+
+  // Look through import_refs and aliases.
+  package_id = context.constant_values().Get(package_id).inst_id();
+
+  // We expect it to be a package, and fail if not.
+  if (auto package_inst =
+          context.insts().TryGetAs<SemIR::Namespace>(package_id)) {
+    auto& name_scope = context.name_scopes().Get(package_inst->name_scope_id);
+    // Check that this is really the `Core` package and not an alias.
+    if (name_scope.is_closed_import && name_scope.name_id == name_id &&
+        name_scope.enclosing_scope_id == SemIR::NameScopeId::Package) {
+      return package_inst->name_scope_id;
+    }
+  }
+  return SemIR::NameScopeId::Invalid;
+}
+
+// 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 carbon_package_id = GetCorePackage(context, node_id);
+  if (!carbon_package_id.is_valid()) {
+    return SemIR::NameScopeId::Invalid;
+  }
+
+  // Lookup `Core.InterfaceName`.
+  auto interface_ident_id = context.identifiers().Add(op.interface_name);
+  auto interface_id = context.LookupQualifiedName(
+      node_id, SemIR::NameId::ForIdentifier(interface_ident_id),
+      carbon_package_id, /*required=*/false);
+  if (!interface_id.is_valid()) {
+    return SemIR::NameScopeId::Invalid;
+  }
+
+  // Look through import_refs and aliases.
+  interface_id = context.constant_values().Get(interface_id).inst_id();
+
+  // 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,
+                                  Operator op) -> SemIR::InstId {
+  auto interface_scope_id = GetOperatorInterface(context, node_id, op);
+  if (!interface_scope_id.is_valid()) {
+    return SemIR::InstId::Invalid;
+  }
+
+  // Lookup `Interface.Op`.
+  auto op_ident_id = context.identifiers().Add(op.op_name);
+  auto op_id = context.LookupQualifiedName(
+      node_id, SemIR::NameId::ForIdentifier(op_ident_id), interface_scope_id,
+      /*required=*/false);
+  if (!op_id.is_valid()) {
+    return SemIR::InstId::Invalid;
+  }
+
+  // Look through import_refs and aliases.
+  op_id = context.constant_values().Get(op_id).inst_id();
+
+  // We expect it to be an associated function.
+  if (context.insts().Is<SemIR::AssociatedEntity>(op_id)) {
+    return op_id;
+  }
+  return SemIR::InstId::Invalid;
+}
+
+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");
+    return SemIR::InstId::BuiltinError;
+  }
+
+  // Form `operand.(Op)`.
+  auto bound_op_id =
+      PerformCompoundMemberAccess(context, node_id, operand_id, op_fn);
+  if (bound_op_id == SemIR::InstId::BuiltinError) {
+    return SemIR::InstId::BuiltinError;
+  }
+
+  // Form `bound_op()`.
+  return PerformCall(context, node_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;
+  }
+
+  // Form `lhs.(Op)`.
+  auto bound_op_id =
+      PerformCompoundMemberAccess(context, node_id, lhs_id, op_fn);
+  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});
+}
+
+}  // namespace Carbon::Check

+ 32 - 0
toolchain/check/operator.h

@@ -0,0 +1,32 @@
+// 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
+
+#ifndef CARBON_TOOLCHAIN_CHECK_OPERATOR_H_
+#define CARBON_TOOLCHAIN_CHECK_OPERATOR_H_
+
+#include "toolchain/check/context.h"
+#include "toolchain/parse/node_ids.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+struct Operator {
+  llvm::StringLiteral interface_name;
+  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;
+
+// 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;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_OPERATOR_H_

+ 5 - 0
toolchain/check/pointer_dereference.cpp

@@ -15,6 +15,11 @@ auto PerformPointerDereference(
     SemIR::InstId base_id,
     llvm::function_ref<auto(SemIR::TypeId not_pointer_type_id)->void>
         diagnose_not_pointer) -> SemIR::InstId {
+  // TODO: Once we have a finalized design for a pointer interface, use
+  //
+  //   HandleUnaryOperator(context, node_id, {"Pointer", "Dereference"});
+  //
+  // to convert to a pointer value.
   base_id = ConvertToValueExpr(context, base_id);
   auto type_id =
       context.GetUnqualifiedType(context.insts().Get(base_id).type_id());

+ 4 - 7
toolchain/check/testdata/array/fail_bound_overflow.carbon

@@ -9,12 +9,9 @@
 // CHECK:STDERR:              ^~~~~~~~~~~~~~~~~~~~
 var a: [i32; 39999999999999999993];
 
-// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+6]]:9: ERROR: Cannot implicitly convert from `i32` to `type`.
+// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+3]]:9: ERROR: Cannot implicitly convert from `i32` to `type`.
 // CHECK:STDERR: var b: [1; 39999999999999999993];
 // CHECK:STDERR:         ^
-// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+3]]:12: ERROR: Array bound of 39999999999999999993 is too large.
-// CHECK:STDERR: var b: [1; 39999999999999999993];
-// CHECK:STDERR:            ^~~~~~~~~~~~~~~~~~~~
 var b: [1; 39999999999999999993];
 
 // CHECK:STDOUT: --- fail_bound_overflow.carbon
@@ -33,9 +30,9 @@ var b: [1; 39999999999999999993];
 // CHECK:STDOUT:   %.loc10_34: type = array_type %.loc10_14, i32 [template = <error>]
 // CHECK:STDOUT:   %a.var: ref <error> = var a
 // CHECK:STDOUT:   %a: ref <error> = bind_name a, %a.var
-// CHECK:STDOUT:   %.loc18_9: i32 = int_literal 1 [template = constants.%.2]
-// CHECK:STDOUT:   %.loc18_12: i32 = int_literal 39999999999999999993 [template = constants.%.1]
-// CHECK:STDOUT:   %.loc18_32: type = array_type %.loc18_12, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc15_9: i32 = int_literal 1 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc15_12: i32 = int_literal 39999999999999999993 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc15_32: type = array_type %.loc15_12, <error> [template = <error>]
 // CHECK:STDOUT:   %b.var: ref <error> = var b
 // CHECK:STDOUT:   %b: ref <error> = bind_name b, %b.var
 // CHECK:STDOUT: }

+ 32 - 2
toolchain/check/testdata/index/fail_negative_indexing.carbon

@@ -5,7 +5,7 @@
 // AUTOUPDATE
 
 var a: (i32, i32) = (12, 6);
-// CHECK:STDERR: fail_negative_indexing.carbon:[[@LINE+3]]:16: ERROR: Semantics TODO: `HandlePrefixOperatorMinus`.
+// CHECK:STDERR: fail_negative_indexing.carbon:[[@LINE+3]]:16: ERROR: Semantics TODO: `missing or invalid operator interface`.
 // CHECK:STDERR: var b: i32 = a[-10];
 // CHECK:STDERR:                ^~~
 var b: i32 = a[-10];
@@ -22,5 +22,35 @@ var b: i32 = a[-10];
 // CHECK:STDOUT:   %.7: i32 = int_literal 10 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .a = %a
+// CHECK:STDOUT:     .b = %b
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc7_17.1: (type, type) = tuple_literal (i32, i32)
+// CHECK:STDOUT:   %.loc7_17.2: type = converted %.loc7_17.1, constants.%.2 [template = constants.%.2]
+// CHECK:STDOUT:   %a.var: ref (i32, i32) = var a
+// CHECK:STDOUT:   %a: ref (i32, i32) = bind_name a, %a.var
+// CHECK:STDOUT:   %b.var: ref i32 = var b
+// CHECK:STDOUT:   %b: ref i32 = bind_name b, %b.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc7_22: i32 = int_literal 12 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc7_26: i32 = int_literal 6 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc7_27.1: (i32, i32) = tuple_literal (%.loc7_22, %.loc7_26)
+// CHECK:STDOUT:   %.loc7_27.2: ref i32 = tuple_access file.%a.var, element0
+// CHECK:STDOUT:   %.loc7_27.3: init i32 = initialize_from %.loc7_22 to %.loc7_27.2 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc7_27.4: ref i32 = tuple_access file.%a.var, element1
+// CHECK:STDOUT:   %.loc7_27.5: init i32 = initialize_from %.loc7_26 to %.loc7_27.4 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc7_27.6: init (i32, i32) = tuple_init (%.loc7_27.3, %.loc7_27.5) to file.%a.var [template = constants.%.6]
+// CHECK:STDOUT:   %.loc7_27.7: init (i32, i32) = converted %.loc7_27.1, %.loc7_27.6 [template = constants.%.6]
+// CHECK:STDOUT:   assign file.%a.var, %.loc7_27.7
+// CHECK:STDOUT:   %a.ref: ref (i32, i32) = name_ref a, file.%a
+// CHECK:STDOUT:   %.loc11_17: i32 = int_literal 10 [template = constants.%.7]
+// CHECK:STDOUT:   %.loc11_19: ref <error> = tuple_index %a.ref, <error>
+// CHECK:STDOUT:   assign file.%b.var, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 256
toolchain/check/testdata/interface/fail_todo_import.carbon

@@ -1,256 +0,0 @@
-// 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
-// CHECK:STDERR: fail_b.carbon: ERROR: Semantics TODO: `TryResolveInst on AssociatedEntityType`.
-// CHECK:STDERR: fail_b.carbon: ERROR: Semantics TODO: `TryResolveInst on AssociatedEntity`.
-// CHECK:STDERR: fail_b.carbon: ERROR: Semantics TODO: `TryResolveInst on AssociatedEntityType`.
-// CHECK:STDERR: fail_b.carbon: ERROR: Semantics TODO: `TryResolveInst on AssociatedEntity`.
-// CHECK:STDERR: fail_b.carbon: ERROR: Semantics TODO: `TryResolveInst on AssociatedEntityType`.
-// CHECK:STDERR: fail_b.carbon: ERROR: Semantics TODO: `TryResolveInst on AssociatedEntity`.
-// CHECK:STDERR: fail_b.carbon: ERROR: Semantics TODO: `TryResolveInst on AssociatedEntityType`.
-// CHECK:STDERR: fail_b.carbon: ERROR: Semantics TODO: `TryResolveInst on AssociatedEntity`.
-
-// --- a.carbon
-
-library "a" api;
-
-interface Empty {
-}
-
-interface Basic {
-  let T:! type;
-  fn F();
-}
-
-interface ForwardDeclared;
-
-interface ForwardDeclared {
-  let T:! type;
-  fn F();
-}
-
-var f_ref: {.f: ForwardDeclared};
-
-// --- fail_b.carbon
-
-library "b" api;
-
-import library "a";
-
-fn UseEmpty(e: Empty) {}
-fn UseBasic(e: Basic) {}
-fn UseForwardDeclared(f: ForwardDeclared) {}
-
-alias UseBasicT = Basic.T;
-alias UseBasicF = Basic.F;
-
-alias UseForwardDeclaredT = ForwardDeclared.T;
-alias UseForwardDeclaredF = ForwardDeclared.F;
-
-var f: ForwardDeclared* = &f_ref.f;
-
-// CHECK:STDOUT: --- a.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %.1: type = interface_type @Empty [template]
-// CHECK:STDOUT:   %.2: type = interface_type @Basic [template]
-// CHECK:STDOUT:   %.3: type = assoc_entity_type @Basic, type [template]
-// CHECK:STDOUT:   %.4: <associated type in Basic> = assoc_entity element0, @Basic.%T [template]
-// CHECK:STDOUT:   %.5: type = assoc_entity_type @Basic, <function> [template]
-// CHECK:STDOUT:   %.6: <associated <function> in Basic> = assoc_entity element1, @Basic.%F [template]
-// CHECK:STDOUT:   %.7: type = interface_type @ForwardDeclared [template]
-// CHECK:STDOUT:   %.8: type = assoc_entity_type @ForwardDeclared, type [template]
-// CHECK:STDOUT:   %.9: <associated type in ForwardDeclared> = assoc_entity element0, @ForwardDeclared.%T [template]
-// CHECK:STDOUT:   %.10: type = assoc_entity_type @ForwardDeclared, <function> [template]
-// CHECK:STDOUT:   %.11: <associated <function> in ForwardDeclared> = assoc_entity element1, @ForwardDeclared.%F [template]
-// CHECK:STDOUT:   %.12: type = struct_type {.f: ForwardDeclared} [template]
-// CHECK:STDOUT:   %.13: type = tuple_type () [template]
-// CHECK:STDOUT:   %.14: type = struct_type {.f: ()} [template]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Empty = %Empty.decl
-// CHECK:STDOUT:     .Basic = %Basic.decl
-// CHECK:STDOUT:     .ForwardDeclared = %ForwardDeclared.decl.loc12
-// CHECK:STDOUT:     .f_ref = %f_ref
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Empty.decl: type = interface_decl @Empty [template = constants.%.1] {}
-// CHECK:STDOUT:   %Basic.decl: type = interface_decl @Basic [template = constants.%.2] {}
-// CHECK:STDOUT:   %ForwardDeclared.decl.loc12: type = interface_decl @ForwardDeclared [template = constants.%.7] {}
-// CHECK:STDOUT:   %ForwardDeclared.decl.loc14: type = interface_decl @ForwardDeclared [template = constants.%.7] {}
-// CHECK:STDOUT:   %ForwardDeclared.ref: type = name_ref ForwardDeclared, %ForwardDeclared.decl.loc12 [template = constants.%.7]
-// CHECK:STDOUT:   %.loc19: type = struct_type {.f: ForwardDeclared} [template = constants.%.12]
-// CHECK:STDOUT:   %f_ref.var: ref {.f: ForwardDeclared} = var f_ref
-// CHECK:STDOUT:   %f_ref: ref {.f: ForwardDeclared} = bind_name f_ref, %f_ref.var
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: interface @Empty {
-// CHECK:STDOUT:   %Self: Empty = bind_symbolic_name Self [symbolic]
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = %Self
-// CHECK:STDOUT:   witness = ()
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: interface @Basic {
-// CHECK:STDOUT:   %Self: Basic = bind_symbolic_name Self [symbolic]
-// CHECK:STDOUT:   %T: type = assoc_const_decl T [template]
-// CHECK:STDOUT:   %.loc8: <associated type in Basic> = assoc_entity element0, %T [template = constants.%.4]
-// CHECK:STDOUT:   %F: <function> = fn_decl @F.1 [template] {}
-// CHECK:STDOUT:   %.loc9: <associated <function> in Basic> = assoc_entity element1, %F [template = constants.%.6]
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = %Self
-// CHECK:STDOUT:   .T = %.loc8
-// CHECK:STDOUT:   .F = %.loc9
-// CHECK:STDOUT:   witness = (%T, %F)
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: interface @ForwardDeclared {
-// CHECK:STDOUT:   %Self: ForwardDeclared = bind_symbolic_name Self [symbolic]
-// CHECK:STDOUT:   %T: type = assoc_const_decl T [template]
-// CHECK:STDOUT:   %.loc15: <associated type in ForwardDeclared> = assoc_entity element0, %T [template = constants.%.9]
-// CHECK:STDOUT:   %F: <function> = fn_decl @F.2 [template] {}
-// CHECK:STDOUT:   %.loc16: <associated <function> in ForwardDeclared> = assoc_entity element1, %F [template = constants.%.11]
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = %Self
-// CHECK:STDOUT:   .T = %.loc15
-// CHECK:STDOUT:   .F = %.loc16
-// CHECK:STDOUT:   witness = (%T, %F)
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @F.1();
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @F.2();
-// CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_b.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %.1: type = interface_type @Empty [template]
-// CHECK:STDOUT:   %.2: type = tuple_type () [template]
-// CHECK:STDOUT:   %.3: type = interface_type @Basic [template]
-// CHECK:STDOUT:   %.4: type = interface_type @ForwardDeclared [template]
-// CHECK:STDOUT:   %.5: type = ptr_type ForwardDeclared [template]
-// CHECK:STDOUT:   %.6: type = struct_type {.f: ForwardDeclared} [template]
-// CHECK:STDOUT:   %.7: type = struct_type {.f: ()} [template]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Empty = %import_ref.1
-// CHECK:STDOUT:     .Basic = %import_ref.2
-// CHECK:STDOUT:     .ForwardDeclared = %import_ref.3
-// CHECK:STDOUT:     .f_ref = %import_ref.4
-// CHECK:STDOUT:     .UseEmpty = %UseEmpty
-// CHECK:STDOUT:     .UseBasic = %UseBasic
-// CHECK:STDOUT:     .UseForwardDeclared = %UseForwardDeclared
-// CHECK:STDOUT:     .UseBasicT = %UseBasicT
-// CHECK:STDOUT:     .UseBasicF = %UseBasicF
-// CHECK:STDOUT:     .UseForwardDeclaredT = %UseForwardDeclaredT
-// CHECK:STDOUT:     .UseForwardDeclaredF = %UseForwardDeclaredF
-// CHECK:STDOUT:     .f = %f.loc16
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.1]
-// CHECK:STDOUT:   %import_ref.2: type = import_ref ir1, inst+4, used [template = constants.%.3]
-// CHECK:STDOUT:   %import_ref.3: type = import_ref ir1, inst+15, used [template = constants.%.4]
-// CHECK:STDOUT:   %import_ref.4: ref {.f: ForwardDeclared} = import_ref ir1, inst+36, used
-// CHECK:STDOUT:   %UseEmpty: <function> = fn_decl @UseEmpty [template] {
-// CHECK:STDOUT:     %Empty.decl: invalid = interface_decl @Empty [template = constants.%.1] {}
-// CHECK:STDOUT:     %Empty.ref: type = name_ref Empty, %import_ref.1 [template = constants.%.1]
-// CHECK:STDOUT:     %e.loc6_13.1: Empty = param e
-// CHECK:STDOUT:     @UseEmpty.%e: Empty = bind_name e, %e.loc6_13.1
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %UseBasic: <function> = fn_decl @UseBasic [template] {
-// CHECK:STDOUT:     %Basic.decl: invalid = interface_decl @Basic [template = constants.%.3] {}
-// CHECK:STDOUT:     %Basic.ref.loc7: type = name_ref Basic, %import_ref.2 [template = constants.%.3]
-// CHECK:STDOUT:     %e.loc7_13.1: Basic = param e
-// CHECK:STDOUT:     @UseBasic.%e: Basic = bind_name e, %e.loc7_13.1
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %UseForwardDeclared: <function> = fn_decl @UseForwardDeclared [template] {
-// CHECK:STDOUT:     %ForwardDeclared.decl: invalid = interface_decl @ForwardDeclared [template = constants.%.4] {}
-// CHECK:STDOUT:     %ForwardDeclared.ref.loc8: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
-// CHECK:STDOUT:     %f.loc8_23.1: ForwardDeclared = param f
-// CHECK:STDOUT:     @UseForwardDeclared.%f: ForwardDeclared = bind_name f, %f.loc8_23.1
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Basic.ref.loc10: type = name_ref Basic, %import_ref.2 [template = constants.%.3]
-// CHECK:STDOUT:   %T.ref.loc10: <error> = name_ref T, @Basic.%import_ref.3 [template = <error>]
-// CHECK:STDOUT:   %UseBasicT: <error> = bind_alias UseBasicT, @Basic.%import_ref.3 [template = <error>]
-// CHECK:STDOUT:   %Basic.ref.loc11: type = name_ref Basic, %import_ref.2 [template = constants.%.3]
-// CHECK:STDOUT:   %F.ref.loc11: <error> = name_ref F, @Basic.%import_ref.2 [template = <error>]
-// CHECK:STDOUT:   %UseBasicF: <error> = bind_alias UseBasicF, @Basic.%import_ref.2 [template = <error>]
-// CHECK:STDOUT:   %ForwardDeclared.ref.loc13: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
-// CHECK:STDOUT:   %T.ref.loc13: <error> = name_ref T, @ForwardDeclared.%import_ref.3 [template = <error>]
-// CHECK:STDOUT:   %UseForwardDeclaredT: <error> = bind_alias UseForwardDeclaredT, @ForwardDeclared.%import_ref.3 [template = <error>]
-// CHECK:STDOUT:   %ForwardDeclared.ref.loc14: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
-// CHECK:STDOUT:   %F.ref.loc14: <error> = name_ref F, @ForwardDeclared.%import_ref.2 [template = <error>]
-// CHECK:STDOUT:   %UseForwardDeclaredF: <error> = bind_alias UseForwardDeclaredF, @ForwardDeclared.%import_ref.2 [template = <error>]
-// CHECK:STDOUT:   %ForwardDeclared.ref.loc16: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
-// CHECK:STDOUT:   %.loc16: type = ptr_type ForwardDeclared [template = constants.%.5]
-// CHECK:STDOUT:   %f.var: ref ForwardDeclared* = var f
-// CHECK:STDOUT:   %f.loc16: ref ForwardDeclared* = bind_name f, %f.var
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: interface @Empty {
-// CHECK:STDOUT:   %import_ref = import_ref ir1, inst+3, unused
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = %import_ref
-// CHECK:STDOUT:   witness = ()
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: interface @Basic {
-// CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+6, unused
-// CHECK:STDOUT:   %import_ref.2: <error> = import_ref ir1, inst+13, used [template = <error>]
-// CHECK:STDOUT:   %import_ref.3: <error> = import_ref ir1, inst+9, used [template = <error>]
-// CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+7, unused
-// CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+11, unused
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = %import_ref.1
-// CHECK:STDOUT:   .F = %import_ref.2
-// CHECK:STDOUT:   .T = %import_ref.3
-// CHECK:STDOUT:   witness = (%import_ref.4, %import_ref.5)
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: interface @ForwardDeclared {
-// CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+18, unused
-// CHECK:STDOUT:   %import_ref.2: <error> = import_ref ir1, inst+25, used [template = <error>]
-// CHECK:STDOUT:   %import_ref.3: <error> = import_ref ir1, inst+21, used [template = <error>]
-// CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+19, unused
-// CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+23, unused
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = %import_ref.1
-// CHECK:STDOUT:   .F = %import_ref.2
-// CHECK:STDOUT:   .T = %import_ref.3
-// CHECK:STDOUT:   witness = (%import_ref.4, %import_ref.5)
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @UseEmpty(%e: Empty) {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @UseBasic(%e: Basic) {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @UseForwardDeclared(%f: ForwardDeclared) {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @__global_init() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %f_ref.ref: ref {.f: ForwardDeclared} = name_ref f_ref, file.%import_ref.4
-// CHECK:STDOUT:   %.loc16_33: ref ForwardDeclared = struct_access %f_ref.ref, element0
-// CHECK:STDOUT:   %.loc16_27: ForwardDeclared* = addr_of %.loc16_33
-// CHECK:STDOUT:   assign file.%f.var, %.loc16_27
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:

+ 49 - 15
toolchain/check/testdata/interface/import.carbon

@@ -35,6 +35,12 @@ fn UseEmpty(e: Empty) {}
 fn UseBasic(e: Basic) {}
 fn UseForwardDeclared(f: ForwardDeclared) {}
 
+alias UseBasicT = Basic.T;
+alias UseBasicF = Basic.F;
+
+alias UseForwardDeclaredT = ForwardDeclared.T;
+alias UseForwardDeclaredF = ForwardDeclared.F;
+
 var f: ForwardDeclared* = &f_ref.f;
 
 // CHECK:STDOUT: --- a.carbon
@@ -120,9 +126,17 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %.3: type = interface_type @Basic [template]
 // CHECK:STDOUT:   %.4: type = interface_type @ForwardDeclared [template]
-// CHECK:STDOUT:   %.5: type = ptr_type ForwardDeclared [template]
-// CHECK:STDOUT:   %.6: type = struct_type {.f: ForwardDeclared} [template]
-// CHECK:STDOUT:   %.7: type = struct_type {.f: ()} [template]
+// CHECK:STDOUT:   %.5: type = assoc_entity_type @Basic, type [template]
+// CHECK:STDOUT:   %.6: <associated type in Basic> = assoc_entity element0, file.%import_ref.5 [template]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type @Basic, <function> [template]
+// CHECK:STDOUT:   %.8: <associated <function> in Basic> = assoc_entity element1, file.%import_ref.6 [template]
+// CHECK:STDOUT:   %.9: type = assoc_entity_type @ForwardDeclared, type [template]
+// CHECK:STDOUT:   %.10: <associated type in ForwardDeclared> = assoc_entity element0, file.%import_ref.7 [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @ForwardDeclared, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in ForwardDeclared> = assoc_entity element1, file.%import_ref.8 [template]
+// CHECK:STDOUT:   %.13: type = ptr_type ForwardDeclared [template]
+// CHECK:STDOUT:   %.14: type = struct_type {.f: ForwardDeclared} [template]
+// CHECK:STDOUT:   %.15: type = struct_type {.f: ()} [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -134,7 +148,11 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:     .UseEmpty = %UseEmpty
 // CHECK:STDOUT:     .UseBasic = %UseBasic
 // CHECK:STDOUT:     .UseForwardDeclared = %UseForwardDeclared
-// CHECK:STDOUT:     .f = %f.loc10
+// CHECK:STDOUT:     .UseBasicT = %UseBasicT
+// CHECK:STDOUT:     .UseBasicF = %UseBasicF
+// CHECK:STDOUT:     .UseForwardDeclaredT = %UseForwardDeclaredT
+// CHECK:STDOUT:     .UseForwardDeclaredF = %UseForwardDeclaredF
+// CHECK:STDOUT:     .f = %f.loc16
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.1]
 // CHECK:STDOUT:   %import_ref.2: type = import_ref ir1, inst+4, used [template = constants.%.3]
@@ -148,7 +166,7 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %UseBasic: <function> = fn_decl @UseBasic [template] {
 // CHECK:STDOUT:     %Basic.decl: invalid = interface_decl @Basic [template = constants.%.3] {}
-// CHECK:STDOUT:     %Basic.ref: type = name_ref Basic, %import_ref.2 [template = constants.%.3]
+// CHECK:STDOUT:     %Basic.ref.loc7: type = name_ref Basic, %import_ref.2 [template = constants.%.3]
 // CHECK:STDOUT:     %e.loc7_13.1: Basic = param e
 // CHECK:STDOUT:     @UseBasic.%e: Basic = bind_name e, %e.loc7_13.1
 // CHECK:STDOUT:   }
@@ -158,10 +176,26 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:     %f.loc8_23.1: ForwardDeclared = param f
 // CHECK:STDOUT:     @UseForwardDeclared.%f: ForwardDeclared = bind_name f, %f.loc8_23.1
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %ForwardDeclared.ref.loc10: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
-// CHECK:STDOUT:   %.loc10: type = ptr_type ForwardDeclared [template = constants.%.5]
+// CHECK:STDOUT:   %Basic.ref.loc10: type = name_ref Basic, %import_ref.2 [template = constants.%.3]
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+7, unused
+// CHECK:STDOUT:   %T.ref.loc10: <associated type in Basic> = name_ref T, @Basic.%import_ref.3 [template = constants.%.6]
+// CHECK:STDOUT:   %UseBasicT: <associated type in Basic> = bind_alias UseBasicT, @Basic.%import_ref.3 [template = constants.%.6]
+// CHECK:STDOUT:   %Basic.ref.loc11: type = name_ref Basic, %import_ref.2 [template = constants.%.3]
+// CHECK:STDOUT:   %import_ref.6 = import_ref ir1, inst+11, unused
+// CHECK:STDOUT:   %F.ref.loc11: <associated <function> in Basic> = name_ref F, @Basic.%import_ref.2 [template = constants.%.8]
+// CHECK:STDOUT:   %UseBasicF: <associated <function> in Basic> = bind_alias UseBasicF, @Basic.%import_ref.2 [template = constants.%.8]
+// CHECK:STDOUT:   %ForwardDeclared.ref.loc13: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
+// CHECK:STDOUT:   %import_ref.7 = import_ref ir1, inst+19, unused
+// CHECK:STDOUT:   %T.ref.loc13: <associated type in ForwardDeclared> = name_ref T, @ForwardDeclared.%import_ref.3 [template = constants.%.10]
+// CHECK:STDOUT:   %UseForwardDeclaredT: <associated type in ForwardDeclared> = bind_alias UseForwardDeclaredT, @ForwardDeclared.%import_ref.3 [template = constants.%.10]
+// CHECK:STDOUT:   %ForwardDeclared.ref.loc14: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
+// CHECK:STDOUT:   %import_ref.8 = import_ref ir1, inst+23, unused
+// CHECK:STDOUT:   %F.ref.loc14: <associated <function> in ForwardDeclared> = name_ref F, @ForwardDeclared.%import_ref.2 [template = constants.%.12]
+// CHECK:STDOUT:   %UseForwardDeclaredF: <associated <function> in ForwardDeclared> = bind_alias UseForwardDeclaredF, @ForwardDeclared.%import_ref.2 [template = constants.%.12]
+// CHECK:STDOUT:   %ForwardDeclared.ref.loc16: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc16: type = ptr_type ForwardDeclared [template = constants.%.13]
 // CHECK:STDOUT:   %f.var: ref ForwardDeclared* = var f
-// CHECK:STDOUT:   %f.loc10: ref ForwardDeclared* = bind_name f, %f.var
+// CHECK:STDOUT:   %f.loc16: ref ForwardDeclared* = bind_name f, %f.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @Empty {
@@ -174,8 +208,8 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @Basic {
 // CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+6, unused
-// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+13, unused
-// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+9, unused
+// CHECK:STDOUT:   %import_ref.2: <associated <function> in Basic> = import_ref ir1, inst+13, used [template = constants.%.8]
+// CHECK:STDOUT:   %import_ref.3: <associated type in Basic> = import_ref ir1, inst+9, used [template = constants.%.6]
 // CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+7, unused
 // CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+11, unused
 // CHECK:STDOUT:
@@ -188,8 +222,8 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @ForwardDeclared {
 // CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+18, unused
-// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+25, unused
-// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+21, unused
+// CHECK:STDOUT:   %import_ref.2: <associated <function> in ForwardDeclared> = import_ref ir1, inst+25, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.3: <associated type in ForwardDeclared> = import_ref ir1, inst+21, used [template = constants.%.10]
 // CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+19, unused
 // CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+23, unused
 // CHECK:STDOUT:
@@ -218,9 +252,9 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %f_ref.ref: ref {.f: ForwardDeclared} = name_ref f_ref, file.%import_ref.4
-// CHECK:STDOUT:   %.loc10_33: ref ForwardDeclared = struct_access %f_ref.ref, element0
-// CHECK:STDOUT:   %.loc10_27: ForwardDeclared* = addr_of %.loc10_33
-// CHECK:STDOUT:   assign file.%f.var, %.loc10_27
+// CHECK:STDOUT:   %.loc16_33: ref ForwardDeclared = struct_access %f_ref.ref, element0
+// CHECK:STDOUT:   %.loc16_27: ForwardDeclared* = addr_of %.loc16_33
+// CHECK:STDOUT:   assign file.%f.var, %.loc16_27
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/let/fail_generic_import.carbon

@@ -37,8 +37,8 @@ let a: T = 0;
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .T = %import_ref
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: type = import_ref ir1, inst+1, used [symbolic]
-// CHECK:STDOUT:   %T.ref: type = name_ref T, %import_ref [symbolic = %import_ref]
+// CHECK:STDOUT:   %import_ref: type = import_ref ir1, inst+1, used [symbolic = <unexpected instref inst+2>]
+// CHECK:STDOUT:   %T.ref: type = name_ref T, %import_ref [symbolic = <unexpected instref inst+2>]
 // CHECK:STDOUT:   %.loc8: i32 = int_literal 0 [template = constants.%.1]
 // CHECK:STDOUT:   %a: T = bind_name a, <error>
 // CHECK:STDOUT: }

+ 3 - 3
toolchain/check/testdata/let/generic_import.carbon

@@ -36,12 +36,12 @@ var b: T = *a;
 // CHECK:STDOUT:     .a = %a
 // CHECK:STDOUT:     .b = %b
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: type = import_ref ir1, inst+1, used [symbolic]
-// CHECK:STDOUT:   %T.ref.loc4: type = name_ref T, %import_ref [symbolic = %import_ref]
+// CHECK:STDOUT:   %import_ref: type = import_ref ir1, inst+1, used [symbolic = <unexpected instref inst+2>]
+// CHECK:STDOUT:   %T.ref.loc4: type = name_ref T, %import_ref [symbolic = <unexpected instref inst+2>]
 // CHECK:STDOUT:   %.loc4: type = ptr_type T [symbolic = constants.%.1]
 // CHECK:STDOUT:   %a.var: ref T* = var a
 // CHECK:STDOUT:   %a: ref T* = bind_name a, %a.var
-// CHECK:STDOUT:   %T.ref.loc5: type = name_ref T, %import_ref [symbolic = %import_ref]
+// CHECK:STDOUT:   %T.ref.loc5: type = name_ref T, %import_ref [symbolic = <unexpected instref inst+2>]
 // CHECK:STDOUT:   %b.var: ref T = var b
 // CHECK:STDOUT:   %b: ref T = bind_name b, %b.var
 // CHECK:STDOUT: }

+ 2 - 2
toolchain/check/testdata/let/import.carbon

@@ -34,8 +34,8 @@ let b:! bool = a;
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .a = %import_ref
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: bool = import_ref ir1, inst+1, used [symbolic]
-// CHECK:STDOUT:   %a.ref: bool = name_ref a, %import_ref [symbolic = %import_ref]
+// CHECK:STDOUT:   %import_ref: bool = import_ref ir1, inst+1, used [symbolic = <unexpected instref inst+3>]
+// CHECK:STDOUT:   %a.ref: bool = name_ref a, %import_ref [symbolic = <unexpected instref inst+3>]
 // CHECK:STDOUT:   %b: bool = bind_symbolic_name b, %a.ref [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 0
toolchain/check/testdata/operators/and.carbon → toolchain/check/testdata/operators/builtin/and.carbon


+ 0 - 0
toolchain/check/testdata/operators/assignment.carbon → toolchain/check/testdata/operators/builtin/assignment.carbon


+ 0 - 0
toolchain/check/testdata/operators/fail_and_or_not_in_function.carbon → toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon


+ 0 - 0
toolchain/check/testdata/operators/fail_assignment_to_error.carbon → toolchain/check/testdata/operators/builtin/fail_assignment_to_error.carbon


+ 0 - 0
toolchain/check/testdata/operators/fail_assignment_to_non_assignable.carbon → toolchain/check/testdata/operators/builtin/fail_assignment_to_non_assignable.carbon


+ 0 - 0
toolchain/check/testdata/operators/fail_redundant_compound_access.carbon → toolchain/check/testdata/operators/builtin/fail_redundant_compound_access.carbon


+ 0 - 0
toolchain/check/testdata/operators/fail_type_mismatch.carbon → toolchain/check/testdata/operators/builtin/fail_type_mismatch.carbon


+ 0 - 0
toolchain/check/testdata/operators/fail_type_mismatch_assignment.carbon → toolchain/check/testdata/operators/builtin/fail_type_mismatch_assignment.carbon


+ 42 - 0
toolchain/check/testdata/operators/builtin/fail_type_mismatch_once.carbon

@@ -0,0 +1,42 @@
+// 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
+
+fn Main() -> i32 {
+  // The following line has two mismatches, but after the first, it shouldn't
+  // keep erroring.
+  // CHECK:STDERR: fail_type_mismatch_once.carbon:[[@LINE+6]]:10: ERROR: Semantics TODO: `missing or invalid operator interface`.
+  // CHECK:STDERR:   return 12 + 3.4 + 12;
+  // CHECK:STDERR:          ^~~~~~~~
+  // CHECK:STDERR: fail_type_mismatch_once.carbon:[[@LINE+3]]:10: ERROR: Semantics TODO: `missing or invalid operator interface`.
+  // CHECK:STDERR:   return 12 + 3.4 + 12;
+  // CHECK:STDERR:          ^~~~~~~~~~~~~
+  return 12 + 3.4 + 12;
+}
+
+// CHECK:STDOUT: --- fail_type_mismatch_once.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: i32 = int_literal 12 [template]
+// CHECK:STDOUT:   %.2: f64 = real_literal 34e-1 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Main = %Main
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Main: <function> = fn_decl @Main [template] {
+// CHECK:STDOUT:     %return.var: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Main() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc16_10: i32 = int_literal 12 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc16_15: f64 = real_literal 34e-1 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc16_21: i32 = int_literal 12 [template = constants.%.1]
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 12 - 2
toolchain/check/testdata/operators/fail_unimplemented_op.carbon → toolchain/check/testdata/operators/builtin/fail_unimplemented_op.carbon

@@ -5,7 +5,7 @@
 // AUTOUPDATE
 
 fn Main() -> i32 {
-  // CHECK:STDERR: fail_unimplemented_op.carbon:[[@LINE+3]]:10: ERROR: Semantics TODO: `HandleInfixOperatorPlus`.
+  // CHECK:STDERR: fail_unimplemented_op.carbon:[[@LINE+3]]:10: ERROR: Semantics TODO: `missing or invalid operator interface`.
   // CHECK:STDERR:   return 12 + 34;
   // CHECK:STDERR:          ^~~~~~~
   return 12 + 34;
@@ -18,9 +18,19 @@ fn Main() -> i32 {
 // CHECK:STDOUT:   %.2: i32 = int_literal 34 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Main = %Main
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Main: <function> = fn_decl @Main [template] {
+// CHECK:STDOUT:     %return.var: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Main() -> i32 {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc11_10: i32 = int_literal 12 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc11_15: i32 = int_literal 34 [template = constants.%.2]
+// CHECK:STDOUT:   return <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 0
toolchain/check/testdata/operators/or.carbon → toolchain/check/testdata/operators/builtin/or.carbon


+ 0 - 0
toolchain/check/testdata/operators/unary_op.carbon → toolchain/check/testdata/operators/builtin/unary_op.carbon


+ 0 - 28
toolchain/check/testdata/operators/fail_type_mismatch_once.carbon

@@ -1,28 +0,0 @@
-// 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
-
-fn Main() -> i32 {
-  // The following line has two mismatches, but after the first, it shouldn't
-  // keep erroring.
-  // CHECK:STDERR: fail_type_mismatch_once.carbon:[[@LINE+3]]:10: ERROR: Semantics TODO: `HandleInfixOperatorPlus`.
-  // CHECK:STDERR:   return 12 + 3.4 + 12;
-  // CHECK:STDERR:          ^~~~~~~~
-  return 12 + 3.4 + 12;
-}
-
-// CHECK:STDOUT: --- fail_type_mismatch_once.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %.1: i32 = int_literal 12 [template]
-// CHECK:STDOUT:   %.2: f64 = real_literal 34e-1 [template]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {}
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @Main() -> i32 {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT: }
-// CHECK:STDOUT:

+ 290 - 0
toolchain/check/testdata/operators/overloaded/add.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Add {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface AddAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Add {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.AddAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a + b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a += b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Add [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Add, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Add> = assoc_entity element0, @Add.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @AddAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @AddAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in AddAssign> = assoc_entity element0, @AddAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Add = %Add.decl
+// CHECK:STDOUT:     .AddAssign = %AddAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Add.decl: type = interface_decl @Add [template = constants.%.1] {}
+// CHECK:STDOUT:   %AddAssign.decl: type = interface_decl @AddAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Add {
+// CHECK:STDOUT:   %Self: Add = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in Add> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddAssign {
+// CHECK:STDOUT:   %Self: AddAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: AddAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: AddAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in AddAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@Add.%self.loc5_9.2: Self](@Add.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @AddAssign.%self.loc8_14.3: Self*](@AddAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Add [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @AddAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @Add, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in Add> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @AddAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in AddAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Add.decl: invalid = interface_decl @Add [template = constants.%.2] {}
+// CHECK:STDOUT:     %Add.ref: type = name_ref Add, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %AddAssign.decl: invalid = interface_decl @AddAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %AddAssign.ref: type = name_ref AddAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Add {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Add> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in AddAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as Add {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as AddAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 43 - 0
toolchain/check/testdata/operators/overloaded/binary_op.carbon.tmpl

@@ -0,0 +1,43 @@
+// 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
+
+HEADER
+
+// --- prelude.carbon
+
+package Core api;
+
+interface INTERFACE {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface INTERFACEAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.INTERFACE {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.INTERFACEAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a OP b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a OP= b;
+}

+ 290 - 0
toolchain/check/testdata/operators/overloaded/bit_and.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface BitAnd {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface BitAndAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.BitAnd {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.BitAndAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a & b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a &= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @BitAnd [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @BitAnd, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in BitAnd> = assoc_entity element0, @BitAnd.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @BitAndAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @BitAndAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in BitAndAssign> = assoc_entity element0, @BitAndAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .BitAnd = %BitAnd.decl
+// CHECK:STDOUT:     .BitAndAssign = %BitAndAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %BitAnd.decl: type = interface_decl @BitAnd [template = constants.%.1] {}
+// CHECK:STDOUT:   %BitAndAssign.decl: type = interface_decl @BitAndAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitAnd {
+// CHECK:STDOUT:   %Self: BitAnd = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: BitAnd = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: BitAnd = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: BitAnd = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in BitAnd> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitAndAssign {
+// CHECK:STDOUT:   %Self: BitAndAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: BitAndAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: BitAndAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in BitAndAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@BitAnd.%self.loc5_9.2: Self](@BitAnd.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @BitAndAssign.%self.loc8_14.3: Self*](@BitAndAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @BitAnd [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @BitAndAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @BitAnd, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in BitAnd> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @BitAndAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in BitAndAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %BitAnd.decl: invalid = interface_decl @BitAnd [template = constants.%.2] {}
+// CHECK:STDOUT:     %BitAnd.ref: type = name_ref BitAnd, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %BitAndAssign.decl: invalid = interface_decl @BitAndAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %BitAndAssign.ref: type = name_ref BitAndAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitAnd {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in BitAnd> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitAndAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in BitAndAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as BitAnd {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as BitAndAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 163 - 0
toolchain/check/testdata/operators/overloaded/bit_complement.carbon

@@ -0,0 +1,163 @@
+// 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
+
+// This file was generated from unary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface BitComplement {
+  fn Op[self: Self]() -> Self;
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.BitComplement {
+  fn Op[self: C]() -> C {
+    return {};
+  }
+}
+
+fn TestOp(a: C) -> C {
+  return ^a;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @BitComplement [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @BitComplement, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in BitComplement> = assoc_entity element0, @BitComplement.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .BitComplement = %BitComplement.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %BitComplement.decl: type = interface_decl @BitComplement [template = constants.%.1] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitComplement {
+// CHECK:STDOUT:   %Self: BitComplement = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: BitComplement = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_26: BitComplement = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_26.1: type = facet_type_access %Self.ref.loc5_26 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_26.2: type = converted %Self.ref.loc5_26, %.loc5_26.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_30: <associated <function> in BitComplement> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_30
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op[@BitComplement.%self.loc5_9.2: Self]() -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @BitComplement [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.%Op) [template]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type @BitComplement, <function> [template]
+// CHECK:STDOUT:   %.8: <associated <function> in BitComplement> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %BitComplement.decl: invalid = interface_decl @BitComplement [template = constants.%.2] {}
+// CHECK:STDOUT:     %BitComplement.ref: type = name_ref BitComplement, %import_ref [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc14_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc14_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc14_11.1
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitComplement {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in BitComplement> = import_ref ir1, inst+15, used [template = constants.%.8]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+13, used [template = imports.%Op]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: C as BitComplement {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_23: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.%self.loc9_9.2: C]() -> @impl.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self]() -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+13, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%Op]
+// CHECK:STDOUT:   %.loc15_10.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc14: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc15_10.2: init C = call %.loc15_10.1(%a.ref) to %.loc14
+// CHECK:STDOUT:   return %.loc15_10.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 290 - 0
toolchain/check/testdata/operators/overloaded/bit_or.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface BitOr {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface BitOrAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.BitOr {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.BitOrAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a | b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a |= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @BitOr [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @BitOr, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in BitOr> = assoc_entity element0, @BitOr.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @BitOrAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @BitOrAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in BitOrAssign> = assoc_entity element0, @BitOrAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .BitOr = %BitOr.decl
+// CHECK:STDOUT:     .BitOrAssign = %BitOrAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %BitOr.decl: type = interface_decl @BitOr [template = constants.%.1] {}
+// CHECK:STDOUT:   %BitOrAssign.decl: type = interface_decl @BitOrAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitOr {
+// CHECK:STDOUT:   %Self: BitOr = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: BitOr = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: BitOr = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: BitOr = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in BitOr> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitOrAssign {
+// CHECK:STDOUT:   %Self: BitOrAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: BitOrAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: BitOrAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in BitOrAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@BitOr.%self.loc5_9.2: Self](@BitOr.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @BitOrAssign.%self.loc8_14.3: Self*](@BitOrAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @BitOr [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @BitOrAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @BitOr, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in BitOr> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @BitOrAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in BitOrAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %BitOr.decl: invalid = interface_decl @BitOr [template = constants.%.2] {}
+// CHECK:STDOUT:     %BitOr.ref: type = name_ref BitOr, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %BitOrAssign.decl: invalid = interface_decl @BitOrAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %BitOrAssign.ref: type = name_ref BitOrAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitOr {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in BitOr> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitOrAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in BitOrAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as BitOr {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as BitOrAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 290 - 0
toolchain/check/testdata/operators/overloaded/bit_xor.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface BitXor {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface BitXorAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.BitXor {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.BitXorAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a ^ b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a ^= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @BitXor [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @BitXor, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in BitXor> = assoc_entity element0, @BitXor.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @BitXorAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @BitXorAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in BitXorAssign> = assoc_entity element0, @BitXorAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .BitXor = %BitXor.decl
+// CHECK:STDOUT:     .BitXorAssign = %BitXorAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %BitXor.decl: type = interface_decl @BitXor [template = constants.%.1] {}
+// CHECK:STDOUT:   %BitXorAssign.decl: type = interface_decl @BitXorAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitXor {
+// CHECK:STDOUT:   %Self: BitXor = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: BitXor = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: BitXor = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: BitXor = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in BitXor> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitXorAssign {
+// CHECK:STDOUT:   %Self: BitXorAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: BitXorAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: BitXorAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in BitXorAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@BitXor.%self.loc5_9.2: Self](@BitXor.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @BitXorAssign.%self.loc8_14.3: Self*](@BitXorAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @BitXor [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @BitXorAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @BitXor, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in BitXor> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @BitXorAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in BitXorAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %BitXor.decl: invalid = interface_decl @BitXor [template = constants.%.2] {}
+// CHECK:STDOUT:     %BitXor.ref: type = name_ref BitXor, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %BitXorAssign.decl: invalid = interface_decl @BitXorAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %BitXorAssign.ref: type = name_ref BitXorAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitXor {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in BitXor> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @BitXorAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in BitXorAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as BitXor {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as BitXorAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 158 - 0
toolchain/check/testdata/operators/overloaded/dec.carbon

@@ -0,0 +1,158 @@
+// 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
+
+// This file was generated from unary_stmt.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Dec {
+  fn Op[addr self: Self*]();
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Dec {
+  fn Op[addr self: C*]();
+}
+
+fn TestOp() {
+  var c: C = {};
+  --c;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Dec [template]
+// CHECK:STDOUT:   %.2: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.3: type = assoc_entity_type @Dec, <function> [template]
+// CHECK:STDOUT:   %.4: <associated <function> in Dec> = assoc_entity element0, @Dec.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Dec = %Dec.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Dec.decl: type = interface_decl @Dec [template = constants.%.1] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Dec {
+// CHECK:STDOUT:   %Self: Dec = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op [template] {
+// CHECK:STDOUT:     %Self.ref: Dec = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_24.1: type = facet_type_access %Self.ref [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_20: type = converted %Self.ref, %.loc5_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_24.2: type = ptr_type Self [symbolic = constants.%.2]
+// CHECK:STDOUT:     %self.loc5_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc5_14.3: Self* = bind_name self, %self.loc5_14.1
+// CHECK:STDOUT:     %.loc5_9: Self* = addr_pattern %self.loc5_14.3
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_28: <associated <function> in Dec> = assoc_entity element0, %Op [template = constants.%.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_28
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op[addr @Dec.%self.loc5_14.3: Self*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Dec [template]
+// CHECK:STDOUT:   %.3: type = ptr_type C [template]
+// CHECK:STDOUT:   %.4: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.5: <witness> = interface_witness (@impl.%Op) [template]
+// CHECK:STDOUT:   %.6: type = tuple_type () [template]
+// CHECK:STDOUT:   %.7: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.8: C = struct_value () [template]
+// CHECK:STDOUT:   %.9: type = assoc_entity_type @Dec, <function> [template]
+// CHECK:STDOUT:   %.10: <associated <function> in Dec> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl {
+// CHECK:STDOUT:     %C.ref: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Dec.decl: invalid = interface_decl @Dec [template = constants.%.2] {}
+// CHECK:STDOUT:     %Dec.ref: type = name_ref Dec, %import_ref [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Dec {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Dec> = import_ref ir1, inst+14, used [template = constants.%.10]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+12, used [template = imports.%Op]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: C as Dec {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc9_21: type = ptr_type C [template = constants.%.3]
+// CHECK:STDOUT:     %self.loc9_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc9_14.3: C* = bind_name self, %self.loc9_14.1
+// CHECK:STDOUT:     %.loc9_9: C* = addr_pattern %self.loc9_14.3
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[addr @impl.%self.loc9_14.3: C*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr %self: Self*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:   %c.var: ref C = var c
+// CHECK:STDOUT:   %c: ref C = bind_name c, %c.var
+// CHECK:STDOUT:   %.loc13_15.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc13_15.2: init C = class_init (), %c.var [template = constants.%.8]
+// CHECK:STDOUT:   %.loc13_15.3: init C = converted %.loc13_15.1, %.loc13_15.2 [template = constants.%.8]
+// CHECK:STDOUT:   assign %c.var, %.loc13_15.3
+// CHECK:STDOUT:   %c.ref: ref C = name_ref c, %c
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+12, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%Op]
+// CHECK:STDOUT:   %.loc14_3.1: <bound method> = bound_method %c.ref, %.1
+// CHECK:STDOUT:   %.loc14_5: C* = addr_of %c.ref
+// CHECK:STDOUT:   %.loc14_3.2: init () = call %.loc14_3.1(%.loc14_5)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 290 - 0
toolchain/check/testdata/operators/overloaded/div.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Div {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface DivAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Div {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.DivAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a / b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a /= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Div [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Div, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Div> = assoc_entity element0, @Div.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @DivAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @DivAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in DivAssign> = assoc_entity element0, @DivAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Div = %Div.decl
+// CHECK:STDOUT:     .DivAssign = %DivAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Div.decl: type = interface_decl @Div [template = constants.%.1] {}
+// CHECK:STDOUT:   %DivAssign.decl: type = interface_decl @DivAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Div {
+// CHECK:STDOUT:   %Self: Div = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: Div = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: Div = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: Div = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in Div> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @DivAssign {
+// CHECK:STDOUT:   %Self: DivAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: DivAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: DivAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in DivAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@Div.%self.loc5_9.2: Self](@Div.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @DivAssign.%self.loc8_14.3: Self*](@DivAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Div [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @DivAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @Div, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in Div> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @DivAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in DivAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Div.decl: invalid = interface_decl @Div [template = constants.%.2] {}
+// CHECK:STDOUT:     %Div.ref: type = name_ref Div, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %DivAssign.decl: invalid = interface_decl @DivAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %DivAssign.ref: type = name_ref DivAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Div {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Div> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @DivAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in DivAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as Div {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as DivAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 494 - 0
toolchain/check/testdata/operators/overloaded/eq.carbon

@@ -0,0 +1,494 @@
+// 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
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Eq {
+  fn Equal[self: Self](other: Self) -> bool;
+  fn NotEqual[self: Self](other: Self) -> bool;
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Eq {
+  fn Equal[self: C](other: C) -> bool;
+  fn NotEqual[self: C](other: C) -> bool;
+}
+
+fn TestEqual(a: C, b: C) -> bool {
+  return a == b;
+}
+
+fn TestNotEqual(a: C, b: C) -> bool {
+  return a != b;
+}
+
+// --- fail_no_impl.carbon
+
+package FailNoImpl api;
+
+import Core;
+
+class D {};
+
+fn TestEqual(a: D, b: D) -> bool {
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Eq in type D that does not implement that interface.
+  // CHECK:STDERR:   return a == b;
+  // CHECK:STDERR:          ^~~~~~
+  return a == b;
+}
+
+fn TestNotEqual(a: D, b: D) -> bool {
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Eq in type D that does not implement that interface.
+  // CHECK:STDERR:   return a != b;
+  // CHECK:STDERR:          ^~~~~~
+  return a != b;
+}
+
+// --- fail_no_impl_for_args.carbon
+
+package FailNoImplForArgs api;
+
+import Core;
+
+class C {};
+class D {};
+
+impl C as Core.Eq {
+  fn Equal[self: C](other: C) -> bool;
+  fn NotEqual[self: C](other: C) -> bool;
+}
+
+fn TestRhsBad(a: C, b: D) -> bool {
+  // CHECK:STDERR: fail_no_impl_for_args.carbon:[[@LINE+6]]:10: ERROR: Cannot implicitly convert from `D` to `C`.
+  // CHECK:STDERR:   return a == b;
+  // CHECK:STDERR:          ^~~~~~
+  // CHECK:STDERR: fail_no_impl_for_args.carbon:[[@LINE-8]]:3: Initializing parameter 1 of function declared here.
+  // CHECK:STDERR:   fn Equal[self: C](other: C) -> bool;
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  return a == b;
+}
+
+fn TestLhsBad(a: D, b: C) -> bool {
+  // CHECK:STDERR: fail_no_impl_for_args.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Eq in type D that does not implement that interface.
+  // CHECK:STDERR:   return a != b;
+  // CHECK:STDERR:          ^~~~~~
+  return a != b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Eq [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Eq, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Eq> = assoc_entity element0, @Eq.%Equal [template]
+// CHECK:STDOUT:   %.4: <associated <function> in Eq> = assoc_entity element1, @Eq.%NotEqual [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Eq = %Eq.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Eq.decl: type = interface_decl @Eq [template = constants.%.1] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Eq {
+// CHECK:STDOUT:   %Self: Eq = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Equal: <function> = fn_decl @Equal [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_18: Eq = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_18.1: type = facet_type_access %Self.ref.loc5_18 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_18.2: type = converted %Self.ref.loc5_18, %.loc5_18.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_12.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_12.2: Self = bind_name self, %self.loc5_12.1
+// CHECK:STDOUT:     %Self.ref.loc5_31: Eq = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_31.1: type = facet_type_access %Self.ref.loc5_31 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_31.2: type = converted %Self.ref.loc5_31, %.loc5_31.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_24.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_24.2: Self = bind_name other, %other.loc5_24.1
+// CHECK:STDOUT:     %return.var.loc5: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_44: <associated <function> in Eq> = assoc_entity element0, %Equal [template = constants.%.3]
+// CHECK:STDOUT:   %NotEqual: <function> = fn_decl @NotEqual [template] {
+// CHECK:STDOUT:     %Self.ref.loc6_21: Eq = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc6_21.1: type = facet_type_access %Self.ref.loc6_21 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc6_21.2: type = converted %Self.ref.loc6_21, %.loc6_21.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc6_15.1: Self = param self
+// CHECK:STDOUT:     %self.loc6_15.2: Self = bind_name self, %self.loc6_15.1
+// CHECK:STDOUT:     %Self.ref.loc6_34: Eq = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc6_34.1: type = facet_type_access %Self.ref.loc6_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc6_34.2: type = converted %Self.ref.loc6_34, %.loc6_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc6_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc6_27.2: Self = bind_name other, %other.loc6_27.1
+// CHECK:STDOUT:     %return.var.loc6: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc6_47: <associated <function> in Eq> = assoc_entity element1, %NotEqual [template = constants.%.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Equal = %.loc5_44
+// CHECK:STDOUT:   .NotEqual = %.loc6_47
+// CHECK:STDOUT:   witness = (%Equal, %NotEqual)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Equal[@Eq.%self.loc5_12.2: Self](@Eq.%other.loc5_24.2: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @NotEqual[@Eq.%self.loc6_15.2: Self](@Eq.%other.loc6_27.2: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Eq [template]
+// CHECK:STDOUT:   %.3: <witness> = interface_witness (@impl.%Equal, @impl.%NotEqual) [template]
+// CHECK:STDOUT:   %.4: type = tuple_type () [template]
+// CHECK:STDOUT:   %.5: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @Eq, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in Eq> = assoc_entity element0, @TestEqual.%import_ref.2 [template]
+// CHECK:STDOUT:   %.8: <associated <function> in Eq> = assoc_entity element1, @TestNotEqual.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestEqual = %TestEqual
+// CHECK:STDOUT:     .TestNotEqual = %TestNotEqual
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Eq.decl: invalid = interface_decl @Eq [template = constants.%.2] {}
+// CHECK:STDOUT:     %Eq.ref: type = name_ref Eq, %import_ref [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestEqual: <function> = fn_decl @TestEqual [template] {
+// CHECK:STDOUT:     %C.ref.loc13_17: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc13_14.1: C = param a
+// CHECK:STDOUT:     @TestEqual.%a: C = bind_name a, %a.loc13_14.1
+// CHECK:STDOUT:     %C.ref.loc13_23: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc13_20.1: C = param b
+// CHECK:STDOUT:     @TestEqual.%b: C = bind_name b, %b.loc13_20.1
+// CHECK:STDOUT:     %return.var.loc13: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestNotEqual: <function> = fn_decl @TestNotEqual [template] {
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_17.1: C = param a
+// CHECK:STDOUT:     @TestNotEqual.%a: C = bind_name a, %a.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_23.1: C = param b
+// CHECK:STDOUT:     @TestNotEqual.%b: C = bind_name b, %b.loc17_23.1
+// CHECK:STDOUT:     %return.var.loc17: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Eq {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Eq> = import_ref ir1, inst+17, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <associated <function> in Eq> = import_ref ir1, inst+31, used [template = constants.%.8]
+// CHECK:STDOUT:   %import_ref.4: <function> = import_ref ir1, inst+15, used [template = imports.%Equal]
+// CHECK:STDOUT:   %import_ref.5: <function> = import_ref ir1, inst+30, used [template = imports.%NotEqual]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Equal = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   .NotEqual = %import_ref.3
+// CHECK:STDOUT:   witness = (%import_ref.4, %import_ref.5)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: C as Eq {
+// CHECK:STDOUT:   %Equal: <function> = fn_decl @Equal.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_18: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_12.1: C = param self
+// CHECK:STDOUT:     %self.loc9_12.2: C = bind_name self, %self.loc9_12.1
+// CHECK:STDOUT:     %C.ref.loc9_28: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_21.1: C = param other
+// CHECK:STDOUT:     %other.loc9_21.2: C = bind_name other, %other.loc9_21.1
+// CHECK:STDOUT:     %return.var.loc9: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %NotEqual: <function> = fn_decl @NotEqual.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc10_21: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc10_15.1: C = param self
+// CHECK:STDOUT:     %self.loc10_15.2: C = bind_name self, %self.loc10_15.1
+// CHECK:STDOUT:     %C.ref.loc10_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc10_24.1: C = param other
+// CHECK:STDOUT:     %other.loc10_24.2: C = bind_name other, %other.loc10_24.1
+// CHECK:STDOUT:     %return.var.loc10: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Equal, %NotEqual) [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Equal = %Equal
+// CHECK:STDOUT:   .NotEqual = %NotEqual
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Equal.1[@impl.%self.loc9_12.2: C](@impl.%other.loc9_21.2: C) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @NotEqual.1[@impl.%self.loc10_15.2: C](@impl.%other.loc10_24.2: C) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Equal.2[%self: Self](%other: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @NotEqual.2[%self: Self](%other: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestEqual(%a: C, %b: C) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+15, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%Equal]
+// CHECK:STDOUT:   %.loc14_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc14_12.2: init bool = call %.loc14_12.1(%a.ref, %b.ref)
+// CHECK:STDOUT:   %.loc14_16: bool = value_of_initializer %.loc14_12.2
+// CHECK:STDOUT:   %.loc14_12.3: bool = converted %.loc14_12.2, %.loc14_16
+// CHECK:STDOUT:   return %.loc14_12.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestNotEqual(%a: C, %b: C) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element1 [template = @impl.%NotEqual]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc18_12.2: init bool = call %.loc18_12.1(%a.ref, %b.ref)
+// CHECK:STDOUT:   %.loc18_16: bool = value_of_initializer %.loc18_12.2
+// CHECK:STDOUT:   %.loc18_12.3: bool = converted %.loc18_12.2, %.loc18_16
+// CHECK:STDOUT:   return %.loc18_12.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_no_impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %.3: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.4: type = interface_type @Eq [template]
+// CHECK:STDOUT:   %.5: type = assoc_entity_type @Eq, <function> [template]
+// CHECK:STDOUT:   %.6: <associated <function> in Eq> = assoc_entity element0, @TestEqual.%import_ref.2 [template]
+// CHECK:STDOUT:   %.7: <associated <function> in Eq> = assoc_entity element1, @TestNotEqual.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .D = %D.decl
+// CHECK:STDOUT:     .TestEqual = %TestEqual
+// CHECK:STDOUT:     .TestNotEqual = %TestNotEqual
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:   %TestEqual: <function> = fn_decl @TestEqual [template] {
+// CHECK:STDOUT:     %D.ref.loc8_17: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %a.loc8_14.1: D = param a
+// CHECK:STDOUT:     @TestEqual.%a: D = bind_name a, %a.loc8_14.1
+// CHECK:STDOUT:     %D.ref.loc8_23: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc8_20.1: D = param b
+// CHECK:STDOUT:     @TestEqual.%b: D = bind_name b, %b.loc8_20.1
+// CHECK:STDOUT:     %return.var.loc8: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestNotEqual: <function> = fn_decl @TestNotEqual [template] {
+// CHECK:STDOUT:     %D.ref.loc15_20: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %a.loc15_17.1: D = param a
+// CHECK:STDOUT:     @TestNotEqual.%a: D = bind_name a, %a.loc15_17.1
+// CHECK:STDOUT:     %D.ref.loc15_26: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc15_23.1: D = param b
+// CHECK:STDOUT:     @TestNotEqual.%b: D = bind_name b, %b.loc15_23.1
+// CHECK:STDOUT:     %return.var.loc15: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Eq {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Eq> = import_ref ir1, inst+17, used [template = constants.%.6]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <associated <function> in Eq> = import_ref ir1, inst+31, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+15, unused
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Equal = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   .NotEqual = %import_ref.3
+// CHECK:STDOUT:   witness = (%import_ref.4, %import_ref.5)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%D
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestEqual(%a: D, %b: D) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: D = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.4]
+// CHECK:STDOUT:   %Eq.decl: invalid = interface_decl @Eq [template = constants.%.4] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+15, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestNotEqual(%a: D, %b: D) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: D = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.4]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_no_impl_for_args.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Eq [template]
+// CHECK:STDOUT:   %.3: <witness> = interface_witness (@impl.%Equal, @impl.%NotEqual) [template]
+// CHECK:STDOUT:   %.4: type = tuple_type () [template]
+// CHECK:STDOUT:   %.5: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @Eq, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in Eq> = assoc_entity element0, @TestRhsBad.%import_ref.2 [template]
+// CHECK:STDOUT:   %.8: <associated <function> in Eq> = assoc_entity element1, @TestLhsBad.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .D = %D.decl
+// CHECK:STDOUT:     .TestRhsBad = %TestRhsBad
+// CHECK:STDOUT:     .TestLhsBad = %TestLhsBad
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:   impl_decl @impl {
+// CHECK:STDOUT:     %C.ref.loc9: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Eq.decl: invalid = interface_decl @Eq [template = constants.%.2] {}
+// CHECK:STDOUT:     %Eq.ref: type = name_ref Eq, %import_ref [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestRhsBad: <function> = fn_decl @TestRhsBad [template] {
+// CHECK:STDOUT:     %C.ref.loc14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc14_15.1: C = param a
+// CHECK:STDOUT:     @TestRhsBad.%a: C = bind_name a, %a.loc14_15.1
+// CHECK:STDOUT:     %D.ref.loc14: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc14_21.1: D = param b
+// CHECK:STDOUT:     @TestRhsBad.%b: D = bind_name b, %b.loc14_21.1
+// CHECK:STDOUT:     %return.var.loc14: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestLhsBad: <function> = fn_decl @TestLhsBad [template] {
+// CHECK:STDOUT:     %D.ref.loc24: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %a.loc24_15.1: D = param a
+// CHECK:STDOUT:     @TestLhsBad.%a: D = bind_name a, %a.loc24_15.1
+// CHECK:STDOUT:     %C.ref.loc24: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc24_21.1: C = param b
+// CHECK:STDOUT:     @TestLhsBad.%b: C = bind_name b, %b.loc24_21.1
+// CHECK:STDOUT:     %return.var.loc24: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Eq {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Eq> = import_ref ir1, inst+17, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <associated <function> in Eq> = import_ref ir1, inst+31, used [template = constants.%.8]
+// CHECK:STDOUT:   %import_ref.4: <function> = import_ref ir1, inst+15, used [template = imports.%Equal]
+// CHECK:STDOUT:   %import_ref.5: <function> = import_ref ir1, inst+30, used [template = imports.%NotEqual]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Equal = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   .NotEqual = %import_ref.3
+// CHECK:STDOUT:   witness = (%import_ref.4, %import_ref.5)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: C as Eq {
+// CHECK:STDOUT:   %Equal: <function> = fn_decl @Equal.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc10_18: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc10_12.1: C = param self
+// CHECK:STDOUT:     %self.loc10_12.2: C = bind_name self, %self.loc10_12.1
+// CHECK:STDOUT:     %C.ref.loc10_28: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc10_21.1: C = param other
+// CHECK:STDOUT:     %other.loc10_21.2: C = bind_name other, %other.loc10_21.1
+// CHECK:STDOUT:     %return.var.loc10: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %NotEqual: <function> = fn_decl @NotEqual.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc11_21: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc11_15.1: C = param self
+// CHECK:STDOUT:     %self.loc11_15.2: C = bind_name self, %self.loc11_15.1
+// CHECK:STDOUT:     %C.ref.loc11_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc11_24.1: C = param other
+// CHECK:STDOUT:     %other.loc11_24.2: C = bind_name other, %other.loc11_24.1
+// CHECK:STDOUT:     %return.var.loc11: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Equal, %NotEqual) [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Equal = %Equal
+// CHECK:STDOUT:   .NotEqual = %NotEqual
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%D
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Equal.1[@impl.%self.loc10_12.2: C](@impl.%other.loc10_21.2: C) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @NotEqual.1[@impl.%self.loc11_15.2: C](@impl.%other.loc11_24.2: C) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Equal.2[%self: Self](%other: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @NotEqual.2[%self: Self](%other: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestRhsBad(%a: C, %b: D) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+15, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%Equal]
+// CHECK:STDOUT:   %.loc21_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc21_12.2: init bool = call %.loc21_12.1(<invalid>)
+// CHECK:STDOUT:   %.loc21_16: bool = value_of_initializer %.loc21_12.2
+// CHECK:STDOUT:   %.loc21_12.3: bool = converted %.loc21_12.2, %.loc21_16
+// CHECK:STDOUT:   return %.loc21_12.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestLhsBad(%a: D, %b: C) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: D = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 270 - 0
toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon

@@ -0,0 +1,270 @@
+// 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
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Inc {
+  fn Op[addr self: Self*]();
+}
+interface AddAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- fail_assign_non_ref.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Inc {
+  fn Op[addr self: C*]();
+}
+impl C as Core.AddAssign {
+  fn Op[addr self: C*](other: C);
+}
+
+fn TestIncNonRef(a: C) {
+  // CHECK:STDERR: fail_assign_non_ref.carbon:[[@LINE+6]]:3: ERROR: `addr self` method cannot be invoked on a value.
+  // CHECK:STDERR:   ++a;
+  // CHECK:STDERR:   ^~
+  // CHECK:STDERR: fail_assign_non_ref.carbon:[[@LINE-10]]:14: Initializing `addr self` parameter of method declared here.
+  // CHECK:STDERR:   fn Op[addr self: C*]();
+  // CHECK:STDERR:              ^~~~
+  ++a;
+}
+
+fn TestAddAssignNonRef(a: C, b: C) {
+  // CHECK:STDERR: fail_assign_non_ref.carbon:[[@LINE+6]]:5: ERROR: `addr self` method cannot be invoked on a value.
+  // CHECK:STDERR:   a += b;
+  // CHECK:STDERR:     ^~
+  // CHECK:STDERR: fail_assign_non_ref.carbon:[[@LINE-17]]:14: Initializing `addr self` parameter of method declared here.
+  // CHECK:STDERR:   fn Op[addr self: C*](other: C);
+  // CHECK:STDERR:              ^~~~
+  a += b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Inc [template]
+// CHECK:STDOUT:   %.2: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.3: type = assoc_entity_type @Inc, <function> [template]
+// CHECK:STDOUT:   %.4: <associated <function> in Inc> = assoc_entity element0, @Inc.%Op [template]
+// CHECK:STDOUT:   %.5: type = interface_type @AddAssign [template]
+// CHECK:STDOUT:   %.6: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type @AddAssign, <function> [template]
+// CHECK:STDOUT:   %.8: <associated <function> in AddAssign> = assoc_entity element0, @AddAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Inc = %Inc.decl
+// CHECK:STDOUT:     .AddAssign = %AddAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Inc.decl: type = interface_decl @Inc [template = constants.%.1] {}
+// CHECK:STDOUT:   %AddAssign.decl: type = interface_decl @AddAssign [template = constants.%.5] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Inc {
+// CHECK:STDOUT:   %Self: Inc = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref: Inc = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_24.1: type = facet_type_access %Self.ref [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_20: type = converted %Self.ref, %.loc5_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_24.2: type = ptr_type Self [symbolic = constants.%.2]
+// CHECK:STDOUT:     %self.loc5_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc5_14.3: Self* = bind_name self, %self.loc5_14.1
+// CHECK:STDOUT:     %.loc5_9: Self* = addr_pattern %self.loc5_14.3
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_28: <associated <function> in Inc> = assoc_entity element0, %Op [template = constants.%.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_28
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddAssign {
+// CHECK:STDOUT:   %Self: AddAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: AddAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.6]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: AddAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in AddAssign> = assoc_entity element0, %Op [template = constants.%.8]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[addr @Inc.%self.loc5_14.3: Self*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @AddAssign.%self.loc8_14.3: Self*](@AddAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_assign_non_ref.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Inc [template]
+// CHECK:STDOUT:   %.3: type = ptr_type C [template]
+// CHECK:STDOUT:   %.4: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.5: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.6: type = interface_type @AddAssign [template]
+// CHECK:STDOUT:   %.7: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.8: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.9: type = tuple_type () [template]
+// CHECK:STDOUT:   %.10: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @Inc, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in Inc> = assoc_entity element0, @TestIncNonRef.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @AddAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in AddAssign> = assoc_entity element0, @TestAddAssignNonRef.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestIncNonRef = %TestIncNonRef
+// CHECK:STDOUT:     .TestAddAssignNonRef = %TestAddAssignNonRef
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Inc.decl: invalid = interface_decl @Inc [template = constants.%.2] {}
+// CHECK:STDOUT:     %Inc.ref: type = name_ref Inc, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc11: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc11: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+16, used [template = constants.%.6]
+// CHECK:STDOUT:     %AddAssign.decl: invalid = interface_decl @AddAssign [template = constants.%.6] {}
+// CHECK:STDOUT:     %AddAssign.ref: type = name_ref AddAssign, %import_ref.2 [template = constants.%.6]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestIncNonRef: <function> = fn_decl @TestIncNonRef [template] {
+// CHECK:STDOUT:     %C.ref.loc15: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc15_18.1: C = param a
+// CHECK:STDOUT:     @TestIncNonRef.%a: C = bind_name a, %a.loc15_18.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAddAssignNonRef: <function> = fn_decl @TestAddAssignNonRef [template] {
+// CHECK:STDOUT:     %C.ref.loc25_27: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc25_24.1: C = param a
+// CHECK:STDOUT:     @TestAddAssignNonRef.%a: C = bind_name a, %a.loc25_24.1
+// CHECK:STDOUT:     %C.ref.loc25_33: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc25_30.1: C = param b
+// CHECK:STDOUT:     @TestAddAssignNonRef.%b: C = bind_name b, %b.loc25_30.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Inc {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Inc> = import_ref ir1, inst+14, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+12, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in AddAssign> = import_ref ir1, inst+34, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+32, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as Inc {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc9_21: type = ptr_type C [template = constants.%.3]
+// CHECK:STDOUT:     %self.loc9_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc9_14.3: C* = bind_name self, %self.loc9_14.1
+// CHECK:STDOUT:     %.loc9_9: C* = addr_pattern %self.loc9_14.3
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as AddAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc12_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc12_21: type = ptr_type C [template = constants.%.3]
+// CHECK:STDOUT:     %self.loc12_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc12_14.3: C* = bind_name self, %self.loc12_14.1
+// CHECK:STDOUT:     %.loc12_9: C* = addr_pattern %self.loc12_14.3
+// CHECK:STDOUT:     %C.ref.loc12_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc12_24.1: C = param other
+// CHECK:STDOUT:     %other.loc12_24.2: C = bind_name other, %other.loc12_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.8]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[addr @impl.1.%self.loc9_14.3: C*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr %self: Self*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc12_14.3: C*](@impl.2.%other.loc12_24.2: C);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestIncNonRef(%a: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+12, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc22_3.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc22_3.2: init () = call %.loc22_3.1(<invalid>)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAddAssignNonRef(%a: C, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+16, used [template = constants.%.6]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+32, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc32_5.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc32_5.2: init () = call %.loc32_5.1(<invalid>)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 328 - 0
toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon

@@ -0,0 +1,328 @@
+// 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
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Negate {
+  fn Op[self: Self]();
+}
+interface Add {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface AddAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+interface Inc {
+  fn Op[addr self: Self*]();
+}
+
+// --- fail_no_impl.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+fn TestUnary(a: C) -> C {
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Negate in type C that does not implement that interface.
+  // CHECK:STDERR:   return -a;
+  // CHECK:STDERR:          ^~
+  return -a;
+}
+
+fn TestBinary(a: C, b: C) -> C {
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Add in type C that does not implement that interface.
+  // CHECK:STDERR:   return a + b;
+  // CHECK:STDERR:          ^~~~~
+  return a + b;
+}
+
+fn TestRef(b: C) {
+  var a: C = {};
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:3: ERROR: Cannot access member of interface AddAssign in type C that does not implement that interface.
+  // CHECK:STDERR:   a += b;
+  // CHECK:STDERR:   ^~~~~~
+  a += b;
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:3: ERROR: Cannot access member of interface Inc in type C that does not implement that interface.
+  // CHECK:STDERR:   ++a;
+  // CHECK:STDERR:   ^~~
+  ++a;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Negate [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Negate, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Negate> = assoc_entity element0, @Negate.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @Add [template]
+// CHECK:STDOUT:   %.5: type = assoc_entity_type @Add, <function> [template]
+// CHECK:STDOUT:   %.6: <associated <function> in Add> = assoc_entity element0, @Add.%Op [template]
+// CHECK:STDOUT:   %.7: type = interface_type @AddAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.9: type = assoc_entity_type @AddAssign, <function> [template]
+// CHECK:STDOUT:   %.10: <associated <function> in AddAssign> = assoc_entity element0, @AddAssign.%Op [template]
+// CHECK:STDOUT:   %.11: type = interface_type @Inc [template]
+// CHECK:STDOUT:   %.12: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @Inc, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in Inc> = assoc_entity element0, @Inc.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Negate = %Negate.decl
+// CHECK:STDOUT:     .Add = %Add.decl
+// CHECK:STDOUT:     .AddAssign = %AddAssign.decl
+// CHECK:STDOUT:     .Inc = %Inc.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Negate.decl: type = interface_decl @Negate [template = constants.%.1] {}
+// CHECK:STDOUT:   %Add.decl: type = interface_decl @Add [template = constants.%.4] {}
+// CHECK:STDOUT:   %AddAssign.decl: type = interface_decl @AddAssign [template = constants.%.7] {}
+// CHECK:STDOUT:   %Inc.decl: type = interface_decl @Inc [template = constants.%.11] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Negate {
+// CHECK:STDOUT:   %Self: Negate = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref: Negate = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_22: <associated <function> in Negate> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_22
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Add {
+// CHECK:STDOUT:   %Self: Add = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_15: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_15.1: type = facet_type_access %Self.ref.loc8_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_15.2: type = converted %Self.ref.loc8_15, %.loc8_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc8_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc8_9.2: Self = bind_name self, %self.loc8_9.1
+// CHECK:STDOUT:     %Self.ref.loc8_28: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_28.1: type = facet_type_access %Self.ref.loc8_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_28.2: type = converted %Self.ref.loc8_28, %.loc8_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_21.2: Self = bind_name other, %other.loc8_21.1
+// CHECK:STDOUT:     %Self.ref.loc8_37: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_37.1: type = facet_type_access %Self.ref.loc8_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_37.2: type = converted %Self.ref.loc8_37, %.loc8_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_41: <associated <function> in Add> = assoc_entity element0, %Op [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddAssign {
+// CHECK:STDOUT:   %Self: AddAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %Self.ref.loc11_20: AddAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc11_24.1: type = facet_type_access %Self.ref.loc11_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc11_20: type = converted %Self.ref.loc11_20, %.loc11_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc11_24.2: type = ptr_type Self [symbolic = constants.%.8]
+// CHECK:STDOUT:     %self.loc11_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc11_14.3: Self* = bind_name self, %self.loc11_14.1
+// CHECK:STDOUT:     %.loc11_9: Self* = addr_pattern %self.loc11_14.3
+// CHECK:STDOUT:     %Self.ref.loc11_34: AddAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc11_34.1: type = facet_type_access %Self.ref.loc11_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc11_34.2: type = converted %Self.ref.loc11_34, %.loc11_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc11_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc11_27.2: Self = bind_name other, %other.loc11_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc11_39: <associated <function> in AddAssign> = assoc_entity element0, %Op [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc11_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Inc {
+// CHECK:STDOUT:   %Self: Inc = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.4 [template] {
+// CHECK:STDOUT:     %Self.ref: Inc = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc14_24.1: type = facet_type_access %Self.ref [symbolic = %Self]
+// CHECK:STDOUT:     %.loc14_20: type = converted %Self.ref, %.loc14_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc14_24.2: type = ptr_type Self [symbolic = constants.%.12]
+// CHECK:STDOUT:     %self.loc14_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: Self* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: Self* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc14_28: <associated <function> in Inc> = assoc_entity element0, %Op [template = constants.%.14]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc14_28
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@Negate.%self.loc5_9.2: Self]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[@Add.%self.loc8_9.2: Self](@Add.%other.loc8_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @AddAssign.%self.loc11_14.3: Self*](@AddAssign.%other.loc11_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr @Inc.%self.loc14_14.3: Self*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_no_impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %.3: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.4: type = interface_type @Negate [template]
+// CHECK:STDOUT:   %.5: type = assoc_entity_type @Negate, <function> [template]
+// CHECK:STDOUT:   %.6: <associated <function> in Negate> = assoc_entity element0, @TestUnary.%import_ref.2 [template]
+// CHECK:STDOUT:   %.7: type = interface_type @Add [template]
+// CHECK:STDOUT:   %.8: type = assoc_entity_type @Add, <function> [template]
+// CHECK:STDOUT:   %.9: <associated <function> in Add> = assoc_entity element0, @TestBinary.%import_ref.2 [template]
+// CHECK:STDOUT:   %.10: C = struct_value () [template]
+// CHECK:STDOUT:   %.11: type = interface_type @AddAssign [template]
+// CHECK:STDOUT:   %.12: type = assoc_entity_type @AddAssign, <function> [template]
+// CHECK:STDOUT:   %.13: <associated <function> in AddAssign> = assoc_entity element0, @TestRef.%import_ref.2 [template]
+// CHECK:STDOUT:   %.14: type = interface_type @Inc [template]
+// CHECK:STDOUT:   %.15: type = assoc_entity_type @Inc, <function> [template]
+// CHECK:STDOUT:   %.16: <associated <function> in Inc> = assoc_entity element0, @TestRef.%import_ref.4 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestUnary = %TestUnary
+// CHECK:STDOUT:     .TestBinary = %TestBinary
+// CHECK:STDOUT:     .TestRef = %TestRef
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %TestUnary: <function> = fn_decl @TestUnary [template] {
+// CHECK:STDOUT:     %C.ref.loc8_17: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc8_14.1: C = param a
+// CHECK:STDOUT:     @TestUnary.%a: C = bind_name a, %a.loc8_14.1
+// CHECK:STDOUT:     %C.ref.loc8_23: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestUnary.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestBinary: <function> = fn_decl @TestBinary [template] {
+// CHECK:STDOUT:     %C.ref.loc15_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc15_15.1: C = param a
+// CHECK:STDOUT:     @TestBinary.%a: C = bind_name a, %a.loc15_15.1
+// CHECK:STDOUT:     %C.ref.loc15_24: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc15_21.1: C = param b
+// CHECK:STDOUT:     @TestBinary.%b: C = bind_name b, %b.loc15_21.1
+// CHECK:STDOUT:     %C.ref.loc15_30: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestBinary.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestRef: <function> = fn_decl @TestRef [template] {
+// CHECK:STDOUT:     %C.ref.loc22: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc22_12.1: C = param b
+// CHECK:STDOUT:     @TestRef.%b: C = bind_name b, %b.loc22_12.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Negate {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Negate> = import_ref ir1, inst+11, used [template = constants.%.6]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+9, unused
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Add {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Add> = import_ref ir1, inst+32, used [template = constants.%.9]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+15, unused
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in AddAssign> = import_ref ir1, inst+52, used [template = constants.%.13]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+36, unused
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+50, unused
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Inc {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Inc> = import_ref ir1, inst+67, used [template = constants.%.16]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+56, unused
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+65, unused
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestUnary(%a: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.4]
+// CHECK:STDOUT:   %Negate.decl: invalid = interface_decl @Negate [template = constants.%.4] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+9, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestBinary(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+13, used [template = constants.%.7]
+// CHECK:STDOUT:   %Add.decl: invalid = interface_decl @Add [template = constants.%.7] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestRef(%b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:   %a.var: ref C = var a
+// CHECK:STDOUT:   %a: ref C = bind_name a, %a.var
+// CHECK:STDOUT:   %.loc23_15.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc23_15.2: init C = class_init (), %a.var [template = constants.%.10]
+// CHECK:STDOUT:   %.loc23_15.3: init C = converted %.loc23_15.1, %.loc23_15.2 [template = constants.%.10]
+// CHECK:STDOUT:   assign %a.var, %.loc23_15.3
+// CHECK:STDOUT:   %a.ref.loc27: ref C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+34, used [template = constants.%.11]
+// CHECK:STDOUT:   %AddAssign.decl: invalid = interface_decl @AddAssign [template = constants.%.11] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+50, unused
+// CHECK:STDOUT:   %a.ref.loc31: ref C = name_ref a, %a
+// CHECK:STDOUT:   %import_ref.3: type = import_ref ir1, inst+54, used [template = constants.%.14]
+// CHECK:STDOUT:   %Inc.decl: invalid = interface_decl @Inc [template = constants.%.14] {}
+// CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+65, unused
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 301 - 0
toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon

@@ -0,0 +1,301 @@
+// 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
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Add {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface AddAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- fail_no_impl_for_arg.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+class D {};
+
+impl C as Core.Add {
+  fn Op[self: C](other: C) -> C;
+}
+impl C as Core.AddAssign {
+  fn Op[addr self: C*](other: C);
+}
+
+fn Test(a: C, b: D) -> C {
+  // CHECK:STDERR: fail_no_impl_for_arg.carbon:[[@LINE+6]]:10: ERROR: Cannot implicitly convert from `D` to `C`.
+  // CHECK:STDERR:   return a + b;
+  // CHECK:STDERR:          ^~~~~
+  // CHECK:STDERR: fail_no_impl_for_arg.carbon:[[@LINE-10]]:3: Initializing parameter 1 of function declared here.
+  // CHECK:STDERR:   fn Op[self: C](other: C) -> C;
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  return a + b;
+}
+
+fn TestAssign(b: D) {
+  var a: C = {};
+  // CHECK:STDERR: fail_no_impl_for_arg.carbon:[[@LINE+6]]:3: ERROR: Cannot implicitly convert from `D` to `C`.
+  // CHECK:STDERR:   a += b;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR: fail_no_impl_for_arg.carbon:[[@LINE-18]]:3: Initializing parameter 1 of function declared here.
+  // CHECK:STDERR:   fn Op[addr self: C*](other: C);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  a += b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Add [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Add, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Add> = assoc_entity element0, @Add.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @AddAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @AddAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in AddAssign> = assoc_entity element0, @AddAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Add = %Add.decl
+// CHECK:STDOUT:     .AddAssign = %AddAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Add.decl: type = interface_decl @Add [template = constants.%.1] {}
+// CHECK:STDOUT:   %AddAssign.decl: type = interface_decl @AddAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Add {
+// CHECK:STDOUT:   %Self: Add = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: Add = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in Add> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddAssign {
+// CHECK:STDOUT:   %Self: AddAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: AddAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: AddAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in AddAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@Add.%self.loc5_9.2: Self](@Add.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @AddAssign.%self.loc8_14.3: Self*](@AddAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_no_impl_for_arg.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Add [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.6: type = interface_type @AddAssign [template]
+// CHECK:STDOUT:   %.7: type = ptr_type C [template]
+// CHECK:STDOUT:   %.8: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.9: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.10: type = assoc_entity_type @Add, <function> [template]
+// CHECK:STDOUT:   %.11: <associated <function> in Add> = assoc_entity element0, @Test.%import_ref.2 [template]
+// CHECK:STDOUT:   %.12: C = struct_value () [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @AddAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in AddAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .D = %D.decl
+// CHECK:STDOUT:     .Test = %Test
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc9: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc9: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Add.decl: invalid = interface_decl @Add [template = constants.%.2] {}
+// CHECK:STDOUT:     %Add.ref: type = name_ref Add, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc12: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc12: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.6]
+// CHECK:STDOUT:     %AddAssign.decl: invalid = interface_decl @AddAssign [template = constants.%.6] {}
+// CHECK:STDOUT:     %AddAssign.ref: type = name_ref AddAssign, %import_ref.2 [template = constants.%.6]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Test: <function> = fn_decl @Test [template] {
+// CHECK:STDOUT:     %C.ref.loc16_12: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc16_9.1: C = param a
+// CHECK:STDOUT:     @Test.%a: C = bind_name a, %a.loc16_9.1
+// CHECK:STDOUT:     %D.ref.loc16: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc16_15.1: D = param b
+// CHECK:STDOUT:     @Test.%b: D = bind_name b, %b.loc16_15.1
+// CHECK:STDOUT:     %C.ref.loc16_24: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @Test.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %D.ref.loc26: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc26_15.1: D = param b
+// CHECK:STDOUT:     @TestAssign.%b: D = bind_name b, %b.loc26_15.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Add {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Add> = import_ref ir1, inst+20, used [template = constants.%.11]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in AddAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as Add {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc10_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc10_9.1: C = param self
+// CHECK:STDOUT:     %self.loc10_9.2: C = bind_name self, %self.loc10_9.1
+// CHECK:STDOUT:     %C.ref.loc10_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc10_18.1: C = param other
+// CHECK:STDOUT:     %other.loc10_18.2: C = bind_name other, %other.loc10_18.1
+// CHECK:STDOUT:     %C.ref.loc10_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as AddAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc13_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc13_21: type = ptr_type C [template = constants.%.7]
+// CHECK:STDOUT:     %self.loc13_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc13_14.3: C* = bind_name self, %self.loc13_14.1
+// CHECK:STDOUT:     %.loc13_9: C* = addr_pattern %self.loc13_14.3
+// CHECK:STDOUT:     %C.ref.loc13_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc13_24.1: C = param other
+// CHECK:STDOUT:     %other.loc13_24.2: C = bind_name other, %other.loc13_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.9]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%D
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc10_9.2: C](@impl.1.%other.loc10_18.2: C) -> @impl.1.%return.var: C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc13_14.3: C*](@impl.2.%other.loc13_24.2: C);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Test(%a: C, %b: D) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc23_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc23_12.2: ref C = temporary_storage
+// CHECK:STDOUT:   %.loc23_12.3: init C = call %.loc23_12.1(<invalid>)
+// CHECK:STDOUT:   return %.loc23_12.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%b: D) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:   %a.var: ref C = var a
+// CHECK:STDOUT:   %a: ref C = bind_name a, %a.var
+// CHECK:STDOUT:   %.loc27_15.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc27_15.2: init C = class_init (), %a.var [template = constants.%.12]
+// CHECK:STDOUT:   %.loc27_15.3: init C = converted %.loc27_15.1, %.loc27_15.2 [template = constants.%.12]
+// CHECK:STDOUT:   assign %a.var, %.loc27_15.3
+// CHECK:STDOUT:   %a.ref: ref C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.6]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc34_5.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc34_3: C* = addr_of %a.ref
+// CHECK:STDOUT:   %.loc34_5.2: init () = call %.loc34_5.1(<invalid>)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 158 - 0
toolchain/check/testdata/operators/overloaded/inc.carbon

@@ -0,0 +1,158 @@
+// 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
+
+// This file was generated from unary_stmt.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Inc {
+  fn Op[addr self: Self*]();
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Inc {
+  fn Op[addr self: C*]();
+}
+
+fn TestOp() {
+  var c: C = {};
+  ++c;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Inc [template]
+// CHECK:STDOUT:   %.2: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.3: type = assoc_entity_type @Inc, <function> [template]
+// CHECK:STDOUT:   %.4: <associated <function> in Inc> = assoc_entity element0, @Inc.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Inc = %Inc.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Inc.decl: type = interface_decl @Inc [template = constants.%.1] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Inc {
+// CHECK:STDOUT:   %Self: Inc = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op [template] {
+// CHECK:STDOUT:     %Self.ref: Inc = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_24.1: type = facet_type_access %Self.ref [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_20: type = converted %Self.ref, %.loc5_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_24.2: type = ptr_type Self [symbolic = constants.%.2]
+// CHECK:STDOUT:     %self.loc5_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc5_14.3: Self* = bind_name self, %self.loc5_14.1
+// CHECK:STDOUT:     %.loc5_9: Self* = addr_pattern %self.loc5_14.3
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_28: <associated <function> in Inc> = assoc_entity element0, %Op [template = constants.%.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_28
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op[addr @Inc.%self.loc5_14.3: Self*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Inc [template]
+// CHECK:STDOUT:   %.3: type = ptr_type C [template]
+// CHECK:STDOUT:   %.4: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.5: <witness> = interface_witness (@impl.%Op) [template]
+// CHECK:STDOUT:   %.6: type = tuple_type () [template]
+// CHECK:STDOUT:   %.7: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.8: C = struct_value () [template]
+// CHECK:STDOUT:   %.9: type = assoc_entity_type @Inc, <function> [template]
+// CHECK:STDOUT:   %.10: <associated <function> in Inc> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl {
+// CHECK:STDOUT:     %C.ref: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Inc.decl: invalid = interface_decl @Inc [template = constants.%.2] {}
+// CHECK:STDOUT:     %Inc.ref: type = name_ref Inc, %import_ref [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Inc {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Inc> = import_ref ir1, inst+14, used [template = constants.%.10]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+12, used [template = imports.%Op]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: C as Inc {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc9_21: type = ptr_type C [template = constants.%.3]
+// CHECK:STDOUT:     %self.loc9_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc9_14.3: C* = bind_name self, %self.loc9_14.1
+// CHECK:STDOUT:     %.loc9_9: C* = addr_pattern %self.loc9_14.3
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[addr @impl.%self.loc9_14.3: C*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr %self: Self*]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:   %c.var: ref C = var c
+// CHECK:STDOUT:   %c: ref C = bind_name c, %c.var
+// CHECK:STDOUT:   %.loc13_15.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc13_15.2: init C = class_init (), %c.var [template = constants.%.8]
+// CHECK:STDOUT:   %.loc13_15.3: init C = converted %.loc13_15.1, %.loc13_15.2 [template = constants.%.8]
+// CHECK:STDOUT:   assign %c.var, %.loc13_15.3
+// CHECK:STDOUT:   %c.ref: ref C = name_ref c, %c
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+12, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%Op]
+// CHECK:STDOUT:   %.loc14_3.1: <bound method> = bound_method %c.ref, %.1
+// CHECK:STDOUT:   %.loc14_5: C* = addr_of %c.ref
+// CHECK:STDOUT:   %.loc14_3.2: init () = call %.loc14_3.1(%.loc14_5)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 290 - 0
toolchain/check/testdata/operators/overloaded/left_shift.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface LeftShift {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface LeftShiftAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.LeftShift {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.LeftShiftAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a << b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a <<= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @LeftShift [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @LeftShift, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in LeftShift> = assoc_entity element0, @LeftShift.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @LeftShiftAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @LeftShiftAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in LeftShiftAssign> = assoc_entity element0, @LeftShiftAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .LeftShift = %LeftShift.decl
+// CHECK:STDOUT:     .LeftShiftAssign = %LeftShiftAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %LeftShift.decl: type = interface_decl @LeftShift [template = constants.%.1] {}
+// CHECK:STDOUT:   %LeftShiftAssign.decl: type = interface_decl @LeftShiftAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @LeftShift {
+// CHECK:STDOUT:   %Self: LeftShift = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: LeftShift = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: LeftShift = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: LeftShift = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in LeftShift> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @LeftShiftAssign {
+// CHECK:STDOUT:   %Self: LeftShiftAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: LeftShiftAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: LeftShiftAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in LeftShiftAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@LeftShift.%self.loc5_9.2: Self](@LeftShift.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @LeftShiftAssign.%self.loc8_14.3: Self*](@LeftShiftAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @LeftShift [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @LeftShiftAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @LeftShift, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in LeftShift> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @LeftShiftAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in LeftShiftAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %LeftShift.decl: invalid = interface_decl @LeftShift [template = constants.%.2] {}
+// CHECK:STDOUT:     %LeftShift.ref: type = name_ref LeftShift, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %LeftShiftAssign.decl: invalid = interface_decl @LeftShiftAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %LeftShiftAssign.ref: type = name_ref LeftShiftAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @LeftShift {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in LeftShift> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @LeftShiftAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in LeftShiftAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as LeftShift {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as LeftShiftAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 43 - 0
toolchain/check/testdata/operators/overloaded/make_tests.sh

@@ -0,0 +1,43 @@
+#! /bin/bash
+# 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
+
+# TODO: This is a short-term script to regenerate the test files for operators
+# while we're in a period where we expect the tests to have substantial churn.
+# Once the implementation of overloaded operators has stabilized, this script
+# and its template files should be removed.
+
+make_test() {
+  HEADER="// This file was generated from $4. Run make_tests.sh to regenerate."
+  sed "s,INTERFACE,$1,g; s,OP,$2,g; s,HEADER,$HEADER," "$4" > "$3.carbon"
+}
+
+make_unary_op_test() {
+  make_test "$1" "$2" "$3" unary_op.carbon.tmpl
+}
+
+make_unary_stmt_test() {
+  make_test "$1" "$2" "$3" unary_stmt.carbon.tmpl
+}
+
+make_binary_op_test() {
+  make_test "$1" "$2" "$3" binary_op.carbon.tmpl
+}
+
+make_unary_op_test BitComplement '^' bit_complement
+make_unary_op_test Negate '-' negate
+
+make_unary_stmt_test Dec '--' dec
+make_unary_stmt_test Inc '++' inc
+
+make_binary_op_test Add '+' add
+make_binary_op_test BitAnd '\&' bit_and
+make_binary_op_test BitOr '|' bit_or
+make_binary_op_test BitXor '^' bit_xor
+make_binary_op_test Div '/' div
+make_binary_op_test LeftShift '<<' left_shift
+make_binary_op_test Mod '%' mod
+make_binary_op_test Mul '*' mul
+make_binary_op_test RightShift '>>' right_shift
+make_binary_op_test Sub '-' sub

+ 290 - 0
toolchain/check/testdata/operators/overloaded/mod.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Mod {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface ModAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Mod {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.ModAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a % b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a %= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Mod [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Mod, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Mod> = assoc_entity element0, @Mod.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @ModAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @ModAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in ModAssign> = assoc_entity element0, @ModAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Mod = %Mod.decl
+// CHECK:STDOUT:     .ModAssign = %ModAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Mod.decl: type = interface_decl @Mod [template = constants.%.1] {}
+// CHECK:STDOUT:   %ModAssign.decl: type = interface_decl @ModAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Mod {
+// CHECK:STDOUT:   %Self: Mod = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: Mod = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: Mod = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: Mod = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in Mod> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @ModAssign {
+// CHECK:STDOUT:   %Self: ModAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: ModAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: ModAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in ModAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@Mod.%self.loc5_9.2: Self](@Mod.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @ModAssign.%self.loc8_14.3: Self*](@ModAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Mod [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @ModAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @Mod, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in Mod> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @ModAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in ModAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Mod.decl: invalid = interface_decl @Mod [template = constants.%.2] {}
+// CHECK:STDOUT:     %Mod.ref: type = name_ref Mod, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %ModAssign.decl: invalid = interface_decl @ModAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %ModAssign.ref: type = name_ref ModAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Mod {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Mod> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @ModAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in ModAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as Mod {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as ModAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 290 - 0
toolchain/check/testdata/operators/overloaded/mul.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Mul {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface MulAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Mul {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.MulAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a * b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a *= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Mul [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Mul, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Mul> = assoc_entity element0, @Mul.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @MulAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @MulAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in MulAssign> = assoc_entity element0, @MulAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Mul = %Mul.decl
+// CHECK:STDOUT:     .MulAssign = %MulAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Mul.decl: type = interface_decl @Mul [template = constants.%.1] {}
+// CHECK:STDOUT:   %MulAssign.decl: type = interface_decl @MulAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Mul {
+// CHECK:STDOUT:   %Self: Mul = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: Mul = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: Mul = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: Mul = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in Mul> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @MulAssign {
+// CHECK:STDOUT:   %Self: MulAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: MulAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: MulAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in MulAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@Mul.%self.loc5_9.2: Self](@Mul.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @MulAssign.%self.loc8_14.3: Self*](@MulAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Mul [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @MulAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @Mul, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in Mul> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @MulAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in MulAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Mul.decl: invalid = interface_decl @Mul [template = constants.%.2] {}
+// CHECK:STDOUT:     %Mul.ref: type = name_ref Mul, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %MulAssign.decl: invalid = interface_decl @MulAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %MulAssign.ref: type = name_ref MulAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Mul {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Mul> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @MulAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in MulAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as Mul {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as MulAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 163 - 0
toolchain/check/testdata/operators/overloaded/negate.carbon

@@ -0,0 +1,163 @@
+// 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
+
+// This file was generated from unary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Negate {
+  fn Op[self: Self]() -> Self;
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Negate {
+  fn Op[self: C]() -> C {
+    return {};
+  }
+}
+
+fn TestOp(a: C) -> C {
+  return -a;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Negate [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Negate, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Negate> = assoc_entity element0, @Negate.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Negate = %Negate.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Negate.decl: type = interface_decl @Negate [template = constants.%.1] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Negate {
+// CHECK:STDOUT:   %Self: Negate = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: Negate = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_26: Negate = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_26.1: type = facet_type_access %Self.ref.loc5_26 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_26.2: type = converted %Self.ref.loc5_26, %.loc5_26.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_30: <associated <function> in Negate> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_30
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op[@Negate.%self.loc5_9.2: Self]() -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Negate [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.%Op) [template]
+// CHECK:STDOUT:   %.7: type = assoc_entity_type @Negate, <function> [template]
+// CHECK:STDOUT:   %.8: <associated <function> in Negate> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Negate.decl: invalid = interface_decl @Negate [template = constants.%.2] {}
+// CHECK:STDOUT:     %Negate.ref: type = name_ref Negate, %import_ref [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc14_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc14_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc14_11.1
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Negate {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Negate> = import_ref ir1, inst+15, used [template = constants.%.8]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+13, used [template = imports.%Op]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: C as Negate {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_23: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.%self.loc9_9.2: C]() -> @impl.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self]() -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+13, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%Op]
+// CHECK:STDOUT:   %.loc15_10.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc14: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc15_10.2: init C = call %.loc15_10.1(%a.ref) to %.loc14
+// CHECK:STDOUT:   return %.loc15_10.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 521 - 0
toolchain/check/testdata/operators/overloaded/ordered.carbon

@@ -0,0 +1,521 @@
+// 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
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Ordered {
+  // TODO: fn Compare
+  fn Less[self: Self](other: Self) -> bool;
+  fn LessOrEquivalent[self: Self](other: Self) -> bool;
+  fn Greater[self: Self](other: Self) -> bool;
+  fn GreaterOrEquivalent[self: Self](other: Self) -> bool;
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Ordered {
+  fn Less[self: C](other: C) -> bool;
+  fn LessOrEquivalent[self: C](other: C) -> bool;
+  fn Greater[self: C](other: C) -> bool;
+  fn GreaterOrEquivalent[self: C](other: C) -> bool;
+}
+
+fn TestLess(a: C, b: C) -> bool {
+  return a < b;
+}
+
+fn TestLessEqual(a: C, b: C) -> bool {
+  return a <= b;
+}
+
+fn TestGreater(a: C, b: C) -> bool {
+  return a > b;
+}
+
+fn TestGreaterEqual(a: C, b: C) -> bool {
+  return a >= b;
+}
+
+// --- fail_no_impl.carbon
+
+package FailNoImpl api;
+
+import Core;
+
+class D {};
+
+fn TestLess(a: D, b: D) -> bool {
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Ordered in type D that does not implement that interface.
+  // CHECK:STDERR:   return a < b;
+  // CHECK:STDERR:          ^~~~~
+  return a < b;
+}
+
+fn TestLessEqual(a: D, b: D) -> bool {
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Ordered in type D that does not implement that interface.
+  // CHECK:STDERR:   return a <= b;
+  // CHECK:STDERR:          ^~~~~~
+  return a <= b;
+}
+
+fn TestGreater(a: D, b: D) -> bool {
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Ordered in type D that does not implement that interface.
+  // CHECK:STDERR:   return a > b;
+  // CHECK:STDERR:          ^~~~~
+  return a > b;
+}
+
+fn TestGreaterEqual(a: D, b: D) -> bool {
+  // CHECK:STDERR: fail_no_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface Ordered in type D that does not implement that interface.
+  // CHECK:STDERR:   return a >= b;
+  // CHECK:STDERR:          ^~~~~~
+  return a >= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Ordered [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Ordered, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Ordered> = assoc_entity element0, @Ordered.%Less [template]
+// CHECK:STDOUT:   %.4: <associated <function> in Ordered> = assoc_entity element1, @Ordered.%LessOrEquivalent [template]
+// CHECK:STDOUT:   %.5: <associated <function> in Ordered> = assoc_entity element2, @Ordered.%Greater [template]
+// CHECK:STDOUT:   %.6: <associated <function> in Ordered> = assoc_entity element3, @Ordered.%GreaterOrEquivalent [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Ordered = %Ordered.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Ordered.decl: type = interface_decl @Ordered [template = constants.%.1] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Ordered {
+// CHECK:STDOUT:   %Self: Ordered = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Less: <function> = fn_decl @Less [template] {
+// CHECK:STDOUT:     %Self.ref.loc6_17: Ordered = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc6_17.1: type = facet_type_access %Self.ref.loc6_17 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc6_17.2: type = converted %Self.ref.loc6_17, %.loc6_17.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc6_11.1: Self = param self
+// CHECK:STDOUT:     %self.loc6_11.2: Self = bind_name self, %self.loc6_11.1
+// CHECK:STDOUT:     %Self.ref.loc6_30: Ordered = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc6_30.1: type = facet_type_access %Self.ref.loc6_30 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc6_30.2: type = converted %Self.ref.loc6_30, %.loc6_30.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc6_23.1: Self = param other
+// CHECK:STDOUT:     %other.loc6_23.2: Self = bind_name other, %other.loc6_23.1
+// CHECK:STDOUT:     %return.var.loc6: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc6_43: <associated <function> in Ordered> = assoc_entity element0, %Less [template = constants.%.3]
+// CHECK:STDOUT:   %LessOrEquivalent: <function> = fn_decl @LessOrEquivalent [template] {
+// CHECK:STDOUT:     %Self.ref.loc7_29: Ordered = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc7_29.1: type = facet_type_access %Self.ref.loc7_29 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc7_29.2: type = converted %Self.ref.loc7_29, %.loc7_29.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc7_23.1: Self = param self
+// CHECK:STDOUT:     %self.loc7_23.2: Self = bind_name self, %self.loc7_23.1
+// CHECK:STDOUT:     %Self.ref.loc7_42: Ordered = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc7_42.1: type = facet_type_access %Self.ref.loc7_42 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc7_42.2: type = converted %Self.ref.loc7_42, %.loc7_42.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc7_35.1: Self = param other
+// CHECK:STDOUT:     %other.loc7_35.2: Self = bind_name other, %other.loc7_35.1
+// CHECK:STDOUT:     %return.var.loc7: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc7_55: <associated <function> in Ordered> = assoc_entity element1, %LessOrEquivalent [template = constants.%.4]
+// CHECK:STDOUT:   %Greater: <function> = fn_decl @Greater [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: Ordered = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20.2: type = converted %Self.ref.loc8_20, %.loc8_20.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc8_14.1: Self = param self
+// CHECK:STDOUT:     %self.loc8_14.2: Self = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %Self.ref.loc8_33: Ordered = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_33.1: type = facet_type_access %Self.ref.loc8_33 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_33.2: type = converted %Self.ref.loc8_33, %.loc8_33.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_26.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_26.2: Self = bind_name other, %other.loc8_26.1
+// CHECK:STDOUT:     %return.var.loc8: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_46: <associated <function> in Ordered> = assoc_entity element2, %Greater [template = constants.%.5]
+// CHECK:STDOUT:   %GreaterOrEquivalent: <function> = fn_decl @GreaterOrEquivalent [template] {
+// CHECK:STDOUT:     %Self.ref.loc9_32: Ordered = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc9_32.1: type = facet_type_access %Self.ref.loc9_32 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc9_32.2: type = converted %Self.ref.loc9_32, %.loc9_32.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc9_26.1: Self = param self
+// CHECK:STDOUT:     %self.loc9_26.2: Self = bind_name self, %self.loc9_26.1
+// CHECK:STDOUT:     %Self.ref.loc9_45: Ordered = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc9_45.1: type = facet_type_access %Self.ref.loc9_45 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc9_45.2: type = converted %Self.ref.loc9_45, %.loc9_45.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc9_38.1: Self = param other
+// CHECK:STDOUT:     %other.loc9_38.2: Self = bind_name other, %other.loc9_38.1
+// CHECK:STDOUT:     %return.var.loc9: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc9_58: <associated <function> in Ordered> = assoc_entity element3, %GreaterOrEquivalent [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Less = %.loc6_43
+// CHECK:STDOUT:   .LessOrEquivalent = %.loc7_55
+// CHECK:STDOUT:   .Greater = %.loc8_46
+// CHECK:STDOUT:   .GreaterOrEquivalent = %.loc9_58
+// CHECK:STDOUT:   witness = (%Less, %LessOrEquivalent, %Greater, %GreaterOrEquivalent)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Less[@Ordered.%self.loc6_11.2: Self](@Ordered.%other.loc6_23.2: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @LessOrEquivalent[@Ordered.%self.loc7_23.2: Self](@Ordered.%other.loc7_35.2: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Greater[@Ordered.%self.loc8_14.2: Self](@Ordered.%other.loc8_26.2: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @GreaterOrEquivalent[@Ordered.%self.loc9_26.2: Self](@Ordered.%other.loc9_38.2: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Ordered [template]
+// CHECK:STDOUT:   %.3: <witness> = interface_witness (@impl.%Less, @impl.%LessOrEquivalent, @impl.%Greater, @impl.%GreaterOrEquivalent) [template]
+// CHECK:STDOUT:   %.4: type = tuple_type () [template]
+// CHECK:STDOUT:   %.5: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @Ordered, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in Ordered> = assoc_entity element0, @TestLess.%import_ref.2 [template]
+// CHECK:STDOUT:   %.8: <associated <function> in Ordered> = assoc_entity element1, @TestLessEqual.%import_ref.2 [template]
+// CHECK:STDOUT:   %.9: <associated <function> in Ordered> = assoc_entity element2, @TestGreater.%import_ref.2 [template]
+// CHECK:STDOUT:   %.10: <associated <function> in Ordered> = assoc_entity element3, @TestGreaterEqual.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestLess = %TestLess
+// CHECK:STDOUT:     .TestLessEqual = %TestLessEqual
+// CHECK:STDOUT:     .TestGreater = %TestGreater
+// CHECK:STDOUT:     .TestGreaterEqual = %TestGreaterEqual
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Ordered.decl: invalid = interface_decl @Ordered [template = constants.%.2] {}
+// CHECK:STDOUT:     %Ordered.ref: type = name_ref Ordered, %import_ref [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestLess: <function> = fn_decl @TestLess [template] {
+// CHECK:STDOUT:     %C.ref.loc15_16: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc15_13.1: C = param a
+// CHECK:STDOUT:     @TestLess.%a: C = bind_name a, %a.loc15_13.1
+// CHECK:STDOUT:     %C.ref.loc15_22: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc15_19.1: C = param b
+// CHECK:STDOUT:     @TestLess.%b: C = bind_name b, %b.loc15_19.1
+// CHECK:STDOUT:     %return.var.loc15: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestLessEqual: <function> = fn_decl @TestLessEqual [template] {
+// CHECK:STDOUT:     %C.ref.loc19_21: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc19_18.1: C = param a
+// CHECK:STDOUT:     @TestLessEqual.%a: C = bind_name a, %a.loc19_18.1
+// CHECK:STDOUT:     %C.ref.loc19_27: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc19_24.1: C = param b
+// CHECK:STDOUT:     @TestLessEqual.%b: C = bind_name b, %b.loc19_24.1
+// CHECK:STDOUT:     %return.var.loc19: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestGreater: <function> = fn_decl @TestGreater [template] {
+// CHECK:STDOUT:     %C.ref.loc23_19: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc23_16.1: C = param a
+// CHECK:STDOUT:     @TestGreater.%a: C = bind_name a, %a.loc23_16.1
+// CHECK:STDOUT:     %C.ref.loc23_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc23_22.1: C = param b
+// CHECK:STDOUT:     @TestGreater.%b: C = bind_name b, %b.loc23_22.1
+// CHECK:STDOUT:     %return.var.loc23: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestGreaterEqual: <function> = fn_decl @TestGreaterEqual [template] {
+// CHECK:STDOUT:     %C.ref.loc27_24: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc27_21.1: C = param a
+// CHECK:STDOUT:     @TestGreaterEqual.%a: C = bind_name a, %a.loc27_21.1
+// CHECK:STDOUT:     %C.ref.loc27_30: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc27_27.1: C = param b
+// CHECK:STDOUT:     @TestGreaterEqual.%b: C = bind_name b, %b.loc27_27.1
+// CHECK:STDOUT:     %return.var.loc27: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Ordered {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Ordered> = import_ref ir1, inst+17, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <associated <function> in Ordered> = import_ref ir1, inst+31, used [template = constants.%.8]
+// CHECK:STDOUT:   %import_ref.4: <associated <function> in Ordered> = import_ref ir1, inst+59, used [template = constants.%.10]
+// CHECK:STDOUT:   %import_ref.5: <associated <function> in Ordered> = import_ref ir1, inst+45, used [template = constants.%.9]
+// CHECK:STDOUT:   %import_ref.6: <function> = import_ref ir1, inst+15, used [template = imports.%Less]
+// CHECK:STDOUT:   %import_ref.7: <function> = import_ref ir1, inst+30, used [template = imports.%LessOrEquivalent]
+// CHECK:STDOUT:   %import_ref.8: <function> = import_ref ir1, inst+44, used [template = imports.%Greater]
+// CHECK:STDOUT:   %import_ref.9: <function> = import_ref ir1, inst+58, used [template = imports.%GreaterOrEquivalent]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Less = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   .LessOrEquivalent = %import_ref.3
+// CHECK:STDOUT:   .GreaterOrEquivalent = %import_ref.4
+// CHECK:STDOUT:   .Greater = %import_ref.5
+// CHECK:STDOUT:   witness = (%import_ref.6, %import_ref.7, %import_ref.8, %import_ref.9)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: C as Ordered {
+// CHECK:STDOUT:   %Less: <function> = fn_decl @Less.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_17: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_11.1: C = param self
+// CHECK:STDOUT:     %self.loc9_11.2: C = bind_name self, %self.loc9_11.1
+// CHECK:STDOUT:     %C.ref.loc9_27: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_20.1: C = param other
+// CHECK:STDOUT:     %other.loc9_20.2: C = bind_name other, %other.loc9_20.1
+// CHECK:STDOUT:     %return.var.loc9: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %LessOrEquivalent: <function> = fn_decl @LessOrEquivalent.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc10_29: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc10_23.1: C = param self
+// CHECK:STDOUT:     %self.loc10_23.2: C = bind_name self, %self.loc10_23.1
+// CHECK:STDOUT:     %C.ref.loc10_39: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc10_32.1: C = param other
+// CHECK:STDOUT:     %other.loc10_32.2: C = bind_name other, %other.loc10_32.1
+// CHECK:STDOUT:     %return.var.loc10: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Greater: <function> = fn_decl @Greater.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc11_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc11_14.1: C = param self
+// CHECK:STDOUT:     %self.loc11_14.2: C = bind_name self, %self.loc11_14.1
+// CHECK:STDOUT:     %C.ref.loc11_30: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc11_23.1: C = param other
+// CHECK:STDOUT:     %other.loc11_23.2: C = bind_name other, %other.loc11_23.1
+// CHECK:STDOUT:     %return.var.loc11: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %GreaterOrEquivalent: <function> = fn_decl @GreaterOrEquivalent.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc12_32: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc12_26.1: C = param self
+// CHECK:STDOUT:     %self.loc12_26.2: C = bind_name self, %self.loc12_26.1
+// CHECK:STDOUT:     %C.ref.loc12_42: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc12_35.1: C = param other
+// CHECK:STDOUT:     %other.loc12_35.2: C = bind_name other, %other.loc12_35.1
+// CHECK:STDOUT:     %return.var.loc12: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Less, %LessOrEquivalent, %Greater, %GreaterOrEquivalent) [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Less = %Less
+// CHECK:STDOUT:   .LessOrEquivalent = %LessOrEquivalent
+// CHECK:STDOUT:   .Greater = %Greater
+// CHECK:STDOUT:   .GreaterOrEquivalent = %GreaterOrEquivalent
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Less.1[@impl.%self.loc9_11.2: C](@impl.%other.loc9_20.2: C) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @LessOrEquivalent.1[@impl.%self.loc10_23.2: C](@impl.%other.loc10_32.2: C) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Greater.1[@impl.%self.loc11_14.2: C](@impl.%other.loc11_23.2: C) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @GreaterOrEquivalent.1[@impl.%self.loc12_26.2: C](@impl.%other.loc12_35.2: C) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Less.2[%self: Self](%other: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @LessOrEquivalent.2[%self: Self](%other: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Greater.2[%self: Self](%other: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @GreaterOrEquivalent.2[%self: Self](%other: Self) -> bool;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestLess(%a: C, %b: C) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+15, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%Less]
+// CHECK:STDOUT:   %.loc16_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc16_12.2: init bool = call %.loc16_12.1(%a.ref, %b.ref)
+// CHECK:STDOUT:   %.loc16_15: bool = value_of_initializer %.loc16_12.2
+// CHECK:STDOUT:   %.loc16_12.3: bool = converted %.loc16_12.2, %.loc16_15
+// CHECK:STDOUT:   return %.loc16_12.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestLessEqual(%a: C, %b: C) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element1 [template = @impl.%LessOrEquivalent]
+// CHECK:STDOUT:   %.loc20_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc20_12.2: init bool = call %.loc20_12.1(%a.ref, %b.ref)
+// CHECK:STDOUT:   %.loc20_16: bool = value_of_initializer %.loc20_12.2
+// CHECK:STDOUT:   %.loc20_12.3: bool = converted %.loc20_12.2, %.loc20_16
+// CHECK:STDOUT:   return %.loc20_12.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestGreater(%a: C, %b: C) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+44, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element2 [template = @impl.%Greater]
+// CHECK:STDOUT:   %.loc24_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc24_12.2: init bool = call %.loc24_12.1(%a.ref, %b.ref)
+// CHECK:STDOUT:   %.loc24_15: bool = value_of_initializer %.loc24_12.2
+// CHECK:STDOUT:   %.loc24_12.3: bool = converted %.loc24_12.2, %.loc24_15
+// CHECK:STDOUT:   return %.loc24_12.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestGreaterEqual(%a: C, %b: C) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+58, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.%.1, element3 [template = @impl.%GreaterOrEquivalent]
+// CHECK:STDOUT:   %.loc28_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc28_12.2: init bool = call %.loc28_12.1(%a.ref, %b.ref)
+// CHECK:STDOUT:   %.loc28_16: bool = value_of_initializer %.loc28_12.2
+// CHECK:STDOUT:   %.loc28_12.3: bool = converted %.loc28_12.2, %.loc28_16
+// CHECK:STDOUT:   return %.loc28_12.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_no_impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %.3: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.4: type = interface_type @Ordered [template]
+// CHECK:STDOUT:   %.5: type = assoc_entity_type @Ordered, <function> [template]
+// CHECK:STDOUT:   %.6: <associated <function> in Ordered> = assoc_entity element0, @TestLess.%import_ref.2 [template]
+// CHECK:STDOUT:   %.7: <associated <function> in Ordered> = assoc_entity element1, @TestLessEqual.%import_ref.2 [template]
+// CHECK:STDOUT:   %.8: <associated <function> in Ordered> = assoc_entity element2, @TestGreater.%import_ref.2 [template]
+// CHECK:STDOUT:   %.9: <associated <function> in Ordered> = assoc_entity element3, @TestGreaterEqual.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .D = %D.decl
+// CHECK:STDOUT:     .TestLess = %TestLess
+// CHECK:STDOUT:     .TestLessEqual = %TestLessEqual
+// CHECK:STDOUT:     .TestGreater = %TestGreater
+// CHECK:STDOUT:     .TestGreaterEqual = %TestGreaterEqual
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:   %TestLess: <function> = fn_decl @TestLess [template] {
+// CHECK:STDOUT:     %D.ref.loc8_16: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %a.loc8_13.1: D = param a
+// CHECK:STDOUT:     @TestLess.%a: D = bind_name a, %a.loc8_13.1
+// CHECK:STDOUT:     %D.ref.loc8_22: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc8_19.1: D = param b
+// CHECK:STDOUT:     @TestLess.%b: D = bind_name b, %b.loc8_19.1
+// CHECK:STDOUT:     %return.var.loc8: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestLessEqual: <function> = fn_decl @TestLessEqual [template] {
+// CHECK:STDOUT:     %D.ref.loc15_21: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %a.loc15_18.1: D = param a
+// CHECK:STDOUT:     @TestLessEqual.%a: D = bind_name a, %a.loc15_18.1
+// CHECK:STDOUT:     %D.ref.loc15_27: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc15_24.1: D = param b
+// CHECK:STDOUT:     @TestLessEqual.%b: D = bind_name b, %b.loc15_24.1
+// CHECK:STDOUT:     %return.var.loc15: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestGreater: <function> = fn_decl @TestGreater [template] {
+// CHECK:STDOUT:     %D.ref.loc22_19: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %a.loc22_16.1: D = param a
+// CHECK:STDOUT:     @TestGreater.%a: D = bind_name a, %a.loc22_16.1
+// CHECK:STDOUT:     %D.ref.loc22_25: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc22_22.1: D = param b
+// CHECK:STDOUT:     @TestGreater.%b: D = bind_name b, %b.loc22_22.1
+// CHECK:STDOUT:     %return.var.loc22: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestGreaterEqual: <function> = fn_decl @TestGreaterEqual [template] {
+// CHECK:STDOUT:     %D.ref.loc29_24: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %a.loc29_21.1: D = param a
+// CHECK:STDOUT:     @TestGreaterEqual.%a: D = bind_name a, %a.loc29_21.1
+// CHECK:STDOUT:     %D.ref.loc29_30: type = name_ref D, %D.decl [template = constants.%D]
+// CHECK:STDOUT:     %b.loc29_27.1: D = param b
+// CHECK:STDOUT:     @TestGreaterEqual.%b: D = bind_name b, %b.loc29_27.1
+// CHECK:STDOUT:     %return.var.loc29: ref bool = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Ordered {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Ordered> = import_ref ir1, inst+17, used [template = constants.%.6]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <associated <function> in Ordered> = import_ref ir1, inst+31, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.4: <associated <function> in Ordered> = import_ref ir1, inst+59, used [template = constants.%.9]
+// CHECK:STDOUT:   %import_ref.5: <associated <function> in Ordered> = import_ref ir1, inst+45, used [template = constants.%.8]
+// CHECK:STDOUT:   %import_ref.6 = import_ref ir1, inst+15, unused
+// CHECK:STDOUT:   %import_ref.7 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:   %import_ref.8 = import_ref ir1, inst+44, unused
+// CHECK:STDOUT:   %import_ref.9 = import_ref ir1, inst+58, unused
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Less = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   .LessOrEquivalent = %import_ref.3
+// CHECK:STDOUT:   .GreaterOrEquivalent = %import_ref.4
+// CHECK:STDOUT:   .Greater = %import_ref.5
+// CHECK:STDOUT:   witness = (%import_ref.6, %import_ref.7, %import_ref.8, %import_ref.9)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%D
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestLess(%a: D, %b: D) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: D = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.4]
+// CHECK:STDOUT:   %Ordered.decl: invalid = interface_decl @Ordered [template = constants.%.4] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+15, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestLessEqual(%a: D, %b: D) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: D = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.4]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+30, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestGreater(%a: D, %b: D) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: D = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.4]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+44, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestGreaterEqual(%a: D, %b: D) -> bool {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: D = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: D = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.4]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+58, unused
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 290 - 0
toolchain/check/testdata/operators/overloaded/right_shift.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface RightShift {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface RightShiftAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.RightShift {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.RightShiftAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a >> b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a >>= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @RightShift [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @RightShift, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in RightShift> = assoc_entity element0, @RightShift.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @RightShiftAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @RightShiftAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in RightShiftAssign> = assoc_entity element0, @RightShiftAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .RightShift = %RightShift.decl
+// CHECK:STDOUT:     .RightShiftAssign = %RightShiftAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %RightShift.decl: type = interface_decl @RightShift [template = constants.%.1] {}
+// CHECK:STDOUT:   %RightShiftAssign.decl: type = interface_decl @RightShiftAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @RightShift {
+// CHECK:STDOUT:   %Self: RightShift = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: RightShift = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: RightShift = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: RightShift = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in RightShift> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @RightShiftAssign {
+// CHECK:STDOUT:   %Self: RightShiftAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: RightShiftAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: RightShiftAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in RightShiftAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@RightShift.%self.loc5_9.2: Self](@RightShift.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @RightShiftAssign.%self.loc8_14.3: Self*](@RightShiftAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @RightShift [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @RightShiftAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @RightShift, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in RightShift> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @RightShiftAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in RightShiftAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %RightShift.decl: invalid = interface_decl @RightShift [template = constants.%.2] {}
+// CHECK:STDOUT:     %RightShift.ref: type = name_ref RightShift, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %RightShiftAssign.decl: invalid = interface_decl @RightShiftAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %RightShiftAssign.ref: type = name_ref RightShiftAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @RightShift {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in RightShift> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @RightShiftAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in RightShiftAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as RightShift {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as RightShiftAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 290 - 0
toolchain/check/testdata/operators/overloaded/sub.carbon

@@ -0,0 +1,290 @@
+// 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
+
+// This file was generated from binary_op.carbon.tmpl. Run make_tests.sh to regenerate.
+
+// --- prelude.carbon
+
+package Core api;
+
+interface Sub {
+  fn Op[self: Self](other: Self) -> Self;
+}
+interface SubAssign {
+  fn Op[addr self: Self*](other: Self);
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.Sub {
+  fn Op[self: C](other: C) -> C {
+    return {};
+  }
+}
+impl C as Core.SubAssign {
+  fn Op[addr self: C*](other: C) {}
+}
+
+fn TestOp(a: C, b: C) -> C {
+  return a - b;
+}
+
+fn TestAssign(a: C*, b: C) {
+  *a -= b;
+}
+
+// CHECK:STDOUT: --- prelude.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Sub [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @Sub, <function> [template]
+// CHECK:STDOUT:   %.3: <associated <function> in Sub> = assoc_entity element0, @Sub.%Op [template]
+// CHECK:STDOUT:   %.4: type = interface_type @SubAssign [template]
+// CHECK:STDOUT:   %.5: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type @SubAssign, <function> [template]
+// CHECK:STDOUT:   %.7: <associated <function> in SubAssign> = assoc_entity element0, @SubAssign.%Op [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Sub = %Sub.decl
+// CHECK:STDOUT:     .SubAssign = %SubAssign.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Sub.decl: type = interface_decl @Sub [template = constants.%.1] {}
+// CHECK:STDOUT:   %SubAssign.decl: type = interface_decl @SubAssign [template = constants.%.4] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Sub {
+// CHECK:STDOUT:   %Self: Sub = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %Self.ref.loc5_15: Sub = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self]
+// CHECK:STDOUT:     %self.loc5_9.1: Self = param self
+// CHECK:STDOUT:     %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1
+// CHECK:STDOUT:     %Self.ref.loc5_28: Sub = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc5_21.1: Self = param other
+// CHECK:STDOUT:     %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1
+// CHECK:STDOUT:     %Self.ref.loc5_37: Sub = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self]
+// CHECK:STDOUT:     %return.var: ref Self = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_41: <associated <function> in Sub> = assoc_entity element0, %Op [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc5_41
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @SubAssign {
+// CHECK:STDOUT:   %Self: SubAssign = bind_symbolic_name Self [symbolic]
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.2 [template] {
+// CHECK:STDOUT:     %Self.ref.loc8_20: SubAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.1: type = facet_type_access %Self.ref.loc8_20 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_20: type = converted %Self.ref.loc8_20, %.loc8_24.1 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_24.2: type = ptr_type Self [symbolic = constants.%.5]
+// CHECK:STDOUT:     %self.loc8_14.1: Self* = param self
+// CHECK:STDOUT:     %self.loc8_14.3: Self* = bind_name self, %self.loc8_14.1
+// CHECK:STDOUT:     %.loc8_9: Self* = addr_pattern %self.loc8_14.3
+// CHECK:STDOUT:     %Self.ref.loc8_34: SubAssign = name_ref Self, %Self [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.1: type = facet_type_access %Self.ref.loc8_34 [symbolic = %Self]
+// CHECK:STDOUT:     %.loc8_34.2: type = converted %Self.ref.loc8_34, %.loc8_34.1 [symbolic = %Self]
+// CHECK:STDOUT:     %other.loc8_27.1: Self = param other
+// CHECK:STDOUT:     %other.loc8_27.2: Self = bind_name other, %other.loc8_27.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_39: <associated <function> in SubAssign> = assoc_entity element0, %Op [template = constants.%.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Op = %.loc8_39
+// CHECK:STDOUT:   witness = (%Op)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@Sub.%self.loc5_9.2: Self](@Sub.%other.loc5_21.2: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[addr @SubAssign.%self.loc8_14.3: Self*](@SubAssign.%other.loc8_27.2: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- user.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = interface_type @Sub [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type {} [template]
+// CHECK:STDOUT:   %.5: C = struct_value () [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness (@impl.1.%Op) [template]
+// CHECK:STDOUT:   %.7: type = interface_type @SubAssign [template]
+// CHECK:STDOUT:   %.8: type = ptr_type C [template]
+// CHECK:STDOUT:   %.9: type = ptr_type Self [symbolic]
+// CHECK:STDOUT:   %.10: <witness> = interface_witness (@impl.2.%Op) [template]
+// CHECK:STDOUT:   %.11: type = assoc_entity_type @Sub, <function> [template]
+// CHECK:STDOUT:   %.12: <associated <function> in Sub> = assoc_entity element0, @TestOp.%import_ref.2 [template]
+// CHECK:STDOUT:   %.13: type = assoc_entity_type @SubAssign, <function> [template]
+// CHECK:STDOUT:   %.14: <associated <function> in SubAssign> = assoc_entity element0, @TestAssign.%import_ref.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .TestOp = %TestOp
+// CHECK:STDOUT:     .TestAssign = %TestAssign
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:     %Sub.decl: invalid = interface_decl @Sub [template = constants.%.2] {}
+// CHECK:STDOUT:     %Sub.ref: type = name_ref Sub, %import_ref.1 [template = constants.%.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
+// CHECK:STDOUT:     %import_ref.2: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:     %SubAssign.decl: invalid = interface_decl @SubAssign [template = constants.%.7] {}
+// CHECK:STDOUT:     %SubAssign.ref: type = name_ref SubAssign, %import_ref.2 [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestOp: <function> = fn_decl @TestOp [template] {
+// CHECK:STDOUT:     %C.ref.loc17_14: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %a.loc17_11.1: C = param a
+// CHECK:STDOUT:     @TestOp.%a: C = bind_name a, %a.loc17_11.1
+// CHECK:STDOUT:     %C.ref.loc17_20: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc17_17.1: C = param b
+// CHECK:STDOUT:     @TestOp.%b: C = bind_name b, %b.loc17_17.1
+// CHECK:STDOUT:     %C.ref.loc17_26: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     @TestOp.%return: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %TestAssign: <function> = fn_decl @TestAssign [template] {
+// CHECK:STDOUT:     %C.ref.loc21_18: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %a.loc21_15.1: C* = param a
+// CHECK:STDOUT:     @TestAssign.%a: C* = bind_name a, %a.loc21_15.1
+// CHECK:STDOUT:     %C.ref.loc21_25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %b.loc21_22.1: C = param b
+// CHECK:STDOUT:     @TestAssign.%b: C = bind_name b, %b.loc21_22.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Sub {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in Sub> = import_ref ir1, inst+20, used [template = constants.%.12]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+18, used [template = imports.%Op.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @SubAssign {
+// CHECK:STDOUT:   %import_ref.1: <associated <function> in SubAssign> = import_ref ir1, inst+40, used [template = constants.%.14]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+24, unused
+// CHECK:STDOUT:   %import_ref.3: <function> = import_ref ir1, inst+38, used [template = imports.%Op.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %import_ref.1
+// CHECK:STDOUT:   .Self = %import_ref.2
+// CHECK:STDOUT:   witness = (%import_ref.3)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: C as Sub {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.1 [template] {
+// CHECK:STDOUT:     %C.ref.loc9_15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %self.loc9_9.1: C = param self
+// CHECK:STDOUT:     %self.loc9_9.2: C = bind_name self, %self.loc9_9.1
+// CHECK:STDOUT:     %C.ref.loc9_25: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc9_18.1: C = param other
+// CHECK:STDOUT:     %other.loc9_18.2: C = bind_name other, %other.loc9_18.1
+// CHECK:STDOUT:     %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %return.var: ref C = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: C as SubAssign {
+// CHECK:STDOUT:   %Op: <function> = fn_decl @Op.3 [template] {
+// CHECK:STDOUT:     %C.ref.loc14_20: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14_21: type = ptr_type C [template = constants.%.8]
+// CHECK:STDOUT:     %self.loc14_14.1: C* = param self
+// CHECK:STDOUT:     %self.loc14_14.3: C* = bind_name self, %self.loc14_14.1
+// CHECK:STDOUT:     %.loc14_9: C* = addr_pattern %self.loc14_14.3
+// CHECK:STDOUT:     %C.ref.loc14_31: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %other.loc14_24.1: C = param other
+// CHECK:STDOUT:     %other.loc14_24.2: C = bind_name other, %other.loc14_24.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%Op) [template = constants.%.10]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Op = %Op
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc10_13.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
+// CHECK:STDOUT:   %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
+// CHECK:STDOUT:   return %.loc10_13.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.3[addr @impl.2.%self.loc14_14.3: C*](@impl.2.%other.loc14_24.2: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Op.4[addr %self: Self*](%other: Self);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestOp(%a: C, %b: C) -> %return: C {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C = name_ref a, %a
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.2]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+18, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op]
+// CHECK:STDOUT:   %.loc18_12.1: <bound method> = bound_method %a.ref, %.1
+// CHECK:STDOUT:   %.loc17: ref C = splice_block %return {}
+// CHECK:STDOUT:   %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17
+// CHECK:STDOUT:   return %.loc18_12.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %a.ref: C* = name_ref a, %a
+// CHECK:STDOUT:   %.loc22_3.1: ref C = deref %a.ref
+// CHECK:STDOUT:   %b.ref: C = name_ref b, %b
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+22, used [template = constants.%.7]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+38, unused
+// CHECK:STDOUT:   %.1: <function> = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op]
+// CHECK:STDOUT:   %.loc22_6.1: <bound method> = bound_method %.loc22_3.1, %.1
+// CHECK:STDOUT:   %.loc22_3.2: C* = addr_of %.loc22_3.1
+// CHECK:STDOUT:   %.loc22_6.2: init () = call %.loc22_6.1(%.loc22_3.2, %b.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 33 - 0
toolchain/check/testdata/operators/overloaded/unary_op.carbon.tmpl

@@ -0,0 +1,33 @@
+// 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
+
+HEADER
+
+// --- prelude.carbon
+
+package Core api;
+
+interface INTERFACE {
+  fn Op[self: Self]() -> Self;
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.INTERFACE {
+  fn Op[self: C]() -> C {
+    return {};
+  }
+}
+
+fn TestOp(a: C) -> C {
+  return OPa;
+}

+ 32 - 0
toolchain/check/testdata/operators/overloaded/unary_stmt.carbon.tmpl

@@ -0,0 +1,32 @@
+// 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
+
+HEADER
+
+// --- prelude.carbon
+
+package Core api;
+
+interface INTERFACE {
+  fn Op[addr self: Self*]();
+}
+
+// --- user.carbon
+
+package User api;
+
+import Core;
+
+class C {};
+
+impl C as Core.INTERFACE {
+  fn Op[addr self: C*]();
+}
+
+fn TestOp() {
+  var c: C = {};
+  OPc;
+}

+ 0 - 3
toolchain/parse/node_ids.h

@@ -65,7 +65,6 @@ struct NodeIdInCategory : public NodeId {
 using AnyDeclId = NodeIdInCategory<NodeCategory::Decl>;
 using AnyExprId = NodeIdInCategory<NodeCategory::Expr>;
 using AnyImplAsId = NodeIdInCategory<NodeCategory::ImplAs>;
-using AnyMemberNameId = NodeIdInCategory<NodeCategory::MemberName>;
 using AnyMemberNameOrMemberExprId =
     NodeIdInCategory<NodeCategory::MemberName | NodeCategory::MemberExpr>;
 using AnyModifierId = NodeIdInCategory<NodeCategory::Modifier>;
@@ -94,8 +93,6 @@ using AnyImplDeclId = NodeIdOneOf<ImplDeclId, ImplDefinitionStartId>;
 using AnyInterfaceDeclId =
     NodeIdOneOf<InterfaceDeclId, InterfaceDefinitionStartId>;
 using AnyNamespaceId = NodeIdOneOf<NamespaceId, ImportDirectiveId>;
-using AnyMemberAccessExprId =
-    NodeIdOneOf<MemberAccessExprId, PointerMemberAccessExprId>;
 using AnyPointerDeferenceExprId =
     NodeIdOneOf<PrefixOperatorStarId, PointerMemberAccessExprId>;
 

+ 5 - 4
toolchain/sem_ir/typed_insts.h

@@ -273,8 +273,7 @@ struct BoolLiteral {
 // `self` parameter, such as `object.MethodName`.
 struct BoundMethod {
   static constexpr auto Kind =
-      InstKind::BoundMethod.Define<Parse::AnyMemberAccessExprId>(
-          "bound_method");
+      InstKind::BoundMethod.Define<Parse::NodeId>("bound_method");
 
   TypeId type_id;
   // The object argument in the bound method, which will be used to initialize
@@ -335,8 +334,10 @@ struct Builtin {
 };
 
 struct Call {
-  static constexpr auto Kind =
-      InstKind::Call.Define<Parse::CallExprStartId>("call");
+  // For a syntactic call, the parse node will be a CallExprStartId. However,
+  // calls can arise from other syntaxes, such as operators and implicit
+  // conversions.
+  static constexpr auto Kind = InstKind::Call.Define<Parse::NodeId>("call");
 
   TypeId type_id;
   InstId callee_id;