فهرست منبع

Implement initialization for C++ thunk parameters. (#5938)

When initializing a C++ thunk parameter:

* If we have an initializing expression, materialize a temporary and
pass its address.
* If we have a reference expression, pass its address directly.
* If we have a value expression with a pointer value representation,
pass the pointer.
* Otherwise, create a new temporary and initialize it with a copy of the
argument, and pass its address.
Richard Smith 9 ماه پیش
والد
کامیت
e0de9ddf05

+ 55 - 10
toolchain/check/convert.cpp

@@ -55,6 +55,21 @@ static auto MarkInitializerFor(SemIR::File& sem_ir, SemIR::InstId init_id,
   }
 }
 
+// For a value or initializing expression using a copy value representation,
+// copy the value into a temporary object.
+static auto CopyValueToTemporary(Context& context, SemIR::InstId init_id)
+    -> SemIR::InstId {
+  // TODO: Consider using `None` to mean that we immediately materialize and
+  // initialize a temporary, rather than two separate instructions.
+  auto init = context.sem_ir().insts().Get(init_id);
+  auto temporary_id = AddInstWithCleanup<SemIR::TemporaryStorage>(
+      context, SemIR::LocId(init_id), {.type_id = init.type_id()});
+  return AddInst<SemIR::Temporary>(context, SemIR::LocId(init_id),
+                                   {.type_id = init.type_id(),
+                                    .storage_id = temporary_id,
+                                    .init_id = init_id});
+}
+
 // Commits to using a temporary to store the result of the initializing
 // expression described by `init_id`, and returns the location of the
 // temporary. If `discarded` is `true`, the result is discarded, and no
@@ -85,15 +100,7 @@ static auto FinalizeTemporary(Context& context, SemIR::InstId init_id,
 
   // The initializer has no return slot, but we want to produce a temporary
   // object. Materialize one now.
-  // TODO: Consider using `None` to mean that we immediately materialize and
-  // initialize a temporary, rather than two separate instructions.
-  auto init = sem_ir.insts().Get(init_id);
-  auto temporary_id = AddInstWithCleanup<SemIR::TemporaryStorage>(
-      context, SemIR::LocId(init_id), {.type_id = init.type_id()});
-  return AddInst<SemIR::Temporary>(context, SemIR::LocId(init_id),
-                                   {.type_id = init.type_id(),
-                                    .storage_id = temporary_id,
-                                    .init_id = init_id});
+  return CopyValueToTemporary(context, init_id);
 }
 
 // Materialize a temporary to hold the result of the given expression if it is
@@ -717,6 +724,8 @@ static auto IsValidExprCategoryForConversionTarget(
              category == SemIR::ExprCategory::Initializing;
     case ConversionTarget::DurableRef:
       return category == SemIR::ExprCategory::DurableRef;
+    case ConversionTarget::CppThunkRef:
+      return category == SemIR::ExprCategory::EphemeralRef;
     case ConversionTarget::ExplicitAs:
       return true;
     case ConversionTarget::Initializer:
@@ -1173,6 +1182,34 @@ static auto PerformCopy(Context& context, SemIR::InstId expr_id, bool diagnose)
   return SemIR::ErrorInst::InstId;
 }
 
+// Convert a value expression so that it can be used to initialize a C++ thunk
+// parameter.
+static auto ConvertValueForCppThunkRef(Context& context, SemIR::InstId expr_id,
+                                       bool diagnose) -> SemIR::InstId {
+  auto expr = context.insts().Get(expr_id);
+
+  // If the expression has a pointer value representation, extract that and use
+  // it directly.
+  if (SemIR::ValueRepr::ForType(context.sem_ir(), expr.type_id()).kind ==
+      SemIR::ValueRepr::Pointer) {
+    return AddInst<SemIR::ValueAsRef>(
+        context, SemIR::LocId(expr_id),
+        {.type_id = expr.type_id(), .value_id = expr_id});
+  }
+
+  // Otherwise, we need a temporary to pass as the thunk argument. Create a copy
+  // and initialize a temporary from it.
+  expr_id = PerformCopy(context, expr_id, diagnose);
+  if (SemIR::GetExprCategory(context.sem_ir(), expr_id) ==
+      SemIR::ExprCategory::Value) {
+    // If we still have a value expression, then it's a value expression
+    // whose value is being used directly to initialize the object. Copy
+    // it into a temporary to form an ephemeral reference.
+    expr_id = CopyValueToTemporary(context, expr_id);
+  }
+  return expr_id;
+}
+
 auto PerformAction(Context& context, SemIR::LocId loc_id,
                    SemIR::ConvertToValueAction action) -> SemIR::InstId {
   return Convert(context, loc_id, action.inst_id,
@@ -1377,7 +1414,8 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
     case SemIR::ExprCategory::EphemeralRef:
       // If a reference expression is an acceptable result, we're done.
       if (target.kind == ConversionTarget::ValueOrRef ||
-          target.kind == ConversionTarget::Discarded) {
+          target.kind == ConversionTarget::Discarded ||
+          target.kind == ConversionTarget::CppThunkRef) {
         break;
       }
 
@@ -1406,6 +1444,13 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
       if (target.is_initializer()) {
         expr_id = PerformCopy(context, expr_id, target.diagnose);
       }
+
+      // When initializing a C++ thunk parameter, form a reference, creating a
+      // temporary if needed.
+      if (target.kind == ConversionTarget::CppThunkRef) {
+        expr_id = ConvertValueForCppThunkRef(context, expr_id, target.diagnose);
+      }
+
       break;
   }
 

+ 3 - 0
toolchain/check/convert.h

@@ -21,6 +21,9 @@ struct ConversionTarget {
     ValueOrRef,
     // Convert to a durable reference of type `type_id`.
     DurableRef,
+    // Convert to a reference of type `type_id`, for use as the argument to a
+    // C++ thunk.
+    CppThunkRef,
     // Convert for an explicit `as` cast. This allows any expression category
     // as the result, and uses the `As` interface instead of the `ImplicitAs`
     // interface.

+ 10 - 16
toolchain/check/cpp_thunk.cpp

@@ -10,6 +10,7 @@
 #include "toolchain/check/call.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/control_flow.h"
+#include "toolchain/check/convert.h"
 #include "toolchain/check/literal.h"
 #include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
@@ -237,7 +238,10 @@ static auto BuildCalleeArgs(clang::Sema& sema,
     clang::Expr* call_arg = sema.BuildDeclRefExpr(
         thunk_param, thunk_param->getType(), clang::VK_LValue, clang_loc);
     if (param_type_changed[i]) {
-      // TODO: Insert a cast to an rvalue.
+      // TODO: Consider inserting a cast to an rvalue. Note that we currently
+      // pass pointers to non-temporary objects as the argument when calling a
+      // thunk, so we'll need to either change that or generate different thunks
+      // depending on whether we're moving from each parameter.
       clang::ExprResult deref_result =
           sema.BuildUnaryOp(nullptr, clang_loc, clang::UO_Deref, call_arg);
       CARBON_CHECK(deref_result.isUsable());
@@ -341,22 +345,12 @@ auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id,
                    GetPointerType(context, context.types().GetInstId(
                                                callee_param_type_id)));
 
-      // TODO: Don't create storage if it's already in a storage (depends on
-      // expression category).
-      SemIR::InstId temporary_storage_inst_id = AddInstWithCleanup(
+      arg_id = Convert(context, loc_id, arg_id,
+                       {.kind = ConversionTarget::CppThunkRef,
+                        .type_id = callee_param_type_id});
+      arg_id = AddInst<SemIR::AddrOf>(
           context, loc_id,
-          SemIR::TemporaryStorage{.type_id = callee_param_type_id});
-      AddInst(context, loc_id,
-              SemIR::InitializeFrom{.type_id = callee_param_type_id,
-                                    .src_id = arg_id,
-                                    .dest_id = temporary_storage_inst_id});
-
-      // TODO: Do not use `InitializeFrom` directly. Use the `Initialize`
-      // machinery. See
-      // https://github.com/carbon-language/carbon-lang/pull/5850/files#r2249030529.
-      arg_id = AddInst(context, loc_id,
-                       SemIR::AddrOf{.type_id = thunk_param_type_id,
-                                     .lvalue_id = temporary_storage_inst_id});
+          {.type_id = thunk_param_type_id, .lvalue_id = arg_id});
     }
     thunk_arg_ids.push_back(arg_id);
   }

+ 90 - 99
toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon

@@ -548,15 +548,15 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
 // CHECK:STDOUT:   %true: bool = bool_literal true [concrete = constants.%true]
-// CHECK:STDOUT:   %.loc8_15.1: ref bool = temporary_storage
-// CHECK:STDOUT:   %.loc8_15.2: init bool = initialize_from %true to %.loc8_15.1 [concrete = constants.%true]
-// CHECK:STDOUT:   %addr.loc8_15.1: %ptr.bb2 = addr_of %.loc8_15.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_15.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_15.1, constants.%T.as.Destroy.impl.Op.8b7
+// CHECK:STDOUT:   %.loc8_11.1: ref bool = temporary_storage
+// CHECK:STDOUT:   %.loc8_11.2: ref bool = temporary %.loc8_11.1, %true
+// CHECK:STDOUT:   %addr.loc8_15: %ptr.bb2 = addr_of %.loc8_11.2
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_15)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_11.1, constants.%T.as.Destroy.impl.Op.8b7
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_15.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_15.2: %ptr.bb2 = addr_of %.loc8_15.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_15.2)
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_11.1, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_11: %ptr.bb2 = addr_of %.loc8_11.1
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_11)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -596,15 +596,15 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
 // CHECK:STDOUT:   %false: bool = bool_literal false [concrete = constants.%false]
-// CHECK:STDOUT:   %.loc8_16.1: ref bool = temporary_storage
-// CHECK:STDOUT:   %.loc8_16.2: init bool = initialize_from %false to %.loc8_16.1 [concrete = constants.%false]
-// CHECK:STDOUT:   %addr.loc8_16.1: %ptr.bb2 = addr_of %.loc8_16.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_16.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_16.1, constants.%T.as.Destroy.impl.Op.8b7
+// CHECK:STDOUT:   %.loc8_11.1: ref bool = temporary_storage
+// CHECK:STDOUT:   %.loc8_11.2: ref bool = temporary %.loc8_11.1, %false
+// CHECK:STDOUT:   %addr.loc8_16: %ptr.bb2 = addr_of %.loc8_11.2
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_16)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_11.1, constants.%T.as.Destroy.impl.Op.8b7
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_16.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_16.2: %ptr.bb2 = addr_of %.loc8_16.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_16.2)
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_11.1, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_11: %ptr.bb2 = addr_of %.loc8_11.1
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_11)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -671,15 +671,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i16 = call %bound_method.loc8_13.2(%int_1) [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.1: %i16 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.2: %i16 = converted %int_1, %.loc8_13.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %.loc8_19.1: ref %i16 = temporary_storage
-// CHECK:STDOUT:   %.loc8_19.2: init %i16 = initialize_from %.loc8_13.2 to %.loc8_19.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %addr.loc8_19.1: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_19.1, constants.%T.as.Destroy.impl.Op.507
+// CHECK:STDOUT:   %.loc8_13.3: ref %i16 = temporary_storage
+// CHECK:STDOUT:   %.loc8_13.4: ref %i16 = temporary %.loc8_13.3, %.loc8_13.2
+// CHECK:STDOUT:   %addr.loc8_19: %ptr.251 = addr_of %.loc8_13.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_13.3, constants.%T.as.Destroy.impl.Op.507
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_19: <bound method> = bound_method %.loc8_19.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_19.2: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_19(%addr.loc8_19.2)
+// CHECK:STDOUT:   %bound_method.loc8_13.3: <bound method> = bound_method %.loc8_13.3, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_13: %ptr.251 = addr_of %.loc8_13.3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_13.3(%addr.loc8_13)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -744,15 +744,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i16 = call %bound_method.loc8_11.2(%int_32767) [concrete = constants.%int_32767.faa]
 // CHECK:STDOUT:   %.loc8_11.1: %i16 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_32767.faa]
 // CHECK:STDOUT:   %.loc8_11.2: %i16 = converted %int_32767, %.loc8_11.1 [concrete = constants.%int_32767.faa]
-// CHECK:STDOUT:   %.loc8_17.1: ref %i16 = temporary_storage
-// CHECK:STDOUT:   %.loc8_17.2: init %i16 = initialize_from %.loc8_11.2 to %.loc8_17.1 [concrete = constants.%int_32767.faa]
-// CHECK:STDOUT:   %addr.loc8_17.1: %ptr.251 = addr_of %.loc8_17.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_17.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_17.1, constants.%T.as.Destroy.impl.Op.507
+// CHECK:STDOUT:   %.loc8_11.3: ref %i16 = temporary_storage
+// CHECK:STDOUT:   %.loc8_11.4: ref %i16 = temporary %.loc8_11.3, %.loc8_11.2
+// CHECK:STDOUT:   %addr.loc8_17: %ptr.251 = addr_of %.loc8_11.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_17)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_11.3, constants.%T.as.Destroy.impl.Op.507
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_17: <bound method> = bound_method %.loc8_17.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_17.2: %ptr.251 = addr_of %.loc8_17.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_17(%addr.loc8_17.2)
+// CHECK:STDOUT:   %bound_method.loc8_11.3: <bound method> = bound_method %.loc8_11.3, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_11: %ptr.251 = addr_of %.loc8_11.3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_11.3(%addr.loc8_11)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -834,15 +834,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i16 = call %bound_method.loc8_11.3(%.loc8_11.2) [concrete = constants.%int_-32768.7e5]
 // CHECK:STDOUT:   %.loc8_11.3: %i16 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_-32768.7e5]
 // CHECK:STDOUT:   %.loc8_11.4: %i16 = converted %Core.IntLiteral.as.Negate.impl.Op.call, %.loc8_11.3 [concrete = constants.%int_-32768.7e5]
-// CHECK:STDOUT:   %.loc8_18.1: ref %i16 = temporary_storage
-// CHECK:STDOUT:   %.loc8_18.2: init %i16 = initialize_from %.loc8_11.4 to %.loc8_18.1 [concrete = constants.%int_-32768.7e5]
-// CHECK:STDOUT:   %addr.loc8_18.1: %ptr.251 = addr_of %.loc8_18.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_18.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_18.1, constants.%T.as.Destroy.impl.Op.507
+// CHECK:STDOUT:   %.loc8_11.5: ref %i16 = temporary_storage
+// CHECK:STDOUT:   %.loc8_11.6: ref %i16 = temporary %.loc8_11.5, %.loc8_11.4
+// CHECK:STDOUT:   %addr.loc8_18: %ptr.251 = addr_of %.loc8_11.6
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_18)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_11.5, constants.%T.as.Destroy.impl.Op.507
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_18: <bound method> = bound_method %.loc8_18.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_18.2: %ptr.251 = addr_of %.loc8_18.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_18(%addr.loc8_18.2)
+// CHECK:STDOUT:   %bound_method.loc8_11.4: <bound method> = bound_method %.loc8_11.5, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_11: %ptr.251 = addr_of %.loc8_11.5
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_11.4(%addr.loc8_11)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -909,15 +909,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i16 = call %bound_method.loc8_13.2(%int_1) [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.1: %i16 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.2: %i16 = converted %int_1, %.loc8_13.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %.loc8_19.1: ref %i16 = temporary_storage
-// CHECK:STDOUT:   %.loc8_19.2: init %i16 = initialize_from %.loc8_13.2 to %.loc8_19.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %addr.loc8_19.1: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_19.1, constants.%T.as.Destroy.impl.Op.507
+// CHECK:STDOUT:   %.loc8_13.3: ref %i16 = temporary_storage
+// CHECK:STDOUT:   %.loc8_13.4: ref %i16 = temporary %.loc8_13.3, %.loc8_13.2
+// CHECK:STDOUT:   %addr.loc8_19: %ptr.251 = addr_of %.loc8_13.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_13.3, constants.%T.as.Destroy.impl.Op.507
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_19: <bound method> = bound_method %.loc8_19.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_19.2: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_19(%addr.loc8_19.2)
+// CHECK:STDOUT:   %bound_method.loc8_13.3: <bound method> = bound_method %.loc8_13.3, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_13: %ptr.251 = addr_of %.loc8_13.3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_13.3(%addr.loc8_13)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -984,15 +984,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i16 = call %bound_method.loc8_13.2(%int_1) [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.1: %i16 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.2: %i16 = converted %int_1, %.loc8_13.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %.loc8_19.1: ref %i16 = temporary_storage
-// CHECK:STDOUT:   %.loc8_19.2: init %i16 = initialize_from %.loc8_13.2 to %.loc8_19.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %addr.loc8_19.1: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_19.1, constants.%T.as.Destroy.impl.Op.507
+// CHECK:STDOUT:   %.loc8_13.3: ref %i16 = temporary_storage
+// CHECK:STDOUT:   %.loc8_13.4: ref %i16 = temporary %.loc8_13.3, %.loc8_13.2
+// CHECK:STDOUT:   %addr.loc8_19: %ptr.251 = addr_of %.loc8_13.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_13.3, constants.%T.as.Destroy.impl.Op.507
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_19: <bound method> = bound_method %.loc8_19.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_19.2: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_19(%addr.loc8_19.2)
+// CHECK:STDOUT:   %bound_method.loc8_13.3: <bound method> = bound_method %.loc8_13.3, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_13: %ptr.251 = addr_of %.loc8_13.3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_13.3(%addr.loc8_13)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1059,15 +1059,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i16 = call %bound_method.loc8_13.2(%int_1) [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.1: %i16 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.2: %i16 = converted %int_1, %.loc8_13.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %.loc8_19.1: ref %i16 = temporary_storage
-// CHECK:STDOUT:   %.loc8_19.2: init %i16 = initialize_from %.loc8_13.2 to %.loc8_19.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %addr.loc8_19.1: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_19.1, constants.%T.as.Destroy.impl.Op.507
+// CHECK:STDOUT:   %.loc8_13.3: ref %i16 = temporary_storage
+// CHECK:STDOUT:   %.loc8_13.4: ref %i16 = temporary %.loc8_13.3, %.loc8_13.2
+// CHECK:STDOUT:   %addr.loc8_19: %ptr.251 = addr_of %.loc8_13.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_13.3, constants.%T.as.Destroy.impl.Op.507
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_19: <bound method> = bound_method %.loc8_19.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_19.2: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_19(%addr.loc8_19.2)
+// CHECK:STDOUT:   %bound_method.loc8_13.3: <bound method> = bound_method %.loc8_13.3, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_13: %ptr.251 = addr_of %.loc8_13.3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_13.3(%addr.loc8_13)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1134,15 +1134,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i16 = call %bound_method.loc8_13.2(%int_1) [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.1: %i16 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc8_13.2: %i16 = converted %int_1, %.loc8_13.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %.loc8_19.1: ref %i16 = temporary_storage
-// CHECK:STDOUT:   %.loc8_19.2: init %i16 = initialize_from %.loc8_13.2 to %.loc8_19.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %addr.loc8_19.1: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_19.1, constants.%T.as.Destroy.impl.Op.507
+// CHECK:STDOUT:   %.loc8_13.3: ref %i16 = temporary_storage
+// CHECK:STDOUT:   %.loc8_13.4: ref %i16 = temporary %.loc8_13.3, %.loc8_13.2
+// CHECK:STDOUT:   %addr.loc8_19: %ptr.251 = addr_of %.loc8_13.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_19)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_13.3, constants.%T.as.Destroy.impl.Op.507
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_19: <bound method> = bound_method %.loc8_19.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_19.2: %ptr.251 = addr_of %.loc8_19.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_19(%addr.loc8_19.2)
+// CHECK:STDOUT:   %bound_method.loc8_13.3: <bound method> = bound_method %.loc8_13.3, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_13: %ptr.251 = addr_of %.loc8_13.3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_13.3(%addr.loc8_13)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1173,8 +1173,6 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.As.impl.Convert.489, @Core.IntLiteral.as.As.impl.Convert(%int_16) [concrete]
 // CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %int_1.f90: %i16 = int_value 1 [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.913: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%const) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.571: %T.as.Destroy.impl.Op.type.913 = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -1211,15 +1209,8 @@ fn F() {
 // CHECK:STDOUT:   %.loc16_13.1: %i16 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc16_13.2: %i16 = converted %int_1, %.loc16_13.1 [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc16_13.3: %const = converted %.loc16_13.2, <error> [concrete = <error>]
-// CHECK:STDOUT:   %.loc16_19.1: ref %const = temporary_storage
-// CHECK:STDOUT:   %.loc16_19.2: init %const = initialize_from <error> to %.loc16_19.1 [concrete = <error>]
-// CHECK:STDOUT:   %addr.loc16_19.1: %ptr.758 = addr_of %.loc16_19.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc16_19.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc16_19.1, constants.%T.as.Destroy.impl.Op.571
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc16_19: <bound method> = bound_method %.loc16_19.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc16_19.2: %ptr.758 = addr_of %.loc16_19.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc16_19(%addr.loc16_19.2)
+// CHECK:STDOUT:   %addr: %ptr.758 = addr_of <error> [concrete = <error>]
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1383,15 +1374,15 @@ fn F() {
 // CHECK:STDOUT:   %Float.call: init type = call constants.%Float(%int_64) [concrete = f64]
 // CHECK:STDOUT:   %.loc8_18.1: type = value_of_initializer %Float.call [concrete = f64]
 // CHECK:STDOUT:   %.loc8_18.2: type = converted %Float.call, %.loc8_18.1 [concrete = f64]
-// CHECK:STDOUT:   %.loc8_21.1: ref f64 = temporary_storage
-// CHECK:STDOUT:   %.loc8_21.2: init f64 = initialize_from %float to %.loc8_21.1 [concrete = constants.%float]
-// CHECK:STDOUT:   %addr.loc8_21.1: %ptr.ef1 = addr_of %.loc8_21.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_21.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_21.1, constants.%T.as.Destroy.impl.Op.868
+// CHECK:STDOUT:   %.loc8_11.1: ref f64 = temporary_storage
+// CHECK:STDOUT:   %.loc8_11.2: ref f64 = temporary %.loc8_11.1, %float
+// CHECK:STDOUT:   %addr.loc8_21: %ptr.ef1 = addr_of %.loc8_11.2
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_21)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_11.1, constants.%T.as.Destroy.impl.Op.868
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_21.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_21.2: %ptr.ef1 = addr_of %.loc8_21.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_21.2)
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_11.1, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_11: %ptr.ef1 = addr_of %.loc8_11.1
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_11)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1537,15 +1528,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %u32 = call %bound_method.loc8_11.2(%int_1) [concrete = constants.%int_1.c1d]
 // CHECK:STDOUT:   %.loc8_11.1: %u32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.c1d]
 // CHECK:STDOUT:   %.loc8_11.2: %u32 = converted %int_1, %.loc8_11.1 [concrete = constants.%int_1.c1d]
-// CHECK:STDOUT:   %.loc8_12.1: ref %u32 = temporary_storage
-// CHECK:STDOUT:   %.loc8_12.2: init %u32 = initialize_from %.loc8_11.2 to %.loc8_12.1 [concrete = constants.%int_1.c1d]
-// CHECK:STDOUT:   %addr.loc8_12.1: %ptr.6fd = addr_of %.loc8_12.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_12.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_12.1, constants.%T.as.Destroy.impl.Op.63d
+// CHECK:STDOUT:   %.loc8_11.3: ref %u32 = temporary_storage
+// CHECK:STDOUT:   %.loc8_11.4: ref %u32 = temporary %.loc8_11.3, %.loc8_11.2
+// CHECK:STDOUT:   %addr.loc8_12: %ptr.6fd = addr_of %.loc8_11.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_11.3, constants.%T.as.Destroy.impl.Op.63d
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc8_12.2: %ptr.6fd = addr_of %.loc8_12.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12.2)
+// CHECK:STDOUT:   %bound_method.loc8_11.3: <bound method> = bound_method %.loc8_11.3, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_11: %ptr.6fd = addr_of %.loc8_11.3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_11.3(%addr.loc8_11)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 40 - 94
toolchain/check/testdata/interop/cpp/function/class.carbon

@@ -575,20 +575,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %C = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %C = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %C = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_22.1: ref %C = temporary_storage
-// CHECK:STDOUT:   %.loc8_22.2: init %C = initialize_from %.loc8_14.2 to %.loc8_22.1
-// CHECK:STDOUT:   %addr.loc8_22.1: %ptr.d9e = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_22: <bound method> = bound_method %.loc8_22.1, constants.%T.as.Destroy.impl.Op.21b
+// CHECK:STDOUT:   %.loc8_14.3: ref %C = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_22: %ptr.d9e = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.21b
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_22: <bound method> = bound_method %.loc8_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_22.2: %ptr.d9e = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_22: init %empty_tuple.type = call %bound_method.loc8_22(%addr.loc8_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.21b
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.d9e = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -652,8 +646,6 @@ fn F() {
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.1b3: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.21b: %T.as.Destroy.impl.Op.type.1b3 = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -683,15 +675,8 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref.loc15_17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %C.ref: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   %.loc15_14: %C = converted %.loc15_12, <error> [concrete = <error>]
-// CHECK:STDOUT:   %.loc15_22.1: ref %C = temporary_storage
-// CHECK:STDOUT:   %.loc15_22.2: init %C = initialize_from <error> to %.loc15_22.1 [concrete = <error>]
-// CHECK:STDOUT:   %addr.loc15_22.1: %ptr.d9e = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc15_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc15_22.1, constants.%T.as.Destroy.impl.Op.21b
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc15_22.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc15_22.2: %ptr.d9e = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc15_22.2)
+// CHECK:STDOUT:   %addr: %ptr.d9e = addr_of <error> [concrete = <error>]
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -706,8 +691,6 @@ fn F() {
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.1b3: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%C) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.21b: %T.as.Destroy.impl.Op.type.1b3 = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -737,15 +720,8 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref.loc15_17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %C.ref: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   %.loc15_14: %C = converted %.loc15_12, <error> [concrete = <error>]
-// CHECK:STDOUT:   %.loc15_22.1: ref %C = temporary_storage
-// CHECK:STDOUT:   %.loc15_22.2: init %C = initialize_from <error> to %.loc15_22.1 [concrete = <error>]
-// CHECK:STDOUT:   %addr.loc15_22.1: %ptr.d9e = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc15_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc15_22.1, constants.%T.as.Destroy.impl.Op.21b
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc15_22.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc15_22.2: %ptr.d9e = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc15_22.2)
+// CHECK:STDOUT:   %addr: %ptr.d9e = addr_of <error> [concrete = <error>]
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -802,10 +778,9 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %C = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %C = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %C = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_24.1: ref %C = temporary_storage
-// CHECK:STDOUT:   %.loc8_24.2: init %C = initialize_from %.loc8_14.2 to %.loc8_24.1
-// CHECK:STDOUT:   %addr.loc8_24.1: %ptr.838 = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %C = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_24: %ptr.838 = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24)
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %x.patt: %pattern_type.69f = binding_pattern x [concrete]
 // CHECK:STDOUT:     %x.var_patt: %pattern_type.69f = var_pattern %x.patt [concrete]
@@ -822,16 +797,11 @@ fn F() {
 // CHECK:STDOUT:   %bound_method.loc10: <bound method> = bound_method %x.var, %T.as.Destroy.impl.Op.specific_fn.1
 // CHECK:STDOUT:   %addr.loc10: %ptr.838 = addr_of %x.var
 // CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_24: <bound method> = bound_method %.loc8_24.1, constants.%T.as.Destroy.impl.Op.dbb
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_24: <bound method> = bound_method %.loc8_24.1, %T.as.Destroy.impl.Op.specific_fn.2
-// CHECK:STDOUT:   %addr.loc8_24.2: %ptr.838 = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_24: init %empty_tuple.type = call %bound_method.loc8_24(%addr.loc8_24.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.dbb
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.dbb
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.3
+// CHECK:STDOUT:   %bound_method.loc8: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.838 = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -893,20 +863,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_15.4: ref %C = temporary %.loc8_15.2, %.loc8_15.3
 // CHECK:STDOUT:   %.loc8_17.1: ref %C = converted %.loc8_15.1, %.loc8_15.4
 // CHECK:STDOUT:   %.loc8_17.2: %C = bind_value %.loc8_17.1
-// CHECK:STDOUT:   %.loc8_31.1: ref %C = temporary_storage
-// CHECK:STDOUT:   %.loc8_31.2: init %C = initialize_from %.loc8_17.2 to %.loc8_31.1
-// CHECK:STDOUT:   %addr.loc8_31.1: %ptr.c0c = addr_of %.loc8_31.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_31.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_31: <bound method> = bound_method %.loc8_31.1, constants.%T.as.Destroy.impl.Op.f48
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_31: <bound method> = bound_method %.loc8_31.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_31.2: %ptr.c0c = addr_of %.loc8_31.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_31: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_15: <bound method> = bound_method %.loc8_15.2, constants.%T.as.Destroy.impl.Op.f48
+// CHECK:STDOUT:   %.loc8_17.3: ref %C = value_as_ref %.loc8_17.2
+// CHECK:STDOUT:   %addr.loc8_31: %ptr.c0c = addr_of %.loc8_17.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_31)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_15.2, constants.%T.as.Destroy.impl.Op.f48
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_15: <bound method> = bound_method %.loc8_15.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_15.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_15: %ptr.c0c = addr_of %.loc8_15.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_15: init %empty_tuple.type = call %bound_method.loc8_15(%addr.loc8_15)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_15)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -964,10 +928,9 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %C = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %C = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %C = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_24.1: ref %C = temporary_storage
-// CHECK:STDOUT:   %.loc8_24.2: init %C = initialize_from %.loc8_14.2 to %.loc8_24.1
-// CHECK:STDOUT:   %addr.loc8_24.1: %ptr.de2 = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %C = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_24: %ptr.de2 = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24)
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %x.patt: %pattern_type.cff = binding_pattern x [concrete]
 // CHECK:STDOUT:     %x.var_patt: %pattern_type.cff = var_pattern %x.patt [concrete]
@@ -983,16 +946,11 @@ fn F() {
 // CHECK:STDOUT:   %bound_method.loc9: <bound method> = bound_method %x.var, %T.as.Destroy.impl.Op.specific_fn.1
 // CHECK:STDOUT:   %addr.loc9: %ptr.820 = addr_of %x.var
 // CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_24: <bound method> = bound_method %.loc8_24.1, constants.%T.as.Destroy.impl.Op.362
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.362
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_24: <bound method> = bound_method %.loc8_24.1, %T.as.Destroy.impl.Op.specific_fn.2
-// CHECK:STDOUT:   %addr.loc8_24.2: %ptr.de2 = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_24: init %empty_tuple.type = call %bound_method.loc8_24(%addr.loc8_24.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.362
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.3
+// CHECK:STDOUT:   %bound_method.loc8: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.de2 = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1050,20 +1008,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc9_12.4: ref %C = temporary %.loc9_12.2, %.loc9_12.3
 // CHECK:STDOUT:   %.loc9_14.1: ref %C = converted %.loc9_12.1, %.loc9_12.4
 // CHECK:STDOUT:   %.loc9_14.2: %C = bind_value %.loc9_14.1
-// CHECK:STDOUT:   %.loc9_22.1: ref %C = temporary_storage
-// CHECK:STDOUT:   %.loc9_22.2: init %C = initialize_from %.loc9_14.2 to %.loc9_22.1
-// CHECK:STDOUT:   %addr.loc9_22.1: %ptr.d9e = addr_of %.loc9_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc9_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc9_22: <bound method> = bound_method %.loc9_22.1, constants.%T.as.Destroy.impl.Op.21b
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc9_22: <bound method> = bound_method %.loc9_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc9_22.2: %ptr.d9e = addr_of %.loc9_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9_22: init %empty_tuple.type = call %bound_method.loc9_22(%addr.loc9_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc9_12: <bound method> = bound_method %.loc9_12.2, constants.%T.as.Destroy.impl.Op.21b
+// CHECK:STDOUT:   %.loc9_14.3: ref %C = value_as_ref %.loc9_14.2
+// CHECK:STDOUT:   %addr.loc9_22: %ptr.d9e = addr_of %.loc9_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc9_22)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc9_12.2, constants.%T.as.Destroy.impl.Op.21b
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc9_12: <bound method> = bound_method %.loc9_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc9_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc9_12: %ptr.d9e = addr_of %.loc9_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9_12: init %empty_tuple.type = call %bound_method.loc9_12(%addr.loc9_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc9_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1117,24 +1069,18 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %C = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %C = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %C = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_22.1: ref %C = temporary_storage
-// CHECK:STDOUT:   %.loc8_22.2: init %C = initialize_from %.loc8_14.2 to %.loc8_22.1
-// CHECK:STDOUT:   %addr.loc8_22.1: %ptr.d9e = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %C = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_22: %ptr.d9e = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22)
 // CHECK:STDOUT:   %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %C.ref.loc9: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   %bar.ref: %C.bar.type = name_ref bar, imports.%C.bar.decl [concrete = constants.%C.bar]
 // CHECK:STDOUT:   %C.bar.call: init %empty_tuple.type = call %bar.ref()
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_22: <bound method> = bound_method %.loc8_22.1, constants.%T.as.Destroy.impl.Op.21b
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_22: <bound method> = bound_method %.loc8_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_22.2: %ptr.d9e = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_22: init %empty_tuple.type = call %bound_method.loc8_22(%addr.loc8_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.21b
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.21b
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.d9e = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 8 - 8
toolchain/check/testdata/interop/cpp/function/full_semir.carbon

@@ -173,15 +173,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i16 = call %bound_method.loc7_13.2(%int_1) [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc7_13.1: %i16 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.f90]
 // CHECK:STDOUT:   %.loc7_13.2: %i16 = converted %int_1, %.loc7_13.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %.loc7_19.1: ref %i16 = temporary_storage
-// CHECK:STDOUT:   %.loc7_19.2: init %i16 = initialize_from %.loc7_13.2 to %.loc7_19.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %addr.loc7_19.1: %ptr.251 = addr_of %.loc7_19.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc7_19.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc7_19.1, constants.%T.as.Destroy.impl.Op.507
+// CHECK:STDOUT:   %.loc7_13.3: ref %i16 = temporary_storage
+// CHECK:STDOUT:   %.loc7_13.4: ref %i16 = temporary %.loc7_13.3, %.loc7_13.2
+// CHECK:STDOUT:   %addr.loc7_19: %ptr.251 = addr_of %.loc7_13.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc7_19)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc7_13.3, constants.%T.as.Destroy.impl.Op.507
 // CHECK:STDOUT:   %T.as.Destroy.impl.Op.specific_fn: <specific function> = specific_function constants.%T.as.Destroy.impl.Op.507, @T.as.Destroy.impl.Op(constants.%i16) [concrete = constants.%T.as.Destroy.impl.Op.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc7_19: <bound method> = bound_method %.loc7_19.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc7_19.2: %ptr.251 = addr_of %.loc7_19.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc7_19(%addr.loc7_19.2)
+// CHECK:STDOUT:   %bound_method.loc7_13.3: <bound method> = bound_method %.loc7_13.3, %T.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc7_13: %ptr.251 = addr_of %.loc7_13.3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc7_13.3(%addr.loc7_13)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 40 - 94
toolchain/check/testdata/interop/cpp/function/struct.carbon

@@ -573,20 +573,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %S = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %S = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %S = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_22.1: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc8_22.2: init %S = initialize_from %.loc8_14.2 to %.loc8_22.1
-// CHECK:STDOUT:   %addr.loc8_22.1: %ptr.5c7 = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_22: <bound method> = bound_method %.loc8_22.1, constants.%T.as.Destroy.impl.Op.ab5
+// CHECK:STDOUT:   %.loc8_14.3: ref %S = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_22: %ptr.5c7 = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.ab5
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_22: <bound method> = bound_method %.loc8_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_22.2: %ptr.5c7 = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_22: init %empty_tuple.type = call %bound_method.loc8_22(%addr.loc8_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.ab5
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.5c7 = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -650,8 +644,6 @@ fn F() {
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.642: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%S) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.ab5: %T.as.Destroy.impl.Op.type.642 = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -681,15 +673,8 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref.loc15_17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %S.ref: type = name_ref S, imports.%S.decl [concrete = constants.%S]
 // CHECK:STDOUT:   %.loc15_14: %S = converted %.loc15_12, <error> [concrete = <error>]
-// CHECK:STDOUT:   %.loc15_22.1: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc15_22.2: init %S = initialize_from <error> to %.loc15_22.1 [concrete = <error>]
-// CHECK:STDOUT:   %addr.loc15_22.1: %ptr.5c7 = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc15_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc15_22.1, constants.%T.as.Destroy.impl.Op.ab5
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc15_22.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc15_22.2: %ptr.5c7 = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc15_22.2)
+// CHECK:STDOUT:   %addr: %ptr.5c7 = addr_of <error> [concrete = <error>]
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -704,8 +689,6 @@ fn F() {
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.642: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%S) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.ab5: %T.as.Destroy.impl.Op.type.642 = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -735,15 +718,8 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref.loc15_17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %S.ref: type = name_ref S, imports.%S.decl [concrete = constants.%S]
 // CHECK:STDOUT:   %.loc15_14: %S = converted %.loc15_12, <error> [concrete = <error>]
-// CHECK:STDOUT:   %.loc15_22.1: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc15_22.2: init %S = initialize_from <error> to %.loc15_22.1 [concrete = <error>]
-// CHECK:STDOUT:   %addr.loc15_22.1: %ptr.5c7 = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc15_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc15_22.1, constants.%T.as.Destroy.impl.Op.ab5
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc15_22.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc15_22.2: %ptr.5c7 = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc15_22.2)
+// CHECK:STDOUT:   %addr: %ptr.5c7 = addr_of <error> [concrete = <error>]
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -800,10 +776,9 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %S = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %S = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %S = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_24.1: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc8_24.2: init %S = initialize_from %.loc8_14.2 to %.loc8_24.1
-// CHECK:STDOUT:   %addr.loc8_24.1: %ptr.edf = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %S = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_24: %ptr.edf = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24)
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %x.patt: %pattern_type.cd8 = binding_pattern x [concrete]
 // CHECK:STDOUT:     %x.var_patt: %pattern_type.cd8 = var_pattern %x.patt [concrete]
@@ -820,16 +795,11 @@ fn F() {
 // CHECK:STDOUT:   %bound_method.loc10: <bound method> = bound_method %x.var, %T.as.Destroy.impl.Op.specific_fn.1
 // CHECK:STDOUT:   %addr.loc10: %ptr.edf = addr_of %x.var
 // CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_24: <bound method> = bound_method %.loc8_24.1, constants.%T.as.Destroy.impl.Op.9b3
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_24: <bound method> = bound_method %.loc8_24.1, %T.as.Destroy.impl.Op.specific_fn.2
-// CHECK:STDOUT:   %addr.loc8_24.2: %ptr.edf = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_24: init %empty_tuple.type = call %bound_method.loc8_24(%addr.loc8_24.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.9b3
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.9b3
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.3
+// CHECK:STDOUT:   %bound_method.loc8: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.edf = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -891,20 +861,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_15.4: ref %S = temporary %.loc8_15.2, %.loc8_15.3
 // CHECK:STDOUT:   %.loc8_17.1: ref %S = converted %.loc8_15.1, %.loc8_15.4
 // CHECK:STDOUT:   %.loc8_17.2: %S = bind_value %.loc8_17.1
-// CHECK:STDOUT:   %.loc8_31.1: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc8_31.2: init %S = initialize_from %.loc8_17.2 to %.loc8_31.1
-// CHECK:STDOUT:   %addr.loc8_31.1: %ptr.887 = addr_of %.loc8_31.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_31.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_31: <bound method> = bound_method %.loc8_31.1, constants.%T.as.Destroy.impl.Op.463
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_31: <bound method> = bound_method %.loc8_31.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_31.2: %ptr.887 = addr_of %.loc8_31.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_31: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_15: <bound method> = bound_method %.loc8_15.2, constants.%T.as.Destroy.impl.Op.463
+// CHECK:STDOUT:   %.loc8_17.3: ref %S = value_as_ref %.loc8_17.2
+// CHECK:STDOUT:   %addr.loc8_31: %ptr.887 = addr_of %.loc8_17.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_31)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_15.2, constants.%T.as.Destroy.impl.Op.463
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_15: <bound method> = bound_method %.loc8_15.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_15.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_15: %ptr.887 = addr_of %.loc8_15.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_15: init %empty_tuple.type = call %bound_method.loc8_15(%addr.loc8_15)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_15)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -962,10 +926,9 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %S = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %S = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %S = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_24.1: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc8_24.2: init %S = initialize_from %.loc8_14.2 to %.loc8_24.1
-// CHECK:STDOUT:   %addr.loc8_24.1: %ptr.149 = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %S = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_24: %ptr.149 = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24)
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %x.patt: %pattern_type.cff = binding_pattern x [concrete]
 // CHECK:STDOUT:     %x.var_patt: %pattern_type.cff = var_pattern %x.patt [concrete]
@@ -981,16 +944,11 @@ fn F() {
 // CHECK:STDOUT:   %bound_method.loc9: <bound method> = bound_method %x.var, %T.as.Destroy.impl.Op.specific_fn.1
 // CHECK:STDOUT:   %addr.loc9: %ptr.820 = addr_of %x.var
 // CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_24: <bound method> = bound_method %.loc8_24.1, constants.%T.as.Destroy.impl.Op.952
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.952
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_24: <bound method> = bound_method %.loc8_24.1, %T.as.Destroy.impl.Op.specific_fn.2
-// CHECK:STDOUT:   %addr.loc8_24.2: %ptr.149 = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_24: init %empty_tuple.type = call %bound_method.loc8_24(%addr.loc8_24.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.952
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.3
+// CHECK:STDOUT:   %bound_method.loc8: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.149 = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1048,20 +1006,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc9_12.4: ref %S = temporary %.loc9_12.2, %.loc9_12.3
 // CHECK:STDOUT:   %.loc9_14.1: ref %S = converted %.loc9_12.1, %.loc9_12.4
 // CHECK:STDOUT:   %.loc9_14.2: %S = bind_value %.loc9_14.1
-// CHECK:STDOUT:   %.loc9_22.1: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc9_22.2: init %S = initialize_from %.loc9_14.2 to %.loc9_22.1
-// CHECK:STDOUT:   %addr.loc9_22.1: %ptr.5c7 = addr_of %.loc9_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc9_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc9_22: <bound method> = bound_method %.loc9_22.1, constants.%T.as.Destroy.impl.Op.ab5
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc9_22: <bound method> = bound_method %.loc9_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc9_22.2: %ptr.5c7 = addr_of %.loc9_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9_22: init %empty_tuple.type = call %bound_method.loc9_22(%addr.loc9_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc9_12: <bound method> = bound_method %.loc9_12.2, constants.%T.as.Destroy.impl.Op.ab5
+// CHECK:STDOUT:   %.loc9_14.3: ref %S = value_as_ref %.loc9_14.2
+// CHECK:STDOUT:   %addr.loc9_22: %ptr.5c7 = addr_of %.loc9_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc9_22)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc9_12.2, constants.%T.as.Destroy.impl.Op.ab5
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc9_12: <bound method> = bound_method %.loc9_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc9_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc9_12: %ptr.5c7 = addr_of %.loc9_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9_12: init %empty_tuple.type = call %bound_method.loc9_12(%addr.loc9_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc9_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1115,24 +1067,18 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %S = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %S = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %S = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_22.1: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc8_22.2: init %S = initialize_from %.loc8_14.2 to %.loc8_22.1
-// CHECK:STDOUT:   %addr.loc8_22.1: %ptr.5c7 = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %S = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_22: %ptr.5c7 = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22)
 // CHECK:STDOUT:   %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %S.ref.loc9: type = name_ref S, imports.%S.decl [concrete = constants.%S]
 // CHECK:STDOUT:   %bar.ref: %S.bar.type = name_ref bar, imports.%S.bar.decl [concrete = constants.%S.bar]
 // CHECK:STDOUT:   %S.bar.call: init %empty_tuple.type = call %bar.ref()
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_22: <bound method> = bound_method %.loc8_22.1, constants.%T.as.Destroy.impl.Op.ab5
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_22: <bound method> = bound_method %.loc8_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_22.2: %ptr.5c7 = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_22: init %empty_tuple.type = call %bound_method.loc8_22(%addr.loc8_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.ab5
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.ab5
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.5c7 = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 40 - 94
toolchain/check/testdata/interop/cpp/function/union.carbon

@@ -571,20 +571,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %U = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %U = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %U = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_22.1: ref %U = temporary_storage
-// CHECK:STDOUT:   %.loc8_22.2: init %U = initialize_from %.loc8_14.2 to %.loc8_22.1
-// CHECK:STDOUT:   %addr.loc8_22.1: %ptr.86f = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_22: <bound method> = bound_method %.loc8_22.1, constants.%T.as.Destroy.impl.Op.2fa
+// CHECK:STDOUT:   %.loc8_14.3: ref %U = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_22: %ptr.86f = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.2fa
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_22: <bound method> = bound_method %.loc8_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_22.2: %ptr.86f = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_22: init %empty_tuple.type = call %bound_method.loc8_22(%addr.loc8_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.2fa
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.86f = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -599,8 +593,6 @@ fn F() {
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.0b7: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%U) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.2fa: %T.as.Destroy.impl.Op.type.0b7 = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -630,15 +622,8 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref.loc15_17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %U.ref: type = name_ref U, imports.%U.decl [concrete = constants.%U]
 // CHECK:STDOUT:   %.loc15_14: %U = converted %.loc15_12, <error> [concrete = <error>]
-// CHECK:STDOUT:   %.loc15_22.1: ref %U = temporary_storage
-// CHECK:STDOUT:   %.loc15_22.2: init %U = initialize_from <error> to %.loc15_22.1 [concrete = <error>]
-// CHECK:STDOUT:   %addr.loc15_22.1: %ptr.86f = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc15_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc15_22.1, constants.%T.as.Destroy.impl.Op.2fa
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc15_22.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc15_22.2: %ptr.86f = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc15_22.2)
+// CHECK:STDOUT:   %addr: %ptr.86f = addr_of <error> [concrete = <error>]
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -653,8 +638,6 @@ fn F() {
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.0b7: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%U) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.2fa: %T.as.Destroy.impl.Op.type.0b7 = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -684,15 +667,8 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref.loc15_17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %U.ref: type = name_ref U, imports.%U.decl [concrete = constants.%U]
 // CHECK:STDOUT:   %.loc15_14: %U = converted %.loc15_12, <error> [concrete = <error>]
-// CHECK:STDOUT:   %.loc15_22.1: ref %U = temporary_storage
-// CHECK:STDOUT:   %.loc15_22.2: init %U = initialize_from <error> to %.loc15_22.1 [concrete = <error>]
-// CHECK:STDOUT:   %addr.loc15_22.1: %ptr.86f = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc15_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc15_22.1, constants.%T.as.Destroy.impl.Op.2fa
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc15_22.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc15_22.2: %ptr.86f = addr_of %.loc15_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc15_22.2)
+// CHECK:STDOUT:   %addr: %ptr.86f = addr_of <error> [concrete = <error>]
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -749,10 +725,9 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %U = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %U = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %U = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_24.1: ref %U = temporary_storage
-// CHECK:STDOUT:   %.loc8_24.2: init %U = initialize_from %.loc8_14.2 to %.loc8_24.1
-// CHECK:STDOUT:   %addr.loc8_24.1: %ptr.87e = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %U = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_24: %ptr.87e = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24)
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %x.patt: %pattern_type.eb9 = binding_pattern x [concrete]
 // CHECK:STDOUT:     %x.var_patt: %pattern_type.eb9 = var_pattern %x.patt [concrete]
@@ -769,16 +744,11 @@ fn F() {
 // CHECK:STDOUT:   %bound_method.loc10: <bound method> = bound_method %x.var, %T.as.Destroy.impl.Op.specific_fn.1
 // CHECK:STDOUT:   %addr.loc10: %ptr.87e = addr_of %x.var
 // CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_24: <bound method> = bound_method %.loc8_24.1, constants.%T.as.Destroy.impl.Op.d5d
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_24: <bound method> = bound_method %.loc8_24.1, %T.as.Destroy.impl.Op.specific_fn.2
-// CHECK:STDOUT:   %addr.loc8_24.2: %ptr.87e = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_24: init %empty_tuple.type = call %bound_method.loc8_24(%addr.loc8_24.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.d5d
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.d5d
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.3
+// CHECK:STDOUT:   %bound_method.loc8: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.87e = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -840,20 +810,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_15.4: ref %U = temporary %.loc8_15.2, %.loc8_15.3
 // CHECK:STDOUT:   %.loc8_17.1: ref %U = converted %.loc8_15.1, %.loc8_15.4
 // CHECK:STDOUT:   %.loc8_17.2: %U = bind_value %.loc8_17.1
-// CHECK:STDOUT:   %.loc8_31.1: ref %U = temporary_storage
-// CHECK:STDOUT:   %.loc8_31.2: init %U = initialize_from %.loc8_17.2 to %.loc8_31.1
-// CHECK:STDOUT:   %addr.loc8_31.1: %ptr.8c1 = addr_of %.loc8_31.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_31.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_31: <bound method> = bound_method %.loc8_31.1, constants.%T.as.Destroy.impl.Op.28c
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_31: <bound method> = bound_method %.loc8_31.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_31.2: %ptr.8c1 = addr_of %.loc8_31.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_31: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_15: <bound method> = bound_method %.loc8_15.2, constants.%T.as.Destroy.impl.Op.28c
+// CHECK:STDOUT:   %.loc8_17.3: ref %U = value_as_ref %.loc8_17.2
+// CHECK:STDOUT:   %addr.loc8_31: %ptr.8c1 = addr_of %.loc8_17.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_31)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_15.2, constants.%T.as.Destroy.impl.Op.28c
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_15: <bound method> = bound_method %.loc8_15.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_15.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_15: %ptr.8c1 = addr_of %.loc8_15.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_15: init %empty_tuple.type = call %bound_method.loc8_15(%addr.loc8_15)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_15)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -911,10 +875,9 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %U = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %U = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %U = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_24.1: ref %U = temporary_storage
-// CHECK:STDOUT:   %.loc8_24.2: init %U = initialize_from %.loc8_14.2 to %.loc8_24.1
-// CHECK:STDOUT:   %addr.loc8_24.1: %ptr.a6c = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %U = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_24: %ptr.a6c = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_24)
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %x.patt: %pattern_type.cff = binding_pattern x [concrete]
 // CHECK:STDOUT:     %x.var_patt: %pattern_type.cff = var_pattern %x.patt [concrete]
@@ -930,16 +893,11 @@ fn F() {
 // CHECK:STDOUT:   %bound_method.loc9: <bound method> = bound_method %x.var, %T.as.Destroy.impl.Op.specific_fn.1
 // CHECK:STDOUT:   %addr.loc9: %ptr.820 = addr_of %x.var
 // CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_24: <bound method> = bound_method %.loc8_24.1, constants.%T.as.Destroy.impl.Op.f3b
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.f3b
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_24: <bound method> = bound_method %.loc8_24.1, %T.as.Destroy.impl.Op.specific_fn.2
-// CHECK:STDOUT:   %addr.loc8_24.2: %ptr.a6c = addr_of %.loc8_24.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_24: init %empty_tuple.type = call %bound_method.loc8_24(%addr.loc8_24.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.f3b
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.3
+// CHECK:STDOUT:   %bound_method.loc8: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.a6c = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -997,20 +955,14 @@ fn F() {
 // CHECK:STDOUT:   %.loc9_12.4: ref %U = temporary %.loc9_12.2, %.loc9_12.3
 // CHECK:STDOUT:   %.loc9_14.1: ref %U = converted %.loc9_12.1, %.loc9_12.4
 // CHECK:STDOUT:   %.loc9_14.2: %U = bind_value %.loc9_14.1
-// CHECK:STDOUT:   %.loc9_22.1: ref %U = temporary_storage
-// CHECK:STDOUT:   %.loc9_22.2: init %U = initialize_from %.loc9_14.2 to %.loc9_22.1
-// CHECK:STDOUT:   %addr.loc9_22.1: %ptr.86f = addr_of %.loc9_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc9_22.1)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc9_22: <bound method> = bound_method %.loc9_22.1, constants.%T.as.Destroy.impl.Op.2fa
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc9_22: <bound method> = bound_method %.loc9_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc9_22.2: %ptr.86f = addr_of %.loc9_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9_22: init %empty_tuple.type = call %bound_method.loc9_22(%addr.loc9_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc9_12: <bound method> = bound_method %.loc9_12.2, constants.%T.as.Destroy.impl.Op.2fa
+// CHECK:STDOUT:   %.loc9_14.3: ref %U = value_as_ref %.loc9_14.2
+// CHECK:STDOUT:   %addr.loc9_22: %ptr.86f = addr_of %.loc9_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc9_22)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc9_12.2, constants.%T.as.Destroy.impl.Op.2fa
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc9_12: <bound method> = bound_method %.loc9_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc9_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc9_12: %ptr.86f = addr_of %.loc9_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc9_12: init %empty_tuple.type = call %bound_method.loc9_12(%addr.loc9_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc9_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1064,24 +1016,18 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_12.4: ref %U = temporary %.loc8_12.2, %.loc8_12.3
 // CHECK:STDOUT:   %.loc8_14.1: ref %U = converted %.loc8_12.1, %.loc8_12.4
 // CHECK:STDOUT:   %.loc8_14.2: %U = bind_value %.loc8_14.1
-// CHECK:STDOUT:   %.loc8_22.1: ref %U = temporary_storage
-// CHECK:STDOUT:   %.loc8_22.2: init %U = initialize_from %.loc8_14.2 to %.loc8_22.1
-// CHECK:STDOUT:   %addr.loc8_22.1: %ptr.86f = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22.1)
+// CHECK:STDOUT:   %.loc8_14.3: ref %U = value_as_ref %.loc8_14.2
+// CHECK:STDOUT:   %addr.loc8_22: %ptr.86f = addr_of %.loc8_14.3
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22)
 // CHECK:STDOUT:   %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %U.ref.loc9: type = name_ref U, imports.%U.decl [concrete = constants.%U]
 // CHECK:STDOUT:   %bar.ref: %U.bar.type = name_ref bar, imports.%U.bar.decl [concrete = constants.%U.bar]
 // CHECK:STDOUT:   %U.bar.call: init %empty_tuple.type = call %bar.ref()
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_22: <bound method> = bound_method %.loc8_22.1, constants.%T.as.Destroy.impl.Op.2fa
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_22: <bound method> = bound_method %.loc8_22.1, %T.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc8_22.2: %ptr.86f = addr_of %.loc8_22.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_22: init %empty_tuple.type = call %bound_method.loc8_22(%addr.loc8_22.2)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound.loc8_12: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.2fa
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_12.2, constants.%T.as.Destroy.impl.Op.2fa
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_12: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc8_12.2, %T.as.Destroy.impl.Op.specific_fn
 // CHECK:STDOUT:   %addr.loc8_12: %ptr.86f = addr_of %.loc8_12.2
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call.loc8_12: init %empty_tuple.type = call %bound_method.loc8_12(%addr.loc8_12)
+// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_12)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 378 - 30
toolchain/lower/testdata/interop/cpp/thunk.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
@@ -14,57 +14,225 @@
 // Parameter requires a thunk to be generated
 // ============================================================================
 
-// --- thunk.h
+// --- ints.h
 
-void foo(short x, int y, long z);
+// TODO: Also test `long long` once it's supported.
 
-// --- import_thunk.carbon
+void pass_signed(signed char, short, int, long);
+
+void pass_unsigned(unsigned char, unsigned short, unsigned int, unsigned long);
+
+void pass_short(short);
+
+// --- import_ints.carbon
 
 library "[[@TEST_NAME]]";
 
-import Cpp library "thunk.h";
+import Cpp library "ints.h";
 
 fn MyF() {
-  // TODO: Make sure we actually store the value 5 into `alloca`.
-  // See https://github.com/carbon-language/carbon-lang/pull/5850/files#r2249030529
-  Cpp.foo(5, 6, 7);
+  Cpp.pass_signed(1, 2, 3, 4);
+
+  Cpp.pass_unsigned(1, 2, 3, 4);
+}
+
+fn MakeShort() -> i16;
+
+var c: i16;
+
+fn PassShort(a: i16, b: i16*) {
+  Cpp.pass_short(a);
+  Cpp.pass_short(*b);
+  Cpp.pass_short(c);
+  Cpp.pass_short(MakeShort());
 }
 
-// CHECK:STDOUT: ; ModuleID = 'import_thunk.carbon'
-// CHECK:STDOUT: source_filename = "import_thunk.carbon"
+// --- struct.h
+
+struct X {
+  int a;
+  int b;
+  int c;
+};
+
+void pass_struct(X);
+
+// --- import_struct.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct.h";
+
+fn Test() {
+  var x: Cpp.X;
+  x.a = 1;
+  x.b = 2;
+  x.c = 3;
+  Cpp.pass_struct(x);
+}
+
+// --- class_with_nontrivial_copy.h
+
+class Y {
+ public:
+  Y(const Y&);
+
+  int a;
+  int b;
+  int c;
+};
+
+void pass_struct(Y);
+
+// --- import_class_with_nontrivial_copy.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_with_nontrivial_copy.h";
+
+fn PassRefExpr() {
+  var y: Cpp.Y;
+  y.a = 1;
+  y.b = 2;
+  y.c = 3;
+  Cpp.pass_struct(y);
+}
+
+fn Make() -> Cpp.Y;
+
+fn PassInitExpr() {
+  Cpp.pass_struct(Make());
+}
+
+fn PassValueExpr(y: Cpp.Y) {
+  Cpp.pass_struct(y);
+}
+
+// CHECK:STDOUT: ; ModuleID = 'import_ints.carbon'
+// CHECK:STDOUT: source_filename = "import_ints.carbon"
 // CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
 // CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
 // CHECK:STDOUT:
+// CHECK:STDOUT: @_Cc.Main = global i16 0
+// CHECK:STDOUT:
 // CHECK:STDOUT: define void @_CMyF.Main() !dbg !7 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %.loc9_18.1.temp = alloca i16, align 2, !dbg !10
-// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 2, ptr %.loc9_18.1.temp), !dbg !10
-// CHECK:STDOUT:   call void @_Z3foosil.carbon_thunk(ptr %.loc9_18.1.temp, i32 6, i64 7), !dbg !10
-// CHECK:STDOUT:   ret void, !dbg !11
+// CHECK:STDOUT:   %.loc7_19.3.temp = alloca i8, align 1, !dbg !10
+// CHECK:STDOUT:   %.loc7_22.3.temp = alloca i16, align 2, !dbg !11
+// CHECK:STDOUT:   %.loc9_21.3.temp = alloca i8, align 1, !dbg !12
+// CHECK:STDOUT:   %.loc9_24.3.temp = alloca i16, align 2, !dbg !13
+// CHECK:STDOUT:   %.loc9_27.3.temp = alloca i32, align 4, !dbg !14
+// CHECK:STDOUT:   %.loc9_30.3.temp = alloca i64, align 8, !dbg !15
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 1, ptr %.loc7_19.3.temp), !dbg !10
+// CHECK:STDOUT:   store i8 1, ptr %.loc7_19.3.temp, align 1, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 2, ptr %.loc7_22.3.temp), !dbg !11
+// CHECK:STDOUT:   store i16 2, ptr %.loc7_22.3.temp, align 2, !dbg !11
+// CHECK:STDOUT:   call void @_Z11pass_signedasil.carbon_thunk(ptr %.loc7_19.3.temp, ptr %.loc7_22.3.temp, i32 3, i64 4), !dbg !16
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 1, ptr %.loc9_21.3.temp), !dbg !12
+// CHECK:STDOUT:   store i8 1, ptr %.loc9_21.3.temp, align 1, !dbg !12
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 2, ptr %.loc9_24.3.temp), !dbg !13
+// CHECK:STDOUT:   store i16 2, ptr %.loc9_24.3.temp, align 2, !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc9_27.3.temp), !dbg !14
+// CHECK:STDOUT:   store i32 3, ptr %.loc9_27.3.temp, align 4, !dbg !14
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %.loc9_30.3.temp), !dbg !15
+// CHECK:STDOUT:   store i64 4, ptr %.loc9_30.3.temp, align 4, !dbg !15
+// CHECK:STDOUT:   call void @_Z13pass_unsignedhtjm.carbon_thunk(ptr %.loc9_21.3.temp, ptr %.loc9_24.3.temp, ptr %.loc9_27.3.temp, ptr %.loc9_30.3.temp), !dbg !17
+// CHECK:STDOUT:   ret void, !dbg !18
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z11pass_signedasil(i8, i16, i32, i64)
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z13pass_unsignedhtjm(i8, i16, i32, i64)
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare i16 @_CMakeShort.Main()
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CPassShort.Main(i16 %a, ptr %b) !dbg !19 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %.loc17_18.1.temp = alloca i16, align 2, !dbg !20
+// CHECK:STDOUT:   %.loc18_18.3.temp = alloca i16, align 2, !dbg !21
+// CHECK:STDOUT:   %.loc19_18.2.temp = alloca i16, align 2, !dbg !22
+// CHECK:STDOUT:   %.loc20_28.3.temp = alloca i16, align 2, !dbg !23
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 2, ptr %.loc17_18.1.temp), !dbg !20
+// CHECK:STDOUT:   store i16 %a, ptr %.loc17_18.1.temp, align 2, !dbg !20
+// CHECK:STDOUT:   call void @_Z10pass_shorts.carbon_thunk(ptr %.loc17_18.1.temp), !dbg !24
+// CHECK:STDOUT:   %.loc18_18.2 = load i16, ptr %b, align 2, !dbg !21
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 2, ptr %.loc18_18.3.temp), !dbg !21
+// CHECK:STDOUT:   store i16 %.loc18_18.2, ptr %.loc18_18.3.temp, align 2, !dbg !21
+// CHECK:STDOUT:   call void @_Z10pass_shorts.carbon_thunk(ptr %.loc18_18.3.temp), !dbg !25
+// CHECK:STDOUT:   %.loc19_18.1 = load i16, ptr @_Cc.Main, align 2, !dbg !22
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 2, ptr %.loc19_18.2.temp), !dbg !22
+// CHECK:STDOUT:   store i16 %.loc19_18.1, ptr %.loc19_18.2.temp, align 2, !dbg !22
+// CHECK:STDOUT:   call void @_Z10pass_shorts.carbon_thunk(ptr %.loc19_18.2.temp), !dbg !26
+// CHECK:STDOUT:   %MakeShort.call = call i16 @_CMakeShort.Main(), !dbg !23
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 2, ptr %.loc20_28.3.temp), !dbg !23
+// CHECK:STDOUT:   store i16 %MakeShort.call, ptr %.loc20_28.3.temp, align 2, !dbg !23
+// CHECK:STDOUT:   call void @_Z10pass_shorts.carbon_thunk(ptr %.loc20_28.3.temp), !dbg !27
+// CHECK:STDOUT:   ret void, !dbg !28
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z3foosil(i16, i32, i64)
+// CHECK:STDOUT: declare void @_Z10pass_shorts(i16)
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
-// CHECK:STDOUT: define dso_local void @_Z3foosil.carbon_thunk(ptr %x, i32 %y, i64 %z) #1 {
+// CHECK:STDOUT: define dso_local void @_Z11pass_signedasil.carbon_thunk(ptr %0, ptr %1, i32 %2, i64 %3) #1 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %.addr = alloca ptr, align 8
+// CHECK:STDOUT:   %.addr1 = alloca ptr, align 8
+// CHECK:STDOUT:   %.addr2 = alloca i32, align 4
+// CHECK:STDOUT:   %.addr3 = alloca i64, align 8
+// CHECK:STDOUT:   store ptr %0, ptr %.addr, align 8
+// CHECK:STDOUT:   store ptr %1, ptr %.addr1, align 8
+// CHECK:STDOUT:   store i32 %2, ptr %.addr2, align 4
+// CHECK:STDOUT:   store i64 %3, ptr %.addr3, align 8
+// CHECK:STDOUT:   %4 = load ptr, ptr %.addr, align 8
+// CHECK:STDOUT:   %5 = load i8, ptr %4, align 1
+// CHECK:STDOUT:   %6 = load ptr, ptr %.addr1, align 8
+// CHECK:STDOUT:   %7 = load i16, ptr %6, align 2
+// CHECK:STDOUT:   %8 = load i32, ptr %.addr2, align 4
+// CHECK:STDOUT:   %9 = load i64, ptr %.addr3, align 8
+// CHECK:STDOUT:   call void @_Z11pass_signedasil(i8 signext %5, i16 signext %7, i32 %8, i64 %9)
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
+// CHECK:STDOUT: define dso_local void @_Z13pass_unsignedhtjm.carbon_thunk(ptr %0, ptr %1, ptr %2, ptr %3) #1 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %.addr = alloca ptr, align 8
+// CHECK:STDOUT:   %.addr1 = alloca ptr, align 8
+// CHECK:STDOUT:   %.addr2 = alloca ptr, align 8
+// CHECK:STDOUT:   %.addr3 = alloca ptr, align 8
+// CHECK:STDOUT:   store ptr %0, ptr %.addr, align 8
+// CHECK:STDOUT:   store ptr %1, ptr %.addr1, align 8
+// CHECK:STDOUT:   store ptr %2, ptr %.addr2, align 8
+// CHECK:STDOUT:   store ptr %3, ptr %.addr3, align 8
+// CHECK:STDOUT:   %4 = load ptr, ptr %.addr, align 8
+// CHECK:STDOUT:   %5 = load i8, ptr %4, align 1
+// CHECK:STDOUT:   %6 = load ptr, ptr %.addr1, align 8
+// CHECK:STDOUT:   %7 = load i16, ptr %6, align 2
+// CHECK:STDOUT:   %8 = load ptr, ptr %.addr2, align 8
+// CHECK:STDOUT:   %9 = load i32, ptr %8, align 4
+// CHECK:STDOUT:   %10 = load ptr, ptr %.addr3, align 8
+// CHECK:STDOUT:   %11 = load i64, ptr %10, align 8
+// CHECK:STDOUT:   call void @_Z13pass_unsignedhtjm(i8 zeroext %5, i16 zeroext %7, i32 %9, i64 %11)
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
+// CHECK:STDOUT: define dso_local void @_Z10pass_shorts.carbon_thunk(ptr %0) #1 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %x.addr = alloca ptr, align 8
-// CHECK:STDOUT:   %y.addr = alloca i32, align 4
-// CHECK:STDOUT:   %z.addr = alloca i64, align 8
-// CHECK:STDOUT:   store ptr %x, ptr %x.addr, align 8
-// CHECK:STDOUT:   store i32 %y, ptr %y.addr, align 4
-// CHECK:STDOUT:   store i64 %z, ptr %z.addr, align 8
-// CHECK:STDOUT:   %0 = load ptr, ptr %x.addr, align 8
-// CHECK:STDOUT:   %1 = load i16, ptr %0, align 2
-// CHECK:STDOUT:   %2 = load i32, ptr %y.addr, align 4
-// CHECK:STDOUT:   %3 = load i64, ptr %z.addr, align 8
-// CHECK:STDOUT:   call void @_Z3foosil(i16 signext %1, i32 %2, i64 %3)
+// CHECK:STDOUT:   %.addr = alloca ptr, align 8
+// CHECK:STDOUT:   store ptr %0, ptr %.addr, align 8
+// CHECK:STDOUT:   %1 = load ptr, ptr %.addr, align 8
+// CHECK:STDOUT:   %2 = load i16, ptr %1, align 2
+// CHECK:STDOUT:   call void @_Z10pass_shorts(i16 signext %2)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }
+// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
@@ -77,9 +245,189 @@ fn MyF() {
 // CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0}
 // CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2}
 // CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
-// CHECK:STDOUT: !6 = !DIFile(filename: "import_thunk.carbon", directory: "")
+// CHECK:STDOUT: !6 = !DIFile(filename: "import_ints.carbon", directory: "")
 // CHECK:STDOUT: !7 = distinct !DISubprogram(name: "MyF", linkageName: "_CMyF.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5)
 // CHECK:STDOUT: !8 = !DISubroutineType(types: !9)
 // CHECK:STDOUT: !9 = !{}
-// CHECK:STDOUT: !10 = !DILocation(line: 9, column: 3, scope: !7)
-// CHECK:STDOUT: !11 = !DILocation(line: 6, column: 1, scope: !7)
+// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 19, scope: !7)
+// CHECK:STDOUT: !11 = !DILocation(line: 7, column: 22, scope: !7)
+// CHECK:STDOUT: !12 = !DILocation(line: 9, column: 21, scope: !7)
+// CHECK:STDOUT: !13 = !DILocation(line: 9, column: 24, scope: !7)
+// CHECK:STDOUT: !14 = !DILocation(line: 9, column: 27, scope: !7)
+// CHECK:STDOUT: !15 = !DILocation(line: 9, column: 30, scope: !7)
+// CHECK:STDOUT: !16 = !DILocation(line: 7, column: 3, scope: !7)
+// CHECK:STDOUT: !17 = !DILocation(line: 9, column: 3, scope: !7)
+// CHECK:STDOUT: !18 = !DILocation(line: 6, column: 1, scope: !7)
+// CHECK:STDOUT: !19 = distinct !DISubprogram(name: "PassShort", linkageName: "_CPassShort.Main", scope: null, file: !6, line: 16, type: !8, spFlags: DISPFlagDefinition, unit: !5)
+// CHECK:STDOUT: !20 = !DILocation(line: 17, column: 18, scope: !19)
+// CHECK:STDOUT: !21 = !DILocation(line: 18, column: 18, scope: !19)
+// CHECK:STDOUT: !22 = !DILocation(line: 19, column: 18, scope: !19)
+// CHECK:STDOUT: !23 = !DILocation(line: 20, column: 18, scope: !19)
+// CHECK:STDOUT: !24 = !DILocation(line: 17, column: 3, scope: !19)
+// CHECK:STDOUT: !25 = !DILocation(line: 18, column: 3, scope: !19)
+// CHECK:STDOUT: !26 = !DILocation(line: 19, column: 3, scope: !19)
+// CHECK:STDOUT: !27 = !DILocation(line: 20, column: 3, scope: !19)
+// CHECK:STDOUT: !28 = !DILocation(line: 16, column: 1, scope: !19)
+// CHECK:STDOUT: ; ModuleID = 'import_struct.carbon'
+// CHECK:STDOUT: source_filename = "import_struct.carbon"
+// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
+// CHECK:STDOUT:
+// CHECK:STDOUT: %struct.X = type { i32, i32, i32 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTest.Main() !dbg !7 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %x.var = alloca [12 x i8], align 1, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %x.var), !dbg !10
+// CHECK:STDOUT:   %.loc8_4.a = getelementptr inbounds nuw [12 x i8], ptr %x.var, i32 0, i32 0, !dbg !11
+// CHECK:STDOUT:   store i32 1, ptr %.loc8_4.a, align 4, !dbg !11
+// CHECK:STDOUT:   %.loc9_4.b = getelementptr inbounds nuw [12 x i8], ptr %x.var, i32 0, i32 4, !dbg !12
+// CHECK:STDOUT:   store i32 2, ptr %.loc9_4.b, align 4, !dbg !12
+// CHECK:STDOUT:   %.loc10_4.c = getelementptr inbounds nuw [12 x i8], ptr %x.var, i32 0, i32 8, !dbg !13
+// CHECK:STDOUT:   store i32 3, ptr %.loc10_4.c, align 4, !dbg !13
+// CHECK:STDOUT:   call void @_Z11pass_struct1X.carbon_thunk(ptr %x.var), !dbg !14
+// CHECK:STDOUT:   ret void, !dbg !15
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z11pass_struct1X(ptr)
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
+// CHECK:STDOUT: define dso_local void @_Z11pass_struct1X.carbon_thunk(ptr %0) #1 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %.addr = alloca ptr, align 8
+// CHECK:STDOUT:   %agg.tmp = alloca %struct.X, align 4
+// CHECK:STDOUT:   %agg.tmp.coerce = alloca { i64, i32 }, align 4
+// CHECK:STDOUT:   store ptr %0, ptr %.addr, align 8
+// CHECK:STDOUT:   %1 = load ptr, ptr %.addr, align 8
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %1, i64 12, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp.coerce, ptr align 4 %agg.tmp, i64 12, i1 false)
+// CHECK:STDOUT:   %2 = getelementptr inbounds nuw { i64, i32 }, ptr %agg.tmp.coerce, i32 0, i32 0
+// CHECK:STDOUT:   %3 = load i64, ptr %2, align 4
+// CHECK:STDOUT:   %4 = getelementptr inbounds nuw { i64, i32 }, ptr %agg.tmp.coerce, i32 0, i32 1
+// CHECK:STDOUT:   %5 = load i32, ptr %4, align 4
+// CHECK:STDOUT:   call void @_Z11pass_struct1X(i64 %3, i32 %5)
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #2
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
+// CHECK:STDOUT: !llvm.dbg.cu = !{!5}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
+// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
+// CHECK:STDOUT: !2 = !{i32 1, !"wchar_size", i32 4}
+// CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0}
+// CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2}
+// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+// CHECK:STDOUT: !6 = !DIFile(filename: "import_struct.carbon", directory: "")
+// CHECK:STDOUT: !7 = distinct !DISubprogram(name: "Test", linkageName: "_CTest.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5)
+// CHECK:STDOUT: !8 = !DISubroutineType(types: !9)
+// CHECK:STDOUT: !9 = !{}
+// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 3, scope: !7)
+// CHECK:STDOUT: !11 = !DILocation(line: 8, column: 3, scope: !7)
+// CHECK:STDOUT: !12 = !DILocation(line: 9, column: 3, scope: !7)
+// CHECK:STDOUT: !13 = !DILocation(line: 10, column: 3, scope: !7)
+// CHECK:STDOUT: !14 = !DILocation(line: 11, column: 3, scope: !7)
+// CHECK:STDOUT: !15 = !DILocation(line: 6, column: 1, scope: !7)
+// CHECK:STDOUT: ; ModuleID = 'import_class_with_nontrivial_copy.carbon'
+// CHECK:STDOUT: source_filename = "import_class_with_nontrivial_copy.carbon"
+// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
+// CHECK:STDOUT:
+// CHECK:STDOUT: %class.Y = type { i32, i32, i32 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CPassRefExpr.Main() !dbg !7 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %y.var = alloca [12 x i8], align 1, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %y.var), !dbg !10
+// CHECK:STDOUT:   %.loc8_4.a = getelementptr inbounds nuw [12 x i8], ptr %y.var, i32 0, i32 0, !dbg !11
+// CHECK:STDOUT:   store i32 1, ptr %.loc8_4.a, align 4, !dbg !11
+// CHECK:STDOUT:   %.loc9_4.b = getelementptr inbounds nuw [12 x i8], ptr %y.var, i32 0, i32 4, !dbg !12
+// CHECK:STDOUT:   store i32 2, ptr %.loc9_4.b, align 4, !dbg !12
+// CHECK:STDOUT:   %.loc10_4.c = getelementptr inbounds nuw [12 x i8], ptr %y.var, i32 0, i32 8, !dbg !13
+// CHECK:STDOUT:   store i32 3, ptr %.loc10_4.c, align 4, !dbg !13
+// CHECK:STDOUT:   call void @_Z11pass_struct1Y.carbon_thunk(ptr %y.var), !dbg !14
+// CHECK:STDOUT:   ret void, !dbg !15
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z11pass_struct1Y(ptr)
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_CMake.Main(ptr sret([12 x i8]))
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CPassInitExpr.Main() !dbg !16 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %.loc17_24.1.temp = alloca [12 x i8], align 1, !dbg !17
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %.loc17_24.1.temp), !dbg !17
+// CHECK:STDOUT:   call void @_CMake.Main(ptr %.loc17_24.1.temp), !dbg !17
+// CHECK:STDOUT:   call void @_Z11pass_struct1Y.carbon_thunk(ptr %.loc17_24.1.temp), !dbg !18
+// CHECK:STDOUT:   ret void, !dbg !19
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CPassValueExpr.Main(ptr %y) !dbg !20 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @_Z11pass_struct1Y.carbon_thunk(ptr %y), !dbg !21
+// CHECK:STDOUT:   ret void, !dbg !22
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
+// CHECK:STDOUT: define dso_local void @_Z11pass_struct1Y.carbon_thunk(ptr %0) #1 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %.addr = alloca ptr, align 8
+// CHECK:STDOUT:   %agg.tmp = alloca %class.Y, align 4
+// CHECK:STDOUT:   store ptr %0, ptr %.addr, align 8
+// CHECK:STDOUT:   %1 = load ptr, ptr %.addr, align 8
+// CHECK:STDOUT:   call void @_ZN1YC1ERKS_(ptr nonnull align 4 dereferenceable(12) %agg.tmp, ptr nonnull align 4 dereferenceable(12) %1)
+// CHECK:STDOUT:   call void @_Z11pass_struct1Y(ptr %agg.tmp)
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_ZN1YC1ERKS_(ptr nonnull align 4 dereferenceable(12), ptr nonnull align 4 dereferenceable(12)) unnamed_addr #2
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
+// CHECK:STDOUT: !llvm.dbg.cu = !{!5}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
+// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
+// CHECK:STDOUT: !2 = !{i32 1, !"wchar_size", i32 4}
+// CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0}
+// CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2}
+// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+// CHECK:STDOUT: !6 = !DIFile(filename: "import_class_with_nontrivial_copy.carbon", directory: "")
+// CHECK:STDOUT: !7 = distinct !DISubprogram(name: "PassRefExpr", linkageName: "_CPassRefExpr.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5)
+// CHECK:STDOUT: !8 = !DISubroutineType(types: !9)
+// CHECK:STDOUT: !9 = !{}
+// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 3, scope: !7)
+// CHECK:STDOUT: !11 = !DILocation(line: 8, column: 3, scope: !7)
+// CHECK:STDOUT: !12 = !DILocation(line: 9, column: 3, scope: !7)
+// CHECK:STDOUT: !13 = !DILocation(line: 10, column: 3, scope: !7)
+// CHECK:STDOUT: !14 = !DILocation(line: 11, column: 3, scope: !7)
+// CHECK:STDOUT: !15 = !DILocation(line: 6, column: 1, scope: !7)
+// CHECK:STDOUT: !16 = distinct !DISubprogram(name: "PassInitExpr", linkageName: "_CPassInitExpr.Main", scope: null, file: !6, line: 16, type: !8, spFlags: DISPFlagDefinition, unit: !5)
+// CHECK:STDOUT: !17 = !DILocation(line: 17, column: 19, scope: !16)
+// CHECK:STDOUT: !18 = !DILocation(line: 17, column: 3, scope: !16)
+// CHECK:STDOUT: !19 = !DILocation(line: 16, column: 1, scope: !16)
+// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "PassValueExpr", linkageName: "_CPassValueExpr.Main", scope: null, file: !6, line: 20, type: !8, spFlags: DISPFlagDefinition, unit: !5)
+// CHECK:STDOUT: !21 = !DILocation(line: 21, column: 3, scope: !20)
+// CHECK:STDOUT: !22 = !DILocation(line: 20, column: 1, scope: !20)

+ 1 - 1
toolchain/sem_ir/typed_insts.h

@@ -1776,7 +1776,7 @@ struct UnboundElementType {
 // example, when indexing a value expression of array type, this is used to
 // form a reference to the array object.
 struct ValueAsRef {
-  static constexpr auto Kind = InstKind::ValueAsRef.Define<Parse::IndexExprId>(
+  static constexpr auto Kind = InstKind::ValueAsRef.Define<Parse::NodeId>(
       {.ir_name = "value_as_ref", .constant_kind = InstConstantKind::Never});
 
   TypeId type_id;