Skip to content

Commit 3533668

Browse files
authored
Support for building thunks for C++ constructors. (#5977)
1 parent d37f1ae commit 3533668

File tree

4 files changed

+143
-58
lines changed

4 files changed

+143
-58
lines changed

toolchain/check/cpp_thunk.cpp

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "clang/AST/GlobalDecl.h"
88
#include "clang/AST/Mangle.h"
99
#include "clang/Sema/Lookup.h"
10+
#include "clang/Sema/Overload.h"
1011
#include "clang/Sema/Sema.h"
1112
#include "toolchain/check/call.h"
1213
#include "toolchain/check/context.h"
@@ -20,12 +21,22 @@
2021

2122
namespace Carbon::Check {
2223

24+
// Returns the GlobalDecl to use to represent the given function declaration.
25+
// TODO: Refactor with `Lower::CreateGlobalDecl`.
26+
static auto GetGlobalDecl(const clang::FunctionDecl* decl)
27+
-> clang::GlobalDecl {
28+
if (const auto* ctor = dyn_cast<clang::CXXConstructorDecl>(decl)) {
29+
return clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete);
30+
}
31+
return clang::GlobalDecl(decl);
32+
}
33+
2334
// Returns the C++ thunk mangled name given the callee function.
2435
static auto GenerateThunkMangledName(
2536
clang::MangleContext& mangle_context,
2637
const clang::FunctionDecl& callee_function_decl) -> std::string {
2738
RawStringOstream mangled_name_stream;
28-
mangle_context.mangleName(clang::GlobalDecl(&callee_function_decl),
39+
mangle_context.mangleName(GetGlobalDecl(&callee_function_decl),
2940
mangled_name_stream);
3041
mangled_name_stream << ".carbon_thunk";
3142

@@ -74,12 +85,6 @@ auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
7485
return false;
7586
}
7687

77-
if (isa<clang::CXXConstructorDecl>(
78-
context.sem_ir().clang_decls().Get(function.clang_decl_id).decl)) {
79-
// TODO: Support generating thunks for constructors.
80-
return false;
81-
}
82-
8388
// A thunk is required if any parameter or return type requires it. However,
8489
// we don't generate a thunk if any relevant type is erroneous.
8590
bool thunk_required = false;
@@ -131,14 +136,18 @@ namespace {
131136
// Information about the callee of a thunk.
132137
struct CalleeFunctionInfo {
133138
explicit CalleeFunctionInfo(clang::FunctionDecl* decl) : decl(decl) {
139+
auto& ast_context = decl->getASTContext();
134140
const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
135-
has_object_parameter = method_decl && !method_decl->isStatic() &&
136-
!isa<clang::CXXConstructorDecl>(method_decl);
141+
bool is_ctor = isa<clang::CXXConstructorDecl>(decl);
142+
has_object_parameter = method_decl && !method_decl->isStatic() && !is_ctor;
137143
if (has_object_parameter && method_decl->isImplicitObjectMemberFunction()) {
138144
implicit_this_type = method_decl->getThisType();
139145
}
146+
effective_return_type =
147+
is_ctor ? ast_context.getRecordType(method_decl->getParent())
148+
: decl->getReturnType();
140149
has_simple_return_type =
141-
IsSimpleAbiType(decl->getASTContext(), decl->getReturnType());
150+
IsSimpleAbiType(ast_context, effective_return_type);
142151
}
143152

144153
// Returns whether this callee has an implicit `this` parameter.
@@ -181,6 +190,11 @@ struct CalleeFunctionInfo {
181190
// type. Otherwise a null type.
182191
clang::QualType implicit_this_type;
183192

193+
// The return type that the callee has when viewed from Carbon. This is the
194+
// C++ return type, except that constructors return the class type in Carbon
195+
// and return void in Clang's AST.
196+
clang::QualType effective_return_type;
197+
184198
// Whether the callee has a simple return type, that we can return directly.
185199
// If not, we'll return through an out parameter instead.
186200
bool has_simple_return_type;
@@ -228,7 +242,7 @@ static auto BuildThunkParameterTypes(clang::ASTContext& ast_context,
228242
if (!callee_info.has_simple_return_type) {
229243
thunk_param_types.push_back(GetNonnullType(
230244
ast_context,
231-
ast_context.getPointerType(callee_info.decl->getReturnType())));
245+
ast_context.getPointerType(callee_info.effective_return_type)));
232246
}
233247

234248
CARBON_CHECK(thunk_param_types.size() == callee_info.num_thunk_params());
@@ -295,7 +309,7 @@ static auto CreateThunkFunctionDecl(
295309

296310
auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo();
297311
clang::QualType thunk_function_type = ast_context.getFunctionType(
298-
callee_info.has_simple_return_type ? callee_info.decl->getReturnType()
312+
callee_info.has_simple_return_type ? callee_info.effective_return_type
299313
: ast_context.VoidTy,
300314
thunk_param_types, ext_proto_info);
301315

@@ -418,7 +432,7 @@ static auto BuildThunkBody(clang::Sema& sema,
418432
/*HadMultipleCandidates=*/false, clang::DeclarationNameInfo(),
419433
sema.getASTContext().BoundMemberTy, clang::VK_PRValue,
420434
clang::OK_Ordinary);
421-
} else {
435+
} else if (!isa<clang::CXXConstructorDecl>(callee_info.decl)) {
422436
callee =
423437
sema.BuildDeclRefExpr(callee_info.decl, callee_info.decl->getType(),
424438
clang::VK_PRValue, clang_loc);
@@ -432,8 +446,27 @@ static auto BuildThunkBody(clang::Sema& sema,
432446
llvm::SmallVector<clang::Expr*> call_args =
433447
BuildCalleeArgs(sema, thunk_function_decl, callee_info);
434448

435-
clang::ExprResult call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc,
436-
call_args, clang_loc);
449+
clang::ExprResult call;
450+
if (auto info = clang::getConstructorInfo(callee_info.decl);
451+
info.Constructor) {
452+
// In C++, there are no direct calls to constructors, only initialization,
453+
// so we need to type-check and build the call ourselves.
454+
auto type = sema.Context.getRecordType(
455+
cast<clang::CXXRecordDecl>(callee_info.decl->getParent()));
456+
llvm::SmallVector<clang::Expr*> converted_args;
457+
converted_args.reserve(call_args.size());
458+
if (sema.CompleteConstructorCall(info.Constructor, type, call_args,
459+
clang_loc, converted_args)) {
460+
return clang::StmtError();
461+
}
462+
call = sema.BuildCXXConstructExpr(
463+
clang_loc, type, callee_info.decl, info.Constructor, converted_args,
464+
false, false, false, false, clang::CXXConstructionKind::Complete,
465+
clang_loc);
466+
} else {
467+
call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc, call_args,
468+
clang_loc);
469+
}
437470
if (!call.isUsable()) {
438471
return clang::StmtError();
439472
}
@@ -444,7 +477,7 @@ static auto BuildThunkBody(clang::Sema& sema,
444477

445478
auto* return_object_addr = BuildThunkParamRef(
446479
sema, thunk_function_decl, callee_info.GetThunkReturnParamIndex());
447-
auto return_type = callee_info.decl->getReturnType();
480+
auto return_type = callee_info.effective_return_type;
448481
auto* return_type_info =
449482
sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc);
450483
auto placement_new = sema.BuildCXXNew(

toolchain/check/import_cpp.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,8 @@ static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id,
13701370
if (!ret_type->isVoidType()) {
13711371
TypeExpr mapped_type = MapType(context, loc_id, ret_type);
13721372
if (!mapped_type.inst_id.has_value()) {
1373+
context.TODO(loc_id, llvm::formatv("Unsupported: return type: {0}",
1374+
ret_type.getAsString()));
13731375
return {.inst_id = SemIR::ErrorInst::TypeInstId,
13741376
.type_id = SemIR::ErrorInst::TypeId};
13751377
}
@@ -1404,12 +1406,6 @@ static auto GetReturnPattern(Context& context, SemIR::LocId loc_id,
14041406
// void.
14051407
return SemIR::InstId::None;
14061408
}
1407-
if (type_inst_id == SemIR::ErrorInst::TypeInstId) {
1408-
context.TODO(loc_id,
1409-
llvm::formatv("Unsupported: return type: {0}",
1410-
clang_decl->getReturnType().getAsString()));
1411-
return SemIR::ErrorInst::InstId;
1412-
}
14131409
auto pattern_type_id = GetPatternType(context, type_id);
14141410
SemIR::InstId return_slot_pattern_id = AddPatternInst(
14151411
// TODO: Fill in a location for the return type once available.

0 commit comments

Comments
 (0)