From 3533668186a68aa915d39d9ecf0b114b54be3875 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 26 Aug 2025 14:49:11 -0700 Subject: [PATCH] Support for building thunks for C++ constructors. (#5977) --- toolchain/check/cpp_thunk.cpp | 65 +++++++--- toolchain/check/import_cpp.cpp | 8 +- .../interop/cpp/class/constructor.carbon | 115 ++++++++++++------ .../testdata/interop/cpp/constructor.carbon | 13 +- 4 files changed, 143 insertions(+), 58 deletions(-) diff --git a/toolchain/check/cpp_thunk.cpp b/toolchain/check/cpp_thunk.cpp index 6f123d0b0c67..14f312fa5a3b 100644 --- a/toolchain/check/cpp_thunk.cpp +++ b/toolchain/check/cpp_thunk.cpp @@ -7,6 +7,7 @@ #include "clang/AST/GlobalDecl.h" #include "clang/AST/Mangle.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Overload.h" #include "clang/Sema/Sema.h" #include "toolchain/check/call.h" #include "toolchain/check/context.h" @@ -20,12 +21,22 @@ namespace Carbon::Check { +// Returns the GlobalDecl to use to represent the given function declaration. +// TODO: Refactor with `Lower::CreateGlobalDecl`. +static auto GetGlobalDecl(const clang::FunctionDecl* decl) + -> clang::GlobalDecl { + if (const auto* ctor = dyn_cast(decl)) { + return clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete); + } + return clang::GlobalDecl(decl); +} + // Returns the C++ thunk mangled name given the callee function. static auto GenerateThunkMangledName( clang::MangleContext& mangle_context, const clang::FunctionDecl& callee_function_decl) -> std::string { RawStringOstream mangled_name_stream; - mangle_context.mangleName(clang::GlobalDecl(&callee_function_decl), + mangle_context.mangleName(GetGlobalDecl(&callee_function_decl), mangled_name_stream); mangled_name_stream << ".carbon_thunk"; @@ -74,12 +85,6 @@ auto IsCppThunkRequired(Context& context, const SemIR::Function& function) return false; } - if (isa( - context.sem_ir().clang_decls().Get(function.clang_decl_id).decl)) { - // TODO: Support generating thunks for constructors. - return false; - } - // A thunk is required if any parameter or return type requires it. However, // we don't generate a thunk if any relevant type is erroneous. bool thunk_required = false; @@ -131,14 +136,18 @@ namespace { // Information about the callee of a thunk. struct CalleeFunctionInfo { explicit CalleeFunctionInfo(clang::FunctionDecl* decl) : decl(decl) { + auto& ast_context = decl->getASTContext(); const auto* method_decl = dyn_cast(decl); - has_object_parameter = method_decl && !method_decl->isStatic() && - !isa(method_decl); + bool is_ctor = isa(decl); + has_object_parameter = method_decl && !method_decl->isStatic() && !is_ctor; if (has_object_parameter && method_decl->isImplicitObjectMemberFunction()) { implicit_this_type = method_decl->getThisType(); } + effective_return_type = + is_ctor ? ast_context.getRecordType(method_decl->getParent()) + : decl->getReturnType(); has_simple_return_type = - IsSimpleAbiType(decl->getASTContext(), decl->getReturnType()); + IsSimpleAbiType(ast_context, effective_return_type); } // Returns whether this callee has an implicit `this` parameter. @@ -181,6 +190,11 @@ struct CalleeFunctionInfo { // type. Otherwise a null type. clang::QualType implicit_this_type; + // The return type that the callee has when viewed from Carbon. This is the + // C++ return type, except that constructors return the class type in Carbon + // and return void in Clang's AST. + clang::QualType effective_return_type; + // Whether the callee has a simple return type, that we can return directly. // If not, we'll return through an out parameter instead. bool has_simple_return_type; @@ -228,7 +242,7 @@ static auto BuildThunkParameterTypes(clang::ASTContext& ast_context, if (!callee_info.has_simple_return_type) { thunk_param_types.push_back(GetNonnullType( ast_context, - ast_context.getPointerType(callee_info.decl->getReturnType()))); + ast_context.getPointerType(callee_info.effective_return_type))); } CARBON_CHECK(thunk_param_types.size() == callee_info.num_thunk_params()); @@ -295,7 +309,7 @@ static auto CreateThunkFunctionDecl( auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo(); clang::QualType thunk_function_type = ast_context.getFunctionType( - callee_info.has_simple_return_type ? callee_info.decl->getReturnType() + callee_info.has_simple_return_type ? callee_info.effective_return_type : ast_context.VoidTy, thunk_param_types, ext_proto_info); @@ -418,7 +432,7 @@ static auto BuildThunkBody(clang::Sema& sema, /*HadMultipleCandidates=*/false, clang::DeclarationNameInfo(), sema.getASTContext().BoundMemberTy, clang::VK_PRValue, clang::OK_Ordinary); - } else { + } else if (!isa(callee_info.decl)) { callee = sema.BuildDeclRefExpr(callee_info.decl, callee_info.decl->getType(), clang::VK_PRValue, clang_loc); @@ -432,8 +446,27 @@ static auto BuildThunkBody(clang::Sema& sema, llvm::SmallVector call_args = BuildCalleeArgs(sema, thunk_function_decl, callee_info); - clang::ExprResult call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc, - call_args, clang_loc); + clang::ExprResult call; + if (auto info = clang::getConstructorInfo(callee_info.decl); + info.Constructor) { + // In C++, there are no direct calls to constructors, only initialization, + // so we need to type-check and build the call ourselves. + auto type = sema.Context.getRecordType( + cast(callee_info.decl->getParent())); + llvm::SmallVector converted_args; + converted_args.reserve(call_args.size()); + if (sema.CompleteConstructorCall(info.Constructor, type, call_args, + clang_loc, converted_args)) { + return clang::StmtError(); + } + call = sema.BuildCXXConstructExpr( + clang_loc, type, callee_info.decl, info.Constructor, converted_args, + false, false, false, false, clang::CXXConstructionKind::Complete, + clang_loc); + } else { + call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc, call_args, + clang_loc); + } if (!call.isUsable()) { return clang::StmtError(); } @@ -444,7 +477,7 @@ static auto BuildThunkBody(clang::Sema& sema, auto* return_object_addr = BuildThunkParamRef( sema, thunk_function_decl, callee_info.GetThunkReturnParamIndex()); - auto return_type = callee_info.decl->getReturnType(); + auto return_type = callee_info.effective_return_type; auto* return_type_info = sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc); auto placement_new = sema.BuildCXXNew( diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index 281491da31d0..f288f49f5fc2 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -1370,6 +1370,8 @@ static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id, if (!ret_type->isVoidType()) { TypeExpr mapped_type = MapType(context, loc_id, ret_type); if (!mapped_type.inst_id.has_value()) { + context.TODO(loc_id, llvm::formatv("Unsupported: return type: {0}", + ret_type.getAsString())); return {.inst_id = SemIR::ErrorInst::TypeInstId, .type_id = SemIR::ErrorInst::TypeId}; } @@ -1404,12 +1406,6 @@ static auto GetReturnPattern(Context& context, SemIR::LocId loc_id, // void. return SemIR::InstId::None; } - if (type_inst_id == SemIR::ErrorInst::TypeInstId) { - context.TODO(loc_id, - llvm::formatv("Unsupported: return type: {0}", - clang_decl->getReturnType().getAsString())); - return SemIR::ErrorInst::InstId; - } auto pattern_type_id = GetPatternType(context, type_id); SemIR::InstId return_slot_pattern_id = AddPatternInst( // TODO: Fill in a location for the return type once available. diff --git a/toolchain/check/testdata/interop/cpp/class/constructor.carbon b/toolchain/check/testdata/interop/cpp/class/constructor.carbon index 17255b56505e..e3fdb48fc5c2 100644 --- a/toolchain/check/testdata/interop/cpp/class/constructor.carbon +++ b/toolchain/check/testdata/interop/cpp/class/constructor.carbon @@ -221,9 +221,11 @@ fn F() { // CHECK:STDOUT: %pattern_type.217: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] // CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete] +// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete] +// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [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: %ptr.d9e: type = ptr_type %C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -237,6 +239,11 @@ fn F() { // CHECK:STDOUT: } { // CHECK:STDOUT: // CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { @@ -248,19 +255,21 @@ fn F() { // CHECK:STDOUT: %C.ref.loc8_21: type = name_ref C, imports.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %C.ref.loc8_23: %C.C.type = name_ref C, imports.%C.C.decl [concrete = constants.%C.C] // CHECK:STDOUT: %.loc8_26.1: ref %C = temporary_storage -// CHECK:STDOUT: %C.C.call: init %C = call %C.ref.loc8_23() to %.loc8_26.1 +// CHECK:STDOUT: %addr.loc8_26.1: %ptr.d9e = addr_of %.loc8_26.1 +// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_26.1) +// CHECK:STDOUT: %.loc8_26.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_26.1 // CHECK:STDOUT: %.loc8_13: type = splice_block %C.ref.loc8_13 [concrete = constants.%C] { // CHECK:STDOUT: %Cpp.ref.loc8_10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %C.ref.loc8_13: type = name_ref C, imports.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } -// CHECK:STDOUT: %.loc8_26.2: ref %C = temporary %.loc8_26.1, %C.C.call -// CHECK:STDOUT: %.loc8_26.3: %C = bind_value %.loc8_26.2 -// CHECK:STDOUT: %c: %C = bind_name c, %.loc8_26.3 +// CHECK:STDOUT: %.loc8_26.3: ref %C = temporary %.loc8_26.1, %.loc8_26.2 +// CHECK:STDOUT: %.loc8_26.4: %C = bind_value %.loc8_26.3 +// CHECK:STDOUT: %c: %C = bind_name c, %.loc8_26.4 // CHECK:STDOUT: %T.as.Destroy.impl.Op.bound: = bound_method %.loc8_26.1, constants.%T.as.Destroy.impl.Op.21b // CHECK:STDOUT: // CHECK:STDOUT: %bound_method: = bound_method %.loc8_26.1, %T.as.Destroy.impl.Op.specific_fn -// CHECK:STDOUT: %addr: %ptr.d9e = addr_of %.loc8_26.1 -// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr) +// CHECK:STDOUT: %addr.loc8_26.2: %ptr.d9e = addr_of %.loc8_26.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_26.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -274,6 +283,9 @@ fn F() { // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] // CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete] +// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete] +// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete] // CHECK:STDOUT: %int_123.fff: Core.IntLiteral = int_value 123 [concrete] // CHECK:STDOUT: %int_456.010: Core.IntLiteral = int_value 456 [concrete] // CHECK:STDOUT: %ImplicitAs.type.205: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] @@ -295,7 +307,6 @@ fn F() { // CHECK:STDOUT: %int_456.d17: %i32 = int_value 456 [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: %ptr.d9e: type = ptr_type %C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -309,6 +320,11 @@ fn F() { // CHECK:STDOUT: } { // CHECK:STDOUT: // CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: %Core.import_ref.a5b: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.0f9) = import_ref Core//prelude/parts/int, loc16_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f06)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.a2f = impl_witness_table (%Core.import_ref.a5b), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } @@ -338,19 +354,21 @@ fn F() { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_31: init %i32 = call %bound_method.loc8_31.2(%int_456) [concrete = constants.%int_456.d17] // CHECK:STDOUT: %.loc8_31.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_31 [concrete = constants.%int_456.d17] // CHECK:STDOUT: %.loc8_31.2: %i32 = converted %int_456, %.loc8_31.1 [concrete = constants.%int_456.d17] -// CHECK:STDOUT: %C.C.call: init %C = call %C.ref.loc8_23(%.loc8_26.2, %.loc8_31.2) to %.loc8_34.1 +// CHECK:STDOUT: %addr.loc8_34.1: %ptr.d9e = addr_of %.loc8_34.1 +// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc8_26.2, %.loc8_31.2, %addr.loc8_34.1) +// CHECK:STDOUT: %.loc8_34.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_34.1 // CHECK:STDOUT: %.loc8_13: type = splice_block %C.ref.loc8_13 [concrete = constants.%C] { // CHECK:STDOUT: %Cpp.ref.loc8_10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %C.ref.loc8_13: type = name_ref C, imports.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } -// CHECK:STDOUT: %.loc8_34.2: ref %C = temporary %.loc8_34.1, %C.C.call -// CHECK:STDOUT: %.loc8_34.3: %C = bind_value %.loc8_34.2 -// CHECK:STDOUT: %c: %C = bind_name c, %.loc8_34.3 +// CHECK:STDOUT: %.loc8_34.3: ref %C = temporary %.loc8_34.1, %.loc8_34.2 +// CHECK:STDOUT: %.loc8_34.4: %C = bind_value %.loc8_34.3 +// CHECK:STDOUT: %c: %C = bind_name c, %.loc8_34.4 // CHECK:STDOUT: %T.as.Destroy.impl.Op.bound: = bound_method %.loc8_34.1, constants.%T.as.Destroy.impl.Op.21b // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc8_34: = bound_method %.loc8_34.1, %T.as.Destroy.impl.Op.specific_fn -// CHECK:STDOUT: %addr: %ptr.d9e = addr_of %.loc8_34.1 -// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_34(%addr) +// CHECK:STDOUT: %addr.loc8_34.2: %ptr.d9e = addr_of %.loc8_34.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_34(%addr.loc8_34.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -410,6 +428,9 @@ fn F() { // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] // CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete] +// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete] +// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete] // CHECK:STDOUT: %int_8.b85: Core.IntLiteral = int_value 8 [concrete] // CHECK:STDOUT: %int_9.988: Core.IntLiteral = int_value 9 [concrete] // CHECK:STDOUT: %ImplicitAs.type.205: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] @@ -431,7 +452,6 @@ fn F() { // CHECK:STDOUT: %int_9.f88: %i32 = int_value 9 [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: %ptr.d9e: type = ptr_type %C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -445,6 +465,11 @@ fn F() { // CHECK:STDOUT: } { // CHECK:STDOUT: // CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: %Core.import_ref.a5b: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.0f9) = import_ref Core//prelude/parts/int, loc16_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f06)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.a2f = impl_witness_table (%Core.import_ref.a5b), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } @@ -474,14 +499,16 @@ fn F() { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_30: init %i32 = call %bound_method.loc8_30.2(%int_9) [concrete = constants.%int_9.f88] // CHECK:STDOUT: %.loc8_30.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_30 [concrete = constants.%int_9.f88] // CHECK:STDOUT: %.loc8_30.2: %i32 = converted %int_9, %.loc8_30.1 [concrete = constants.%int_9.f88] -// CHECK:STDOUT: %C.C.call: init %C = call %C.ref.loc8_24(%.loc8_27.2, %.loc8_30.2) to %.loc8_31.1 +// CHECK:STDOUT: %addr.loc8_31.1: %ptr.d9e = addr_of %.loc8_31.1 +// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc8_27.2, %.loc8_30.2, %addr.loc8_31.1) +// CHECK:STDOUT: %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_31.1 // CHECK:STDOUT: %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] { // CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } -// CHECK:STDOUT: %.loc8_31.2: ref %C = temporary %.loc8_31.1, %C.C.call -// CHECK:STDOUT: %.loc8_31.3: %C = bind_value %.loc8_31.2 -// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_31.3 +// CHECK:STDOUT: %.loc8_31.3: ref %C = temporary %.loc8_31.1, %.loc8_31.2 +// CHECK:STDOUT: %.loc8_31.4: %C = bind_value %.loc8_31.3 +// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_31.4 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete] // CHECK:STDOUT: } @@ -497,8 +524,8 @@ fn F() { // CHECK:STDOUT: %T.as.Destroy.impl.Op.bound: = bound_method %.loc8_31.1, constants.%T.as.Destroy.impl.Op.21b // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc8_31: = bound_method %.loc8_31.1, %T.as.Destroy.impl.Op.specific_fn -// CHECK:STDOUT: %addr: %ptr.d9e = addr_of %.loc8_31.1 -// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_31(%addr) +// CHECK:STDOUT: %addr.loc8_31.2: %ptr.d9e = addr_of %.loc8_31.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -512,6 +539,9 @@ fn F() { // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] // CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete] +// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete] +// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete] // CHECK:STDOUT: %int_8.b85: Core.IntLiteral = int_value 8 [concrete] // CHECK:STDOUT: %ImplicitAs.type.205: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete] @@ -541,7 +571,6 @@ fn F() { // CHECK:STDOUT: %bound_method.3a8: = bound_method %int_8.b85, %Core.IntLiteral.as.As.impl.Convert.specific_fn [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: %ptr.d9e: type = ptr_type %C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -555,6 +584,11 @@ fn F() { // CHECK:STDOUT: } { // CHECK:STDOUT: // CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: %Core.import_ref.a5b: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.0f9) = import_ref Core//prelude/parts/int, loc16_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f06)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.a2f = impl_witness_table (%Core.import_ref.a5b), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.78a: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.062) = import_ref Core//prelude/parts/int, loc25_39, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.527)] @@ -578,14 +612,16 @@ fn F() { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc8_27.2(%int_8.loc8) [concrete = constants.%int_8.98c] // CHECK:STDOUT: %.loc8_27.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_8.98c] // CHECK:STDOUT: %.loc8_27.2: %i32 = converted %int_8.loc8, %.loc8_27.1 [concrete = constants.%int_8.98c] -// CHECK:STDOUT: %C.C.call: init %C = call %C.ref.loc8_24(%.loc8_27.2) to %.loc8_28.1 +// CHECK:STDOUT: %addr.loc8_28.1: %ptr.d9e = addr_of %.loc8_28.1 +// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc8_27.2, %addr.loc8_28.1) +// CHECK:STDOUT: %.loc8_28.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_28.1 // CHECK:STDOUT: %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] { // CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } -// CHECK:STDOUT: %.loc8_28.2: ref %C = temporary %.loc8_28.1, %C.C.call -// CHECK:STDOUT: %.loc8_28.3: %C = bind_value %.loc8_28.2 -// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_28.3 +// CHECK:STDOUT: %.loc8_28.3: ref %C = temporary %.loc8_28.1, %.loc8_28.2 +// CHECK:STDOUT: %.loc8_28.4: %C = bind_value %.loc8_28.3 +// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_28.4 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete] // CHECK:STDOUT: } @@ -608,8 +644,8 @@ fn F() { // CHECK:STDOUT: %T.as.Destroy.impl.Op.bound: = bound_method %.loc8_28.1, constants.%T.as.Destroy.impl.Op.21b // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc8_28: = bound_method %.loc8_28.1, %T.as.Destroy.impl.Op.specific_fn -// CHECK:STDOUT: %addr: %ptr.d9e = addr_of %.loc8_28.1 -// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_28(%addr) +// CHECK:STDOUT: %addr.loc8_28.2: %ptr.d9e = addr_of %.loc8_28.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_28(%addr.loc8_28.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -623,6 +659,9 @@ fn F() { // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.C.type: type = fn_type @C.C [concrete] // CHECK:STDOUT: %C.C: %C.C.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete] +// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete] +// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete] // CHECK:STDOUT: %int_8.b85: Core.IntLiteral = int_value 8 [concrete] // CHECK:STDOUT: %int_9.988: Core.IntLiteral = int_value 9 [concrete] // CHECK:STDOUT: %ImplicitAs.type.205: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] @@ -656,7 +695,6 @@ fn F() { // CHECK:STDOUT: %bound_method.3a8: = bound_method %int_8.b85, %Core.IntLiteral.as.As.impl.Convert.specific_fn [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: %ptr.d9e: type = ptr_type %C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -670,6 +708,11 @@ fn F() { // CHECK:STDOUT: } { // CHECK:STDOUT: // CHECK:STDOUT: } +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: %Core.import_ref.a5b: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.0f9) = import_ref Core//prelude/parts/int, loc16_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f06)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.a2f = impl_witness_table (%Core.import_ref.a5b), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.78a: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.062) = import_ref Core//prelude/parts/int, loc25_39, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.527)] @@ -701,14 +744,16 @@ fn F() { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_30: init %i32 = call %bound_method.loc8_30.2(%int_9) [concrete = constants.%int_9.f88] // CHECK:STDOUT: %.loc8_30.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_30 [concrete = constants.%int_9.f88] // CHECK:STDOUT: %.loc8_30.2: %i32 = converted %int_9, %.loc8_30.1 [concrete = constants.%int_9.f88] -// CHECK:STDOUT: %C.C.call: init %C = call %C.ref.loc8_24(%.loc8_27.2, %.loc8_30.2) to %.loc8_31.1 +// CHECK:STDOUT: %addr.loc8_31.1: %ptr.d9e = addr_of %.loc8_31.1 +// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc8_27.2, %.loc8_30.2, %addr.loc8_31.1) +// CHECK:STDOUT: %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_31.1 // CHECK:STDOUT: %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] { // CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } -// CHECK:STDOUT: %.loc8_31.2: ref %C = temporary %.loc8_31.1, %C.C.call -// CHECK:STDOUT: %.loc8_31.3: %C = bind_value %.loc8_31.2 -// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_31.3 +// CHECK:STDOUT: %.loc8_31.3: ref %C = temporary %.loc8_31.1, %.loc8_31.2 +// CHECK:STDOUT: %.loc8_31.4: %C = bind_value %.loc8_31.3 +// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_31.4 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete] // CHECK:STDOUT: } @@ -743,8 +788,8 @@ fn F() { // CHECK:STDOUT: %T.as.Destroy.impl.Op.bound: = bound_method %.loc8_31.1, constants.%T.as.Destroy.impl.Op.21b // CHECK:STDOUT: // CHECK:STDOUT: %bound_method.loc8_31: = bound_method %.loc8_31.1, %T.as.Destroy.impl.Op.specific_fn -// CHECK:STDOUT: %addr: %ptr.d9e = addr_of %.loc8_31.1 -// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_31(%addr) +// CHECK:STDOUT: %addr.loc8_31.2: %ptr.d9e = addr_of %.loc8_31.1 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/interop/cpp/constructor.carbon b/toolchain/lower/testdata/interop/cpp/constructor.carbon index aff0e270a44e..a63748f14e1e 100644 --- a/toolchain/lower/testdata/interop/cpp/constructor.carbon +++ b/toolchain/lower/testdata/interop/cpp/constructor.carbon @@ -50,7 +50,7 @@ fn F() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc7_26.1.temp = alloca [8 x i8], align 1, !dbg !10 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc7_26.1.temp), !dbg !10 -// CHECK:STDOUT: call void @_ZN1CC1Ev(ptr %.loc7_26.1.temp), !dbg !10 +// CHECK:STDOUT: call void @_ZN1CC1Ev.carbon_thunk(ptr %.loc7_26.1.temp), !dbg !10 // CHECK:STDOUT: ret void, !dbg !11 // CHECK:STDOUT: } // CHECK:STDOUT: @@ -80,9 +80,20 @@ fn F() { // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress +// CHECK:STDOUT: define dso_local void @_ZN1CC1Ev.carbon_thunk(ptr %return) #3 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %return.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8 +// CHECK:STDOUT: %0 = load ptr, ptr %return.addr, align 8 +// CHECK:STDOUT: call void @_ZN1CC1Ev(ptr nonnull align 4 dereferenceable(8) %0) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } // CHECK:STDOUT: attributes #1 = { mustprogress noinline optnone "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 = { mustprogress noinline nounwind optnone "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 #3 = { 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: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} // CHECK:STDOUT: !llvm.dbg.cu = !{!5}