ソースを参照

Fix lowering of thunks in generic impls (#5631)

Build a `SpecificConstant` (if needed) and `NameRef` instruction when
referencing the thunk target from a thunk. The former is necessary if
the impl is generic in order to call the right version of the thunk
target. This previously caused a crash in lowering.

Also add some more check testing for the interaction of thunks and
generics. This testing uncovered an unrelated bug with thunks for
generic interface functions for which I've added a TODO.
Richard Smith 10 ヶ月 前
コミット
e24ba02352

+ 2 - 0
toolchain/check/BUILD

@@ -44,6 +44,7 @@ cc_library(
         "modifiers.cpp",
         "name_component.cpp",
         "name_lookup.cpp",
+        "name_ref.cpp",
         "operator.cpp",
         "pattern.cpp",
         "pattern_match.cpp",
@@ -89,6 +90,7 @@ cc_library(
         "modifiers.h",
         "name_component.h",
         "name_lookup.h",
+        "name_ref.h",
         "operator.h",
         "param_and_arg_refs_stack.h",
         "pattern.h",

+ 3 - 20
toolchain/check/handle_name.cpp

@@ -9,6 +9,7 @@
 #include "toolchain/check/member_access.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/name_ref.h"
 #include "toolchain/check/pointer_dereference.h"
 #include "toolchain/check/type.h"
 #include "toolchain/lex/token_kind.h"
@@ -109,26 +110,8 @@ static auto GetIdentifierAsNameId(
 static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id,
                              SemIR::NameId name_id) -> SemIR::InstId {
   auto result = LookupUnqualifiedName(context, node_id, name_id);
-  SemIR::InstId inst_id = result.scope_result.target_inst_id();
-  auto type_id = SemIR::GetTypeOfInstInSpecific(context.sem_ir(),
-                                                result.specific_id, inst_id);
-  CARBON_CHECK(type_id.has_value(), "Missing type for {0}",
-               context.insts().Get(inst_id));
-
-  // If the named entity has a constant value that depends on its specific,
-  // store the specific too.
-  if (result.specific_id.has_value() &&
-      context.constant_values().Get(inst_id).is_symbolic()) {
-    inst_id =
-        AddInst<SemIR::SpecificConstant>(context, node_id,
-                                         {.type_id = type_id,
-                                          .inst_id = inst_id,
-                                          .specific_id = result.specific_id});
-  }
-
-  return AddInst<SemIR::NameRef>(
-      context, node_id,
-      {.type_id = type_id, .name_id = name_id, .value_id = inst_id});
+  return BuildNameRef(context, node_id, name_id,
+                      result.scope_result.target_inst_id(), result.specific_id);
 }
 
 auto HandleParseNode(Context& context,

+ 33 - 0
toolchain/check/name_ref.cpp

@@ -0,0 +1,33 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/check/name_ref.h"
+
+#include "toolchain/check/inst.h"
+
+namespace Carbon::Check {
+
+auto BuildNameRef(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id,
+                  SemIR::InstId inst_id, SemIR::SpecificId specific_id)
+    -> SemIR::InstId {
+  auto type_id =
+      SemIR::GetTypeOfInstInSpecific(context.sem_ir(), specific_id, inst_id);
+  CARBON_CHECK(type_id.has_value(), "Missing type for {0}",
+               context.insts().Get(inst_id));
+
+  // If the named entity has a constant value that depends on its specific,
+  // store the specific too.
+  if (specific_id.has_value() &&
+      context.constant_values().Get(inst_id).is_symbolic()) {
+    inst_id = AddInst<SemIR::SpecificConstant>(
+        context, loc_id,
+        {.type_id = type_id, .inst_id = inst_id, .specific_id = specific_id});
+  }
+
+  return AddInst<SemIR::NameRef>(
+      context, loc_id,
+      {.type_id = type_id, .name_id = name_id, .value_id = inst_id});
+}
+
+}  // namespace Carbon::Check

+ 21 - 0
toolchain/check/name_ref.h

@@ -0,0 +1,21 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_CHECK_NAME_REF_H_
+#define CARBON_TOOLCHAIN_CHECK_NAME_REF_H_
+
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+// Builds a reference to the given name, which has already been resolved to
+// `inst_id` within `specific_id`.
+auto BuildNameRef(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id,
+                  SemIR::InstId inst_id, SemIR::SpecificId specific_id)
+    -> SemIR::InstId;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_NAME_REF_H_

+ 2 - 1
toolchain/check/testdata/impl/fail_call_invalid.carbon

@@ -150,8 +150,9 @@ fn InstanceCall(n: i32) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G.3(%self.param: %i32) {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %G.ref: %G.type.c9825d.1 = name_ref G, @impl.006.%G.decl.loc23_27.1 [concrete = constants.%G.e73e91.1]
 // CHECK:STDOUT:   %self.ref: %i32 = name_ref self, %self.param
-// CHECK:STDOUT:   %G.bound: <bound method> = bound_method %self.ref, @impl.006.%G.decl.loc23_27.1
+// CHECK:STDOUT:   %G.bound: <bound method> = bound_method %self.ref, %G.ref
 // CHECK:STDOUT:   %G.call: init %empty_tuple.type = call %G.bound(<error>)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 19 - 9
toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon

@@ -1201,6 +1201,7 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.3() {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.44ef8c.1 = name_ref F, @impl.ddd.%F.decl.loc68_18.1 [concrete = constants.%F.424e9e.1]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1208,7 +1209,8 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.5() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %F.call: init %empty_tuple.type = call @impl.698.%F.decl.loc84_23.1(<error>)
+// CHECK:STDOUT:   %F.ref: %F.type.e1abdd.1 = name_ref F, @impl.698.%F.decl.loc84_23.1 [concrete = constants.%F.6ff574.1]
+// CHECK:STDOUT:   %F.call: init %empty_tuple.type = call %F.ref(<error>)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1222,8 +1224,9 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.9(%self.param: bool, %b.param: bool) -> bool {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.69596c.1 = name_ref F, @impl.5cf.%F.decl.loc116_31.1 [concrete = constants.%F.738f31.1]
 // CHECK:STDOUT:   %self.ref: bool = name_ref self, %self.param
-// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, @impl.5cf.%F.decl.loc116_31.1
+// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, %F.ref
 // CHECK:STDOUT:   %b.ref: bool = name_ref b, %b.param
 // CHECK:STDOUT:   return <error>
 // CHECK:STDOUT: }
@@ -1232,9 +1235,10 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.11(%self.param: bool, %b.param: bool) -> bool {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.d97cef.1 = name_ref F, @impl.bac.%F.decl.loc129_26.1 [concrete = constants.%F.01de92.1]
 // CHECK:STDOUT:   %self.ref: bool = name_ref self, %self.param
 // CHECK:STDOUT:   %b.ref: bool = name_ref b, %b.param
-// CHECK:STDOUT:   %F.call: init bool = call @impl.bac.%F.decl.loc129_26.1(%b.ref)
+// CHECK:STDOUT:   %F.call: init bool = call %F.ref(%b.ref)
 // CHECK:STDOUT:   %.loc129_26.1: bool = value_of_initializer %F.call
 // CHECK:STDOUT:   %.loc129_26.2: bool = converted %F.call, %.loc129_26.1
 // CHECK:STDOUT:   return %.loc129_26.2
@@ -1244,8 +1248,9 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.13(%self.param: bool, %b.param: bool) -> bool {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.123d7a.1 = name_ref F, @impl.1a7.%F.decl.loc151_30.1 [concrete = constants.%F.c7d02d.1]
 // CHECK:STDOUT:   %self.ref: bool = name_ref self, %self.param
-// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, @impl.1a7.%F.decl.loc151_30.1
+// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, %F.ref
 // CHECK:STDOUT:   %b.ref: bool = name_ref b, %b.param
 // CHECK:STDOUT:   %F.call: init %empty_tuple.type = call %F.bound(%self.ref, %b.ref)
 // CHECK:STDOUT:   %.loc151: bool = converted %F.call, <error> [concrete = <error>]
@@ -1256,8 +1261,9 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.15(%self.param: bool, %b.param: bool) -> bool {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.6b537d.1 = name_ref F, @impl.f2b.%F.decl.loc170_38.1 [concrete = constants.%F.04313a.1]
 // CHECK:STDOUT:   %self.ref: bool = name_ref self, %self.param
-// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, @impl.f2b.%F.decl.loc170_38.1
+// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, %F.ref
 // CHECK:STDOUT:   %b.ref: bool = name_ref b, %b.param
 // CHECK:STDOUT:   %.loc102: %FDifferentParamType = converted %b.ref, <error> [concrete = <error>]
 // CHECK:STDOUT:   %F.call: init bool = call %F.bound(%self.ref, <error>)
@@ -1270,8 +1276,9 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.17(%self.param: bool, %b.param: bool) -> bool {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.d6232a.1 = name_ref F, @impl.db4.%F.decl.loc183_38.1 [concrete = constants.%F.886f70.1]
 // CHECK:STDOUT:   %self.ref: bool = name_ref self, %self.param
-// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, @impl.db4.%F.decl.loc183_38.1
+// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, %F.ref
 // CHECK:STDOUT:   %b.ref: bool = name_ref b, %b.param
 // CHECK:STDOUT:   %.loc102: %FDifferentImplicitParamType = converted %self.ref, <error> [concrete = <error>]
 // CHECK:STDOUT:   %F.call: init bool = call %F.bound(<error>, %b.ref)
@@ -1284,8 +1291,9 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.19(%self.param: bool, %b.param: bool) -> bool {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.d3b58f.1 = name_ref F, @impl.fcc.%F.decl.loc199_38.1 [concrete = constants.%F.be86c9.1]
 // CHECK:STDOUT:   %self.ref: bool = name_ref self, %self.param
-// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, @impl.fcc.%F.decl.loc199_38.1
+// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, %F.ref
 // CHECK:STDOUT:   %b.ref: bool = name_ref b, %b.param
 // CHECK:STDOUT:   %.loc199_38.1: ref %FDifferentReturnType = temporary_storage
 // CHECK:STDOUT:   %F.call: init %FDifferentReturnType = call %F.bound(%self.ref, %b.ref) to %.loc199_38.1
@@ -1310,13 +1318,14 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.22(%x.param: %tuple.type.9c9) -> %return.param: %array_type.a41 {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.f90165.1 = name_ref F, @impl.6a5.%F.decl.loc222_87.1 [concrete = constants.%F.fa8d04.1]
 // CHECK:STDOUT:   %x.ref: %tuple.type.9c9 = name_ref x, %x.param
 // CHECK:STDOUT:   %.loc210_41: ref %array_type.a41 = splice_block %return {}
 // CHECK:STDOUT:   %tuple.elem0: %ptr.4cd = tuple_access %x.ref, element0
 // CHECK:STDOUT:   %tuple.elem1: %struct_type.x.y.a89 = tuple_access %x.ref, element1
 // CHECK:STDOUT:   %.loc210_9.1: %SelfNestedBadParam = struct_access %tuple.elem1, element0
 // CHECK:STDOUT:   %.loc210_9.2: %i32 = converted %.loc210_9.1, <error> [concrete = <error>]
-// CHECK:STDOUT:   %F.call: init %array_type.a41 = call @impl.6a5.%F.decl.loc222_87.1(<error>) to %.loc210_41
+// CHECK:STDOUT:   %F.call: init %array_type.a41 = call %F.ref(<error>) to %.loc210_41
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1324,9 +1333,10 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.24(%x.param: %tuple.type.eb9) -> %return.param: %array_type.126 {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.0e7d1d.1 = name_ref F, @impl.bfc.%F.decl.loc238_112.1 [concrete = constants.%F.0bc78a.1]
 // CHECK:STDOUT:   %x.ref: %tuple.type.eb9 = name_ref x, %x.param
 // CHECK:STDOUT:   %.loc238_112.1: ref %array_type.a41 = temporary_storage
-// CHECK:STDOUT:   %F.call: init %array_type.a41 = call @impl.bfc.%F.decl.loc238_112.1(%x.ref) to %.loc238_112.1
+// CHECK:STDOUT:   %F.call: init %array_type.a41 = call %F.ref(%x.ref) to %.loc238_112.1
 // CHECK:STDOUT:   %.loc238_112.2: %array_type.126 = converted %F.call, <error> [concrete = <error>]
 // CHECK:STDOUT:   return <error> to %return
 // CHECK:STDOUT: }

+ 2 - 1
toolchain/check/testdata/impl/fail_self_type_mismatch.carbon

@@ -242,9 +242,10 @@ impl i32 as I {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.4(%c.param: %C.d88) {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.066a53.1 = name_ref F, @impl.a9a.%F.decl.loc42_18.1 [concrete = constants.%F.9ec58f.1]
 // CHECK:STDOUT:   %c.ref: %C.d88 = name_ref c, %c.param
 // CHECK:STDOUT:   %.loc24: %C.6fb = converted %c.ref, <error> [concrete = <error>]
-// CHECK:STDOUT:   %F.call: init %empty_tuple.type = call @impl.a9a.%F.decl.loc42_18.1(<error>)
+// CHECK:STDOUT:   %F.call: init %empty_tuple.type = call %F.ref(<error>)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

ファイルの差分が大きいため隠しています
+ 154 - 767
toolchain/check/testdata/impl/impl_thunk.carbon


+ 3 - 1
toolchain/check/testdata/impl/import_thunk.carbon

@@ -249,8 +249,10 @@ fn G() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%x.param: %empty_struct_type) {
 // CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %.loc8: @F.3.%F.type (%F.type.0daaa1.1) = specific_constant @impl.%F.decl.loc8_17.1, @impl(constants.%Y) [symbolic = %F (constants.%F.49c1ac.1)]
+// CHECK:STDOUT:     %F.ref: @F.3.%F.type (%F.type.0daaa1.1) = name_ref F, %.loc8 [symbolic = %F (constants.%F.49c1ac.1)]
 // CHECK:STDOUT:     %x.ref: %empty_struct_type = name_ref x, %x.param
-// CHECK:STDOUT:     %F.specific_fn.loc8_17.1: <specific function> = specific_function @impl.%F.decl.loc8_17.1, @F.2(constants.%Y) [symbolic = %F.specific_fn.loc8_17.2 (constants.%F.specific_fn)]
+// CHECK:STDOUT:     %F.specific_fn.loc8_17.1: <specific function> = specific_function %F.ref, @F.2(constants.%Y) [symbolic = %F.specific_fn.loc8_17.2 (constants.%F.specific_fn)]
 // CHECK:STDOUT:     %.1: ref @F.3.%C (%C.13320f.2) = temporary_storage
 // CHECK:STDOUT:     %.2: init @F.3.%C (%C.13320f.2) = class_init (), %.1 [symbolic = %C.val (constants.%C.val)]
 // CHECK:STDOUT:     %.3: ref @F.3.%C (%C.13320f.2) = temporary %.1, %.2

+ 6 - 2
toolchain/check/testdata/impl/use_assoc_const.carbon

@@ -2422,8 +2422,9 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.3(%u.param: %i32) -> %i32 {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.b842fd.1 = name_ref F, @impl.%F.decl.loc24_21.1 [concrete = constants.%F.b07d12.1]
 // CHECK:STDOUT:   %u.ref: %i32 = name_ref u, %u.param
-// CHECK:STDOUT:   %F.call: init <error> = call @impl.%F.decl.loc24_21.1(<error>)
+// CHECK:STDOUT:   %F.call: init <error> = call %F.ref(<error>)
 // CHECK:STDOUT:   return <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -2689,6 +2690,7 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.3(%self.param: %empty_tuple.type, %.param: <error>) -> <error> {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.f405c5.1 = name_ref F, @impl.4e9.%F.decl.loc30_33.1 [concrete = constants.%F.214a71.1]
 // CHECK:STDOUT:   %self.ref: %empty_tuple.type = name_ref self, %self.param
 // CHECK:STDOUT:   %.ref: <error> = name_ref <none>, %.param [concrete = <error>]
 // CHECK:STDOUT:   return <error>
@@ -2702,8 +2704,9 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.5(%self.param: %C2, %.param: <error>) -> <error> {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.05d6a0.1 = name_ref F, @impl.8b3.%F.decl.loc39_33.1 [concrete = constants.%F.bfa759.1]
 // CHECK:STDOUT:   %self.ref: %C2 = name_ref self, %self.param
-// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, @impl.8b3.%F.decl.loc39_33.1
+// CHECK:STDOUT:   %F.bound: <bound method> = bound_method %self.ref, %F.ref
 // CHECK:STDOUT:   %.ref: <error> = name_ref <none>, %.param [concrete = <error>]
 // CHECK:STDOUT:   %F.call: init %C2 = call %F.bound(%self.ref, <error>)
 // CHECK:STDOUT:   return <error>
@@ -2912,6 +2915,7 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.3(%self.param: %empty_tuple.type, %v.param: %struct_type.a) -> %struct_type.a {
 // CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type.c5249c.1 = name_ref F, @impl.%F.decl.loc16_45.1 [concrete = constants.%F.5eb4dd.1]
 // CHECK:STDOUT:   %self.ref: %empty_tuple.type = name_ref self, %self.param
 // CHECK:STDOUT:   %v.ref: %struct_type.a = name_ref v, %v.param
 // CHECK:STDOUT:   return <error>

+ 7 - 0
toolchain/check/thunk.cpp

@@ -15,6 +15,7 @@
 #include "toolchain/check/generic.h"
 #include "toolchain/check/inst.h"
 #include "toolchain/check/member_access.h"
+#include "toolchain/check/name_ref.h"
 #include "toolchain/check/pattern.h"
 #include "toolchain/check/pattern_match.h"
 #include "toolchain/check/pointer_dereference.h"
@@ -333,6 +334,12 @@ static auto BuildThunkCall(Context& context, SemIR::FunctionId function_id,
   auto loc_id = SemIR::LocId(callee_id);
   auto& function = context.functions().Get(function_id);
 
+  // Build a `NameRef` naming the callee, and a `SpecificConstant` if needed.
+  auto callee_type = context.types().GetAs<SemIR::FunctionType>(
+      context.insts().Get(callee_id).type_id());
+  callee_id = BuildNameRef(context, loc_id, function.name_id, callee_id,
+                           callee_type.specific_id);
+
   // If we have a self parameter, form `self.<callee_id>`.
   if (function.self_param_id.has_value()) {
     callee_id = PerformCompoundMemberAccess(

+ 152 - 36
toolchain/lower/testdata/impl/thunk.carbon

@@ -8,6 +8,10 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/impl/thunk.carbon
 
+// --- thunk.carbon
+
+library "[[@TEST_NAME]]";
+
 class A { var a: i32; }
 class B { var b: i32; }
 class C { var c: i32; }
@@ -31,44 +35,77 @@ fn Test(a: A) -> C {
   return ().(I.F)(a);
 }
 
+// --- generic_thunk.carbon
+
+library "[[@TEST_NAME]]";
+
+class A {}
+class B {
+  impl as Core.ImplicitAs(A) {
+    fn Convert[self: Self]() -> A { return {}; }
+  }
+}
+
+interface I(T:! type) {
+  fn F[self: Self](x: B) -> A;
+}
+
+class C(U:! type) {}
+
+impl forall [V:! type] C(V) as I(V) {
+  fn F[self: Self](x: A) -> B { return {}; }
+}
+
+fn Call(c: C(()), b: B) -> A {
+  return c.(I(()).F)(b);
+}
+
+fn CallGeneric[T:! I(())](c: T, b: B) -> A {
+  return c.F(b);
+}
+
+fn CallCallGeneric(c: C(()), b: B) -> A {
+  return CallGeneric(c, b);
+}
+
 // CHECK:STDOUT: ; ModuleID = 'thunk.carbon'
 // CHECK:STDOUT: source_filename = "thunk.carbon"
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @"_CConvert.A.Main:ImplicitAs.Core"(ptr sret({ i32 }) %return, ptr %self) !dbg !4 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %.loc16_49.1.a = getelementptr inbounds nuw { i32 }, ptr %self, i32 0, i32 0, !dbg !7
-// CHECK:STDOUT:   %.loc16_49.2 = load i32, ptr %.loc16_49.1.a, align 4, !dbg !7
-// CHECK:STDOUT:   %.loc16_51.2.b = getelementptr inbounds nuw { i32 }, ptr %return, i32 0, i32 0, !dbg !8
-// CHECK:STDOUT:   store i32 %.loc16_49.2, ptr %.loc16_51.2.b, align 4, !dbg !8
+// CHECK:STDOUT:   %.loc9_49.1.a = getelementptr inbounds nuw { i32 }, ptr %self, i32 0, i32 0, !dbg !7
+// CHECK:STDOUT:   %.loc9_49.2 = load i32, ptr %.loc9_49.1.a, align 4, !dbg !7
+// CHECK:STDOUT:   %.loc9_51.2.b = getelementptr inbounds nuw { i32 }, ptr %return, i32 0, i32 0, !dbg !8
+// CHECK:STDOUT:   store i32 %.loc9_49.2, ptr %.loc9_51.2.b, align 4, !dbg !8
 // CHECK:STDOUT:   ret void, !dbg !9
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @"_CConvert.B.Main:ImplicitAs.Core"(ptr sret({ i32 }) %return, ptr %self) !dbg !10 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %.loc19_49.1.b = getelementptr inbounds nuw { i32 }, ptr %self, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   %.loc19_49.2 = load i32, ptr %.loc19_49.1.b, align 4, !dbg !11
-// CHECK:STDOUT:   %.loc19_51.2.c = getelementptr inbounds nuw { i32 }, ptr %return, i32 0, i32 0, !dbg !12
-// CHECK:STDOUT:   store i32 %.loc19_49.2, ptr %.loc19_51.2.c, align 4, !dbg !12
+// CHECK:STDOUT:   %.loc12_49.1.b = getelementptr inbounds nuw { i32 }, ptr %self, i32 0, i32 0, !dbg !11
+// CHECK:STDOUT:   %.loc12_49.2 = load i32, ptr %.loc12_49.1.b, align 4, !dbg !11
+// CHECK:STDOUT:   %.loc12_51.2.c = getelementptr inbounds nuw { i32 }, ptr %return, i32 0, i32 0, !dbg !12
+// CHECK:STDOUT:   store i32 %.loc12_49.2, ptr %.loc12_51.2.c, align 4, !dbg !12
 // CHECK:STDOUT:   ret void, !dbg !13
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @"_CF.61ea2aba74ab3bf1:I.Main"(ptr sret({ i32 }) %return, ptr %b) !dbg !14 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %.loc27_35.1.b = getelementptr inbounds nuw { i32 }, ptr %b, i32 0, i32 0, !dbg !15
-// CHECK:STDOUT:   %.loc27_35.2 = load i32, ptr %.loc27_35.1.b, align 4, !dbg !15
-// CHECK:STDOUT:   %.loc27_37.2.b = getelementptr inbounds nuw { i32 }, ptr %return, i32 0, i32 0, !dbg !16
-// CHECK:STDOUT:   store i32 %.loc27_35.2, ptr %.loc27_37.2.b, align 4, !dbg !16
+// CHECK:STDOUT:   %.loc20_35.1.b = getelementptr inbounds nuw { i32 }, ptr %b, i32 0, i32 0, !dbg !15
+// CHECK:STDOUT:   %.loc20_35.2 = load i32, ptr %.loc20_35.1.b, align 4, !dbg !15
+// CHECK:STDOUT:   %.loc20_37.2.b = getelementptr inbounds nuw { i32 }, ptr %return, i32 0, i32 0, !dbg !16
+// CHECK:STDOUT:   store i32 %.loc20_35.2, ptr %.loc20_37.2.b, align 4, !dbg !16
 // CHECK:STDOUT:   ret void, !dbg !17
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @"_CF:thunk.61ea2aba74ab3bf1:I.Main"(ptr sret({ i32 }) %return, ptr %a) !dbg !18 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %.loc27_19.1.temp = alloca { i32 }, align 8, !dbg !19
-// CHECK:STDOUT:   %.loc23_9.1.temp = alloca { i32 }, align 8, !dbg !20
-// CHECK:STDOUT:   call void @"_CConvert.A.Main:ImplicitAs.Core"(ptr %.loc23_9.1.temp, ptr %a), !dbg !20
-// CHECK:STDOUT:   call void @"_CF.61ea2aba74ab3bf1:I.Main"(ptr %.loc27_19.1.temp, ptr %.loc23_9.1.temp), !dbg !19
-// CHECK:STDOUT:   %.loc27_19.2.temp = alloca { i32 }, align 8, !dbg !19
-// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc27_19.2.temp, ptr %.loc27_19.1.temp), !dbg !19
+// CHECK:STDOUT:   %.loc20_19.1.temp = alloca { i32 }, align 8, !dbg !19
+// CHECK:STDOUT:   %.loc16_9.1.temp = alloca { i32 }, align 8, !dbg !20
+// CHECK:STDOUT:   call void @"_CConvert.A.Main:ImplicitAs.Core"(ptr %.loc16_9.1.temp, ptr %a), !dbg !20
+// CHECK:STDOUT:   call void @"_CF.61ea2aba74ab3bf1:I.Main"(ptr %.loc20_19.1.temp, ptr %.loc16_9.1.temp), !dbg !19
+// CHECK:STDOUT:   %.loc20_19.2.temp = alloca { i32 }, align 8, !dbg !19
+// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc20_19.2.temp, ptr %.loc20_19.1.temp), !dbg !19
 // CHECK:STDOUT:   ret void, !dbg !19
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -85,23 +122,102 @@ fn Test(a: A) -> C {
 // CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
 // CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
 // CHECK:STDOUT: !3 = !DIFile(filename: "thunk.carbon", directory: "")
-// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Convert", linkageName: "_CConvert.A.Main:ImplicitAs.Core", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Convert", linkageName: "_CConvert.A.Main:ImplicitAs.Core", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
+// CHECK:STDOUT: !6 = !{}
+// CHECK:STDOUT: !7 = !DILocation(line: 9, column: 45, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 9, column: 39, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 9, column: 32, scope: !4)
+// CHECK:STDOUT: !10 = distinct !DISubprogram(name: "Convert", linkageName: "_CConvert.B.Main:ImplicitAs.Core", scope: null, file: !3, line: 12, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !11 = !DILocation(line: 12, column: 45, scope: !10)
+// CHECK:STDOUT: !12 = !DILocation(line: 12, column: 39, scope: !10)
+// CHECK:STDOUT: !13 = !DILocation(line: 12, column: 32, scope: !10)
+// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "F", linkageName: "_CF.61ea2aba74ab3bf1:I.Main", scope: null, file: !3, line: 20, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !15 = !DILocation(line: 20, column: 34, scope: !14)
+// CHECK:STDOUT: !16 = !DILocation(line: 20, column: 28, scope: !14)
+// CHECK:STDOUT: !17 = !DILocation(line: 20, column: 21, scope: !14)
+// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "F", linkageName: "_CF:thunk.61ea2aba74ab3bf1:I.Main", scope: null, file: !3, line: 20, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !19 = !DILocation(line: 20, column: 3, scope: !18)
+// CHECK:STDOUT: !20 = !DILocation(line: 16, column: 8, scope: !18)
+// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "Test", linkageName: "_CTest.Main", scope: null, file: !3, line: 23, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !22 = !DILocation(line: 24, column: 10, scope: !21)
+// CHECK:STDOUT: !23 = !DILocation(line: 24, column: 3, scope: !21)
+// CHECK:STDOUT: ; ModuleID = 'generic_thunk.carbon'
+// CHECK:STDOUT: source_filename = "generic_thunk.carbon"
+// CHECK:STDOUT:
+// CHECK:STDOUT: @A.val.loc7_46 = internal constant {} zeroinitializer
+// CHECK:STDOUT: @B.val.loc18_42 = internal constant {} zeroinitializer
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @"_CConvert.B.Main:ImplicitAs.Core"(ptr sret({}) %return, ptr %self) !dbg !4 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %return, ptr align 1 @A.val.loc7_46, i64 0, i1 false), !dbg !7
+// CHECK:STDOUT:   ret void, !dbg !7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CCall.Main(ptr sret({}) %return, ptr %c, ptr %b) !dbg !8 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @"_CF:thunk.C.Main:I.Main.e43630e9a6c38c3f"(ptr %return, ptr %c, ptr %b), !dbg !9
+// CHECK:STDOUT:   ret void, !dbg !10
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CCallCallGeneric.Main(ptr sret({}) %return, ptr %c, ptr %b) !dbg !11 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @_CCallGeneric.Main.9ef6968dffa77413(ptr %return, ptr %c, ptr %b), !dbg !12
+// CHECK:STDOUT:   ret void, !dbg !13
+// 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) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: define linkonce_odr void @"_CF:thunk.C.Main:I.Main.e43630e9a6c38c3f"(ptr sret({}) %return, ptr %self, ptr %x) !dbg !14 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %.loc18_31.2.temp = alloca {}, align 8, !dbg !15
+// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc18_31.2.temp, ptr %x), !dbg !15
+// CHECK:STDOUT:   %.loc18_31.6.temp = alloca {}, align 8, !dbg !15
+// CHECK:STDOUT:   %.loc12_21.1.temp = alloca {}, align 8, !dbg !16
+// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc12_21.1.temp, ptr %x), !dbg !16
+// CHECK:STDOUT:   call void @"_CF.C.Main:I.Main.e43630e9a6c38c3f"(ptr %.loc18_31.6.temp, ptr %self, ptr %.loc12_21.1.temp), !dbg !15
+// CHECK:STDOUT:   %.loc18_31.7.temp = alloca {}, align 8, !dbg !15
+// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc18_31.7.temp, ptr %.loc18_31.6.temp), !dbg !15
+// CHECK:STDOUT:   ret void, !dbg !15
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define linkonce_odr void @_CCallGeneric.Main.9ef6968dffa77413(ptr sret({}) %return, ptr %c, ptr %b) !dbg !17 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @"_CF:thunk.C.Main:I.Main.e43630e9a6c38c3f"(ptr %return, ptr %c, ptr %b), !dbg !18
+// CHECK:STDOUT:   ret void, !dbg !19
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define linkonce_odr void @"_CF.C.Main:I.Main.e43630e9a6c38c3f"(ptr sret({}) %return, ptr %self, ptr %x) !dbg !20 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %return, ptr align 1 @B.val.loc18_42, i64 0, i1 false), !dbg !21
+// CHECK:STDOUT:   ret void, !dbg !21
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
+// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
+// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
+// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+// CHECK:STDOUT: !3 = !DIFile(filename: "generic_thunk.carbon", directory: "")
+// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Convert", linkageName: "_CConvert.B.Main:ImplicitAs.Core", scope: null, file: !3, line: 7, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
 // CHECK:STDOUT: !6 = !{}
-// CHECK:STDOUT: !7 = !DILocation(line: 16, column: 45, scope: !4)
-// CHECK:STDOUT: !8 = !DILocation(line: 16, column: 39, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 16, column: 32, scope: !4)
-// CHECK:STDOUT: !10 = distinct !DISubprogram(name: "Convert", linkageName: "_CConvert.B.Main:ImplicitAs.Core", scope: null, file: !3, line: 19, type: !5, spFlags: DISPFlagDefinition, unit: !2)
-// CHECK:STDOUT: !11 = !DILocation(line: 19, column: 45, scope: !10)
-// CHECK:STDOUT: !12 = !DILocation(line: 19, column: 39, scope: !10)
-// CHECK:STDOUT: !13 = !DILocation(line: 19, column: 32, scope: !10)
-// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "F", linkageName: "_CF.61ea2aba74ab3bf1:I.Main", scope: null, file: !3, line: 27, type: !5, spFlags: DISPFlagDefinition, unit: !2)
-// CHECK:STDOUT: !15 = !DILocation(line: 27, column: 34, scope: !14)
-// CHECK:STDOUT: !16 = !DILocation(line: 27, column: 28, scope: !14)
-// CHECK:STDOUT: !17 = !DILocation(line: 27, column: 21, scope: !14)
-// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "F", linkageName: "_CF:thunk.61ea2aba74ab3bf1:I.Main", scope: null, file: !3, line: 27, type: !5, spFlags: DISPFlagDefinition, unit: !2)
-// CHECK:STDOUT: !19 = !DILocation(line: 27, column: 3, scope: !18)
-// CHECK:STDOUT: !20 = !DILocation(line: 23, column: 8, scope: !18)
-// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "Test", linkageName: "_CTest.Main", scope: null, file: !3, line: 30, type: !5, spFlags: DISPFlagDefinition, unit: !2)
-// CHECK:STDOUT: !22 = !DILocation(line: 31, column: 10, scope: !21)
-// CHECK:STDOUT: !23 = !DILocation(line: 31, column: 3, scope: !21)
+// CHECK:STDOUT: !7 = !DILocation(line: 7, column: 37, scope: !4)
+// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "Call", linkageName: "_CCall.Main", scope: null, file: !3, line: 21, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !9 = !DILocation(line: 22, column: 10, scope: !8)
+// CHECK:STDOUT: !10 = !DILocation(line: 22, column: 3, scope: !8)
+// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "CallCallGeneric", linkageName: "_CCallCallGeneric.Main", scope: null, file: !3, line: 29, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !12 = !DILocation(line: 30, column: 10, scope: !11)
+// CHECK:STDOUT: !13 = !DILocation(line: 30, column: 3, scope: !11)
+// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "F", linkageName: "_CF:thunk.C.Main:I.Main.e43630e9a6c38c3f", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !15 = !DILocation(line: 18, column: 3, scope: !14)
+// CHECK:STDOUT: !16 = !DILocation(line: 12, column: 20, scope: !14)
+// CHECK:STDOUT: !17 = distinct !DISubprogram(name: "CallGeneric", linkageName: "_CCallGeneric.Main.9ef6968dffa77413", scope: null, file: !3, line: 25, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !18 = !DILocation(line: 26, column: 10, scope: !17)
+// CHECK:STDOUT: !19 = !DILocation(line: 26, column: 3, scope: !17)
+// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "F", linkageName: "_CF.C.Main:I.Main.e43630e9a6c38c3f", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !21 = !DILocation(line: 18, column: 33, scope: !20)

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません