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.
This commit is contained in:
Richard Smith
2025-06-10 20:54:58 +00:00
committed by GitHub
parent 81ca949ab8
commit e24ba02352
12 changed files with 636 additions and 1586 deletions
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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: }
@@ -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:
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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: 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: 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: 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)