mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 15:01:03 +01:00
Generalize CalleeFunctionInfo and reuse it in C++ import (#7787)
This is a step toward supporting C++ function pointer types, which need to be imported and thunked in much the same way as C++ functions, but have a different underlying representation. `CalleeFunctionInfo` gives us a way to abstract away the representation differences, so expressing import and thunking in terms of `CalleeFunctionInfo` lets us reuse that code for function pointers. Actual support for function pointers will come in a follow-up PR, but the API design choices I've made here are driven by that use case.
This commit is contained in:
+116
-115
@@ -477,6 +477,9 @@ static auto GetParentDecl(clang::Decl* clang_decl) -> clang::Decl* {
|
||||
// was already imported.
|
||||
static auto GetParentNameScopeId(Context& context, clang::Decl* clang_decl)
|
||||
-> SemIR::NameScopeId {
|
||||
if (clang_decl == nullptr) {
|
||||
return SemIR::NameScopeId::Package;
|
||||
}
|
||||
auto* parent_decl = GetParentDecl(clang_decl);
|
||||
|
||||
if (auto* tag_decl = dyn_cast<clang::TagDecl>(parent_decl)) {
|
||||
@@ -1524,10 +1527,9 @@ static auto MapParameterType(
|
||||
// passing mode, and parameter name. Returns the IDs of the pattern inst and
|
||||
// the inst representing the parameter type.
|
||||
static auto MakeParamPattern(
|
||||
Context& context, SemIR::LocId loc_id,
|
||||
SemIR::ImportIRInstId import_ir_inst_id, clang::QualType type,
|
||||
SemIR::ClangDeclSignature::PassingMode passing_mode, SemIR::NameId name_id)
|
||||
-> std::pair<SemIR::InstId, SemIR::TypeInstId> {
|
||||
Context& context, SemIR::LocId loc_id, SemIR::LocId param_loc_id,
|
||||
clang::QualType type, SemIR::ClangDeclSignature::PassingMode passing_mode,
|
||||
SemIR::NameId name_id) -> std::pair<SemIR::InstId, SemIR::TypeInstId> {
|
||||
// Mark the start of a region of insts, needed for the type expression
|
||||
// created later with the call of `ConsumeExprRegionForPattern()`.
|
||||
BeginExprRegionForPattern(context);
|
||||
@@ -1545,8 +1547,8 @@ static auto MakeParamPattern(
|
||||
return {SemIR::ErrorInst::InstId, SemIR::ErrorInst::TypeInstId};
|
||||
}
|
||||
|
||||
return {AddParamPattern(context, import_ir_inst_id, name_id,
|
||||
type_expr_region_id, type_id, param_info.kind),
|
||||
return {AddParamPattern(context, param_loc_id, name_id, type_expr_region_id,
|
||||
type_id, param_info.kind),
|
||||
type_inst_id};
|
||||
}
|
||||
|
||||
@@ -1558,30 +1560,22 @@ static auto MakeParamPattern(
|
||||
// TODO: Consider refactoring to extract and reuse more logic from
|
||||
// `HandleAnyBindingPattern()`.
|
||||
static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::ImportIRInstId import_ir_inst_id,
|
||||
const clang::FunctionDecl& clang_decl,
|
||||
SemIR::ClangDeclSignatureId signature_id)
|
||||
const CalleeFunctionInfo& function_info)
|
||||
-> SemIR::InstBlockId {
|
||||
// The `self` parameter of a method is the first entry in the explicit
|
||||
// parameter list. Build it (if any) first, then the remaining explicit
|
||||
// parameters.
|
||||
bool has_self_param = IsObjectMemberFunction(clang_decl);
|
||||
const auto& signature = context.clang_decl_signatures().Get(signature_id);
|
||||
llvm::SmallVector<SemIR::InstId> param_ids;
|
||||
llvm::SmallVector<SemIR::InstId> param_type_ids;
|
||||
param_ids.reserve(signature.num_params + has_self_param);
|
||||
param_type_ids.reserve(signature.num_params);
|
||||
if (has_self_param) {
|
||||
const auto* method_decl = cast<clang::CXXMethodDecl>(&clang_decl);
|
||||
clang::QualType param_type =
|
||||
method_decl->getFunctionObjectParameterReferenceType();
|
||||
const auto& signature = context.clang_decl_signatures().Get(signature_id);
|
||||
|
||||
param_ids.reserve(function_info.num_carbon_params());
|
||||
param_type_ids.reserve(function_info.num_carbon_params());
|
||||
if (function_info.carbon_has_self_param()) {
|
||||
// TODO: Use a location associated with the object parameter instead of the
|
||||
// location of the function as a whole.
|
||||
auto [self_param_pattern_id, _] =
|
||||
MakeParamPattern(context, loc_id, import_ir_inst_id, param_type,
|
||||
signature.self_passing_mode, SemIR::NameId::SelfValue);
|
||||
auto [self_param_pattern_id, _] = MakeParamPattern(
|
||||
context, loc_id, function_info.sem_ir_loc,
|
||||
function_info.self_param_type,
|
||||
function_info.signature->self_passing_mode, SemIR::NameId::SelfValue);
|
||||
if (self_param_pattern_id == SemIR::ErrorInst::InstId) {
|
||||
return SemIR::InstBlockId::None;
|
||||
}
|
||||
@@ -1589,16 +1583,16 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
|
||||
// We don't push to param_type_ids because the self parameter can't be part
|
||||
// of a parameter tuple, which is the only case where we use param_type_ids.
|
||||
}
|
||||
CARBON_CHECK(static_cast<int>(clang_decl.getNumNonObjectParams()) >=
|
||||
signature.num_params,
|
||||
"Function has fewer parameters than requested: {0} < {1}",
|
||||
clang_decl.getNumNonObjectParams(), signature.num_params);
|
||||
const auto* function_type =
|
||||
clang_decl.getType()->castAs<clang::FunctionProtoType>();
|
||||
for (auto i : llvm::seq(signature.num_params)) {
|
||||
const auto* param = clang_decl.getNonObjectParameter(i);
|
||||
clang::QualType orig_param_type = function_type->getParamType(
|
||||
clang_decl.hasCXXExplicitFunctionObjectParameter() + i);
|
||||
// The signature parameters always exclude the self parameter, like the callee
|
||||
// args, so we can use the callee args offset to index into the signature
|
||||
// params.
|
||||
for (auto signature_index :
|
||||
llvm::seq(function_info.num_callee_params -
|
||||
function_info.callee_arg_to_callee_param_offset())) {
|
||||
int callee_index =
|
||||
signature_index + function_info.callee_arg_to_callee_param_offset();
|
||||
clang::QualType orig_param_type =
|
||||
function_info.function_type->getParamType(callee_index);
|
||||
|
||||
// The parameter type is decayed but hasn't necessarily had its qualifiers
|
||||
// removed.
|
||||
@@ -1606,21 +1600,19 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
|
||||
clang::QualType param_type =
|
||||
ClangGetUnqualifiedTypePreserveNonNull(context, orig_param_type);
|
||||
|
||||
llvm::StringRef param_name = param->getName();
|
||||
SemIR::NameId name_id =
|
||||
param_name.empty()
|
||||
// Translate an unnamed parameter to an underscore to
|
||||
// match Carbon's naming of unnamed/unused function params.
|
||||
? SemIR::NameId::Underscore
|
||||
: AddIdentifierName(context, param_name);
|
||||
SemIR::NameId name_id = SemIR::NameId::Underscore;
|
||||
if (auto* identifier_info =
|
||||
function_info.GetCalleeParamIdentifier(callee_index)) {
|
||||
name_id = AddIdentifierName(context, identifier_info->getName());
|
||||
}
|
||||
|
||||
SemIR::ImportIRInstId param_loc_id =
|
||||
AddImportIRInst(context.sem_ir(), param->getLocation());
|
||||
SemIR::ImportIRInstId param_loc_id = AddImportIRInst(
|
||||
context.sem_ir(), function_info.GetCalleeParamLocation(callee_index));
|
||||
|
||||
// TODO: Add template support.
|
||||
auto [pattern_id, type_inst_id] =
|
||||
MakeParamPattern(context, loc_id, param_loc_id, param_type,
|
||||
signature.GetPassingMode(i), name_id);
|
||||
auto [pattern_id, type_inst_id] = MakeParamPattern(
|
||||
context, loc_id, param_loc_id, param_type,
|
||||
function_info.signature->GetPassingMode(signature_index), name_id);
|
||||
if (pattern_id == SemIR::ErrorInst::InstId) {
|
||||
return SemIR::InstBlockId::None;
|
||||
}
|
||||
@@ -1628,7 +1620,7 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
|
||||
param_type_ids.push_back(type_inst_id);
|
||||
}
|
||||
|
||||
switch (signature.kind) {
|
||||
switch (function_info.signature->kind) {
|
||||
case SemIR::ClangDeclSignature::Normal: {
|
||||
// Use the converted parameter list as-is.
|
||||
break;
|
||||
@@ -1637,13 +1629,13 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
|
||||
case SemIR::ClangDeclSignature::TuplePattern: {
|
||||
// Replace the parameters with a single tuple pattern containing the
|
||||
// converted parameter list.
|
||||
CARBON_CHECK(!has_self_param);
|
||||
CARBON_CHECK(!function_info.carbon_has_self_param());
|
||||
auto param_block_id = context.inst_blocks().Add(param_ids);
|
||||
auto tuple_pattern_type_id =
|
||||
GetPatternType(context, GetTupleType(context, param_type_ids));
|
||||
SemIR::InstId pattern_id = AddInst(
|
||||
context, SemIR::LocIdAndInst::RuntimeVerified(
|
||||
context.sem_ir(), import_ir_inst_id,
|
||||
context.sem_ir(), function_info.sem_ir_loc,
|
||||
SemIR::TuplePattern{.type_id = tuple_pattern_type_id,
|
||||
.elements_id = param_block_id}));
|
||||
param_ids = {pattern_id};
|
||||
@@ -1659,7 +1651,7 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
|
||||
// are treated as returning a class instance.
|
||||
// TODO: Support more return types.
|
||||
static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id,
|
||||
clang::FunctionDecl* clang_decl)
|
||||
const CalleeFunctionInfo& function_info)
|
||||
-> Context::FormExpr {
|
||||
auto make_init_form = [&](SemIR::TypeInstId type_component_inst_id) {
|
||||
SemIR::InitForm inst = {.type_id = SemIR::FormType::TypeId,
|
||||
@@ -1671,7 +1663,7 @@ static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id,
|
||||
.type_component_inst_id = type_component_inst_id};
|
||||
return context.constant_values().GetInstId(TryEvalInst(context, inst));
|
||||
};
|
||||
clang::QualType orig_ret_type = clang_decl->getReturnType();
|
||||
clang::QualType orig_ret_type = function_info.function_type->getReturnType();
|
||||
if (!orig_ret_type->isVoidType()) {
|
||||
bool is_reference = orig_ret_type->isReferenceType();
|
||||
if (is_reference) {
|
||||
@@ -1695,7 +1687,8 @@ static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id,
|
||||
return result;
|
||||
}
|
||||
|
||||
auto* ctor = dyn_cast<clang::CXXConstructorDecl>(clang_decl);
|
||||
auto* ctor =
|
||||
dyn_cast_if_present<clang::CXXConstructorDecl>(function_info.decl);
|
||||
if (!ctor) {
|
||||
// void.
|
||||
return {.form_inst_id = SemIR::InstId::None,
|
||||
@@ -1725,9 +1718,10 @@ struct ReturnInfo {
|
||||
// and the returned return_type_inst_id will be `SemIR::ErrorInst::InstId`.
|
||||
// Constructors are treated as returning a class instance.
|
||||
static auto GetReturnInfo(Context& context, SemIR::LocId loc_id,
|
||||
clang::FunctionDecl* clang_decl) -> ReturnInfo {
|
||||
const CalleeFunctionInfo& function_info)
|
||||
-> ReturnInfo {
|
||||
auto [form_inst_id, type_inst_id, type_id] =
|
||||
GetReturnTypeExpr(context, loc_id, clang_decl);
|
||||
GetReturnTypeExpr(context, loc_id, function_info);
|
||||
if (!form_inst_id.has_value()) {
|
||||
// void.
|
||||
return {.return_type_inst_id = SemIR::TypeInstId::None,
|
||||
@@ -1740,14 +1734,16 @@ static auto GetReturnInfo(Context& context, SemIR::LocId loc_id,
|
||||
.return_pattern_id = SemIR::InstId::None};
|
||||
}
|
||||
auto pattern_type_id = GetPatternType(context, type_id);
|
||||
clang::SourceLocation return_type_loc =
|
||||
clang_decl->getReturnTypeSourceRange().getBegin();
|
||||
if (return_type_loc.isInvalid()) {
|
||||
// TODO: While `getReturnTypeSourceRange()` should work, it seems broken for
|
||||
// trailing return type. See
|
||||
// https://github.com/llvm/llvm-project/issues/162649. Until this is fixed,
|
||||
// we fallback to `getTypeSpecStartLoc()`.
|
||||
return_type_loc = clang_decl->getTypeSpecStartLoc();
|
||||
clang::SourceLocation return_type_loc;
|
||||
if (function_info.decl != nullptr) {
|
||||
return_type_loc = function_info.decl->getReturnTypeSourceRange().getBegin();
|
||||
if (return_type_loc.isInvalid()) {
|
||||
// TODO: While `getReturnTypeSourceRange()` should work, it seems broken
|
||||
// for trailing return type. See
|
||||
// https://github.com/llvm/llvm-project/issues/162649. Until this is
|
||||
// fixed, we fallback to `getTypeSpecStartLoc()`.
|
||||
return_type_loc = function_info.decl->getTypeSpecStartLoc();
|
||||
}
|
||||
}
|
||||
SemIR::ImportIRInstId return_type_import_ir_inst_id =
|
||||
AddImportIRInst(context.sem_ir(), return_type_loc);
|
||||
@@ -1798,19 +1794,18 @@ struct FunctionSignatureInsts {
|
||||
// signature to the Carbon function signature.
|
||||
static auto CreateFunctionSignatureInsts(
|
||||
Context& context, SemIR::LocId loc_id,
|
||||
SemIR::ImportIRInstId import_ir_inst_id, clang::FunctionDecl* clang_decl,
|
||||
SemIR::ClangDeclSignatureId signature_id)
|
||||
const CalleeFunctionInfo& function_info)
|
||||
-> std::optional<FunctionSignatureInsts> {
|
||||
context.full_pattern_stack().StartExplicitParamList();
|
||||
auto param_patterns_id = MakeParamPatternsBlockId(
|
||||
context, loc_id, import_ir_inst_id, *clang_decl, signature_id);
|
||||
auto param_patterns_id =
|
||||
MakeParamPatternsBlockId(context, loc_id, function_info);
|
||||
if (!param_patterns_id.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
context.full_pattern_stack().EndExplicitParamList();
|
||||
|
||||
auto [return_type_inst_id, return_form_inst_id, return_pattern_id] =
|
||||
GetReturnInfo(context, loc_id, clang_decl);
|
||||
GetReturnInfo(context, loc_id, function_info);
|
||||
if (return_type_inst_id == SemIR::ErrorInst::TypeInstId) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1832,12 +1827,14 @@ static auto CreateFunctionSignatureInsts(
|
||||
}
|
||||
|
||||
// Returns the Carbon function name for the given function.
|
||||
static auto GetFunctionName(Context& context, clang::FunctionDecl* clang_decl)
|
||||
static auto GetFunctionName(Context& context,
|
||||
const CalleeFunctionInfo& function_info)
|
||||
-> SemIR::NameId {
|
||||
switch (clang_decl->getDeclName().getNameKind()) {
|
||||
clang::DeclarationName decl_name = function_info.decl_name;
|
||||
switch (decl_name.getNameKind()) {
|
||||
case clang::DeclarationName::CXXConstructorName: {
|
||||
auto key = SemIR::ClangDeclKey(
|
||||
cast<clang::CXXConstructorDecl>(clang_decl)->getParent());
|
||||
cast<clang::CXXConstructorDecl>(function_info.decl)->getParent());
|
||||
return context.classes()
|
||||
.Get(context.insts()
|
||||
.GetAs<SemIR::ClassDecl>(LookupClangDeclInstId(context, key))
|
||||
@@ -1855,29 +1852,28 @@ static auto GetFunctionName(Context& context, clang::FunctionDecl* clang_decl)
|
||||
}
|
||||
|
||||
default: {
|
||||
return AddIdentifierName(context, clang_decl->getName());
|
||||
CARBON_CHECK(decl_name.isIdentifier());
|
||||
return AddIdentifierName(context,
|
||||
decl_name.getAsIdentifierInfo()->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a `FunctionDecl` and a `Function` without C++ thunk information.
|
||||
// Returns std::nullopt on failure.
|
||||
// Creates a `FunctionDecl` and a `Function` that represents the given C++
|
||||
// callee in Carbon, but does not create an ABI thunk for it, or add it to
|
||||
// clang_decls. Returns the ID of the `FunctionDecl`, or `std::nullopt` on
|
||||
// failure.
|
||||
//
|
||||
// The given Clang declaration is assumed to:
|
||||
// The given callee is assumed to:
|
||||
// * Have not been imported before.
|
||||
// * Be of supported type (ignoring parameters).
|
||||
//
|
||||
// `signature` specifies how to convert the C++ function signature to the Carbon
|
||||
// function signature.
|
||||
static auto ImportFunction(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::ImportIRInstId import_ir_inst_id,
|
||||
clang::FunctionDecl* clang_decl,
|
||||
SemIR::ClangDeclSignatureId signature_id)
|
||||
-> std::optional<SemIR::FunctionId> {
|
||||
const CalleeFunctionInfo& function_info)
|
||||
-> std::optional<SemIR::InstId> {
|
||||
StartFunctionSignature(context);
|
||||
|
||||
auto function_params_insts = CreateFunctionSignatureInsts(
|
||||
context, loc_id, import_ir_inst_id, clang_decl, signature_id);
|
||||
auto function_params_insts =
|
||||
CreateFunctionSignatureInsts(context, loc_id, function_info);
|
||||
|
||||
auto [pattern_block_id, decl_block_id] =
|
||||
FinishFunctionSignature(context, /*check_unused=*/false);
|
||||
@@ -1888,7 +1884,8 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
|
||||
|
||||
auto virtual_modifier = SemIR::Function::VirtualModifier::None;
|
||||
int32_t virtual_index = -1;
|
||||
if (auto* method_decl = dyn_cast<clang::CXXMethodDecl>(clang_decl)) {
|
||||
if (auto* method_decl =
|
||||
dyn_cast_if_present<clang::CXXMethodDecl>(function_info.decl)) {
|
||||
if (method_decl->size_overridden_methods()) {
|
||||
virtual_modifier = SemIR::Function::VirtualModifier::Override;
|
||||
} else if (method_decl->isVirtual()) {
|
||||
@@ -1911,19 +1908,22 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
|
||||
|
||||
SemIR::FunctionFields::EvaluationMode evaluation_mode =
|
||||
SemIR::FunctionFields::EvaluationMode::None;
|
||||
if (clang_decl->isConsteval()) {
|
||||
evaluation_mode = SemIR::FunctionFields::EvaluationMode::MustEval;
|
||||
} else if (clang_decl->isConstexpr()) {
|
||||
evaluation_mode = SemIR::FunctionFields::EvaluationMode::Eval;
|
||||
if (function_info.decl != nullptr) {
|
||||
if (function_info.decl->isConsteval()) {
|
||||
evaluation_mode = SemIR::FunctionFields::EvaluationMode::MustEval;
|
||||
} else if (function_info.decl->isConstexpr()) {
|
||||
evaluation_mode = SemIR::FunctionFields::EvaluationMode::Eval;
|
||||
}
|
||||
}
|
||||
|
||||
auto [decl_id, function_id] = MakeFunctionDecl(
|
||||
context, import_ir_inst_id, decl_block_id, /*build_generic=*/false,
|
||||
context, function_info.sem_ir_loc, decl_block_id, /*build_generic=*/false,
|
||||
/*is_definition=*/false,
|
||||
SemIR::Function{
|
||||
{
|
||||
.name_id = GetFunctionName(context, clang_decl),
|
||||
.parent_scope_id = GetParentNameScopeId(context, clang_decl),
|
||||
.name_id = GetFunctionName(context, function_info),
|
||||
.parent_scope_id =
|
||||
GetParentNameScopeId(context, function_info.decl),
|
||||
.generic_id = SemIR::GenericId::None,
|
||||
.first_param_node_id = Parse::NodeId::None,
|
||||
.last_param_node_id = Parse::NodeId::None,
|
||||
@@ -1953,13 +1953,7 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
|
||||
function_params_insts->param_patterns_id),
|
||||
}});
|
||||
context.imports().push_back(decl_id);
|
||||
|
||||
context.clang_decls().Add(
|
||||
{.key = SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, signature_id),
|
||||
.inst_id = decl_id,
|
||||
.is_imported = true});
|
||||
|
||||
return function_id;
|
||||
return decl_id;
|
||||
}
|
||||
|
||||
// Imports a C++ function, returning a corresponding Carbon function.
|
||||
@@ -1992,20 +1986,21 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
auto import_ir_inst_id =
|
||||
AddImportIRInst(context.sem_ir(), clang_decl->getLocation());
|
||||
|
||||
CARBON_CHECK(clang_decl->getFunctionType()->isFunctionProtoType(),
|
||||
"Not Prototype function (non-C++ code)");
|
||||
auto function_id = ImportFunction(context, loc_id, import_ir_inst_id,
|
||||
clang_decl, signature_id);
|
||||
if (!function_id) {
|
||||
CalleeFunctionInfo callee_info(context, clang_decl, key.signature_id);
|
||||
auto function_decl_id = ImportFunction(context, loc_id, callee_info);
|
||||
if (!function_decl_id) {
|
||||
MarkFailedDecl(context, key);
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
context.clang_decls().Add(
|
||||
{.key = key, .inst_id = *function_decl_id, .is_imported = true});
|
||||
|
||||
SemIR::Function& function_info = context.functions().Get(*function_id);
|
||||
if (IsCppThunkRequired(context, function_info)) {
|
||||
auto function_id =
|
||||
context.insts().GetAs<SemIR::FunctionDecl>(*function_decl_id).function_id;
|
||||
SemIR::Function& imported_function = context.functions().Get(function_id);
|
||||
if (IsCppThunkRequired(context, callee_info)) {
|
||||
Diagnostics::AnnotationScope annotate_diagnostics(
|
||||
&context.emitter(), [&](auto& builder) {
|
||||
CARBON_DIAGNOSTIC(InCppThunk, Note,
|
||||
@@ -2014,7 +2009,7 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
|
||||
});
|
||||
|
||||
if (clang::FunctionDecl* thunk_clang_decl =
|
||||
BuildCppThunk(context, function_info)) {
|
||||
BuildCppThunk(context, callee_info)) {
|
||||
SemIR::ClangDeclSignature thunk_signature;
|
||||
thunk_signature.kind = SemIR::ClangDeclSignature::Normal;
|
||||
thunk_signature.num_params =
|
||||
@@ -2025,14 +2020,20 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::ClangDeclSignatureId thunk_signature_id =
|
||||
context.clang_decl_signatures().Add(std::move(thunk_signature));
|
||||
|
||||
if (auto thunk_function_id =
|
||||
ImportFunction(context, loc_id, import_ir_inst_id,
|
||||
thunk_clang_decl, thunk_signature_id)) {
|
||||
auto& thunk_function = context.functions().Get(*thunk_function_id);
|
||||
thunk_function.SetCppThunk(function_info.first_owning_decl_id);
|
||||
SemIR::InstId thunk_function_decl_id =
|
||||
thunk_function.first_owning_decl_id;
|
||||
function_info.SetHasCppThunk(thunk_function_decl_id);
|
||||
CalleeFunctionInfo thunk_callee_info(context, thunk_clang_decl,
|
||||
thunk_signature_id);
|
||||
if (auto thunk_decl_id =
|
||||
ImportFunction(context, loc_id, thunk_callee_info)) {
|
||||
context.clang_decls().Add({.key = SemIR::ClangDeclKey::ForFunctionDecl(
|
||||
thunk_clang_decl, thunk_signature_id),
|
||||
.inst_id = *thunk_decl_id,
|
||||
.is_imported = true});
|
||||
auto thunk_function_id = context.insts()
|
||||
.GetAs<SemIR::FunctionDecl>(*thunk_decl_id)
|
||||
.function_id;
|
||||
auto& thunk_function = context.functions().Get(thunk_function_id);
|
||||
thunk_function.SetCppThunk(imported_function.first_owning_decl_id);
|
||||
imported_function.SetHasCppThunk(*thunk_decl_id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2045,7 +2046,7 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
|
||||
if (clang_decl->isTrivial()) {
|
||||
// Trivial destructors map to a "no_op" builtin.
|
||||
if (isa<clang::CXXDestructorDecl>(clang_decl)) {
|
||||
function_info.SetBuiltinFunction(SemIR::BuiltinFunctionKind::NoOp);
|
||||
imported_function.SetBuiltinFunction(SemIR::BuiltinFunctionKind::NoOp);
|
||||
}
|
||||
// TODO: Should we model a trivial default constructor as performing
|
||||
// value-initialization (zero-initializing all fields) or
|
||||
@@ -2055,7 +2056,7 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
}
|
||||
|
||||
return function_info.first_owning_decl_id;
|
||||
return *function_decl_id;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
+124
-178
@@ -75,11 +75,13 @@ static auto GetGlobalDecl(const clang::FunctionDecl* decl)
|
||||
// Returns the C++ thunk mangled name given the callee function.
|
||||
static auto GenerateThunkMangledName(
|
||||
clang::MangleContext& mangle_context,
|
||||
const clang::FunctionDecl& callee_function_decl,
|
||||
const clang::FunctionDecl* callee_function_decl,
|
||||
const SemIR::ClangDeclSignature& signature) -> std::string {
|
||||
RawStringOstream mangled_name_stream;
|
||||
mangle_context.mangleName(GetGlobalDecl(&callee_function_decl),
|
||||
mangled_name_stream);
|
||||
if (callee_function_decl != nullptr) {
|
||||
mangle_context.mangleName(GetGlobalDecl(callee_function_decl),
|
||||
mangled_name_stream);
|
||||
}
|
||||
switch (signature.kind) {
|
||||
case SemIR::ClangDeclSignature::Normal:
|
||||
mangled_name_stream << ".carbon_thunk";
|
||||
@@ -107,7 +109,10 @@ static auto GenerateThunkMangledName(
|
||||
}
|
||||
};
|
||||
|
||||
if (IsObjectMemberFunction(callee_function_decl)) {
|
||||
// If there is no decl, the callee is a function pointer, which we treat as
|
||||
// the thunk's `self` parameter.
|
||||
if (callee_function_decl == nullptr ||
|
||||
IsObjectMemberFunction(*callee_function_decl)) {
|
||||
append_mode(signature.self_passing_mode);
|
||||
}
|
||||
for (auto mode : signature.passing_modes) {
|
||||
@@ -160,122 +165,69 @@ static auto IsSimpleAbiType(clang::ASTContext& ast_context,
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Information about the callee of a thunk.
|
||||
struct CalleeFunctionInfo {
|
||||
explicit CalleeFunctionInfo(clang::FunctionDecl* decl,
|
||||
const SemIR::ClangDeclSignature* signature)
|
||||
: decl(decl),
|
||||
signature(signature),
|
||||
num_params(signature->num_params +
|
||||
decl->hasCXXExplicitFunctionObjectParameter()) {
|
||||
auto& ast_context = decl->getASTContext();
|
||||
const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
|
||||
bool is_ctor = isa<clang::CXXConstructorDecl>(decl);
|
||||
has_object_parameter = IsObjectMemberFunction(*decl);
|
||||
if (has_object_parameter && method_decl->isImplicitObjectMemberFunction()) {
|
||||
implicit_object_parameter_type =
|
||||
method_decl->getFunctionObjectParameterReferenceType();
|
||||
CalleeFunctionInfo::CalleeFunctionInfo(Context& context,
|
||||
clang::FunctionDecl* decl,
|
||||
SemIR::ClangDeclSignatureId signature_id)
|
||||
: decl(decl),
|
||||
decl_name(decl->getDeclName()),
|
||||
clang_loc(decl->getLocation()),
|
||||
sem_ir_loc(AddImportIRInst(context.sem_ir(), clang_loc)),
|
||||
function_type(decl->getType()->getAs<clang::FunctionProtoType>()),
|
||||
signature_id(signature_id),
|
||||
signature(&context.clang_decl_signatures().Get(signature_id)),
|
||||
num_callee_params(signature->num_params +
|
||||
decl->hasCXXExplicitFunctionObjectParameter()) {
|
||||
auto& ast_context = decl->getASTContext();
|
||||
const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
|
||||
bool is_ctor = isa<clang::CXXConstructorDecl>(decl);
|
||||
if (IsObjectMemberFunction(*decl)) {
|
||||
self_param_type = method_decl->getFunctionObjectParameterReferenceType();
|
||||
if (method_decl->isImplicitObjectMemberFunction()) {
|
||||
self_param_kind = SelfParamKind::ImplicitObjectParam;
|
||||
} else {
|
||||
self_param_kind = SelfParamKind::ExplicitObjectParam;
|
||||
}
|
||||
effective_return_type =
|
||||
is_ctor ? ast_context.getCanonicalTagType(method_decl->getParent())
|
||||
: decl->getReturnType();
|
||||
has_simple_return_type = IsSimpleAbiType(ast_context, effective_return_type,
|
||||
/*for_parameter=*/false);
|
||||
} else {
|
||||
self_param_kind = SelfParamKind::None;
|
||||
}
|
||||
effective_return_type =
|
||||
is_ctor ? ast_context.getCanonicalTagType(method_decl->getParent())
|
||||
: decl->getReturnType();
|
||||
has_simple_return_type = IsSimpleAbiType(ast_context, effective_return_type,
|
||||
/*for_parameter=*/false);
|
||||
}
|
||||
|
||||
// Returns whether this callee has an implicit `this` parameter.
|
||||
auto has_implicit_object_parameter() const -> bool {
|
||||
return !implicit_object_parameter_type.isNull();
|
||||
}
|
||||
auto CalleeFunctionInfo::GetCalleeParamIdentifier(int i) const
|
||||
-> clang::IdentifierInfo* {
|
||||
return decl->getParamDecl(i)->getIdentifier();
|
||||
}
|
||||
|
||||
// Returns whether this callee has an explicit `this` parameter.
|
||||
auto has_explicit_object_parameter() const -> bool {
|
||||
return has_object_parameter && !has_implicit_object_parameter();
|
||||
}
|
||||
auto CalleeFunctionInfo::GetCalleeParamLocation(int i) const
|
||||
-> clang::SourceLocation {
|
||||
return decl->getParamDecl(i)->getLocation();
|
||||
}
|
||||
|
||||
// Returns the number of parameters the thunk should have.
|
||||
auto num_thunk_params() const -> unsigned {
|
||||
return has_implicit_object_parameter() + num_params +
|
||||
!has_simple_return_type;
|
||||
}
|
||||
|
||||
// Returns the thunk parameter index corresponding to a given callee parameter
|
||||
// index.
|
||||
auto GetThunkParamIndex(unsigned callee_param_index) const -> unsigned {
|
||||
return has_implicit_object_parameter() + callee_param_index;
|
||||
}
|
||||
|
||||
// Returns the thunk parameter index corresponding to the parameter that holds
|
||||
// the address of the return value.
|
||||
auto GetThunkReturnParamIndex() const -> unsigned {
|
||||
CARBON_CHECK(!has_simple_return_type);
|
||||
return has_implicit_object_parameter() + num_params;
|
||||
}
|
||||
|
||||
// The callee function.
|
||||
clang::FunctionDecl* decl;
|
||||
|
||||
// The signature of the function being imported.
|
||||
const SemIR::ClangDeclSignature* signature;
|
||||
|
||||
// The number of explicit parameters to import. This may be less than the
|
||||
// number of parameters that the function has if default arguments are being
|
||||
// used.
|
||||
int num_params;
|
||||
|
||||
// Whether the callee has an object parameter, which might be explicit or
|
||||
// implicit.
|
||||
bool has_object_parameter;
|
||||
|
||||
// If the callee has an implicit object parameter, the type of that parameter,
|
||||
// which will always be a reference type. Otherwise a null type.
|
||||
clang::QualType implicit_object_parameter_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;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
|
||||
auto IsCppThunkRequired(Context& context, const CalleeFunctionInfo& callee_info)
|
||||
-> bool {
|
||||
const auto* clang_decl =
|
||||
context.clang_decls().Lookup(function.first_decl_id());
|
||||
if (!clang_decl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clang_decl->is_imported) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& signature =
|
||||
context.clang_decl_signatures().Get(clang_decl->key.signature_id);
|
||||
auto* decl = cast<clang::FunctionDecl>(clang_decl->decl());
|
||||
if (signature.kind != SemIR::ClangDeclSignature::Normal ||
|
||||
signature.num_params != static_cast<int>(decl->getNumNonObjectParams())) {
|
||||
auto* decl = cast<clang::FunctionDecl>(callee_info.decl);
|
||||
if (callee_info.signature->kind != SemIR::ClangDeclSignature::Normal ||
|
||||
callee_info.signature->num_params !=
|
||||
static_cast<int>(decl->getNumNonObjectParams())) {
|
||||
// We require a thunk if the number of parameters we want isn't all of them.
|
||||
// This happens if default arguments are in use, or (eventually) when
|
||||
// calling a varargs function.
|
||||
return true;
|
||||
}
|
||||
|
||||
CalleeFunctionInfo callee_info(decl, &signature);
|
||||
if (!callee_info.has_simple_return_type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto& ast_context = context.ast_context();
|
||||
if (callee_info.has_implicit_object_parameter() &&
|
||||
(!IsSimpleAbiType(ast_context, callee_info.implicit_object_parameter_type,
|
||||
if (!callee_info.self_param_type.isNull() &&
|
||||
(!IsSimpleAbiType(ast_context, callee_info.self_param_type,
|
||||
/*for_parameter=*/true) ||
|
||||
signature.self_passing_mode ==
|
||||
callee_info.signature->self_passing_mode ==
|
||||
SemIR::ClangDeclSignature::PassingMode::ByVar)) {
|
||||
return true;
|
||||
}
|
||||
@@ -285,7 +237,7 @@ auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
|
||||
for (int i : llvm::seq(decl->getNumParams())) {
|
||||
if (!IsSimpleAbiType(ast_context, function_type->getParamType(i),
|
||||
/*for_parameter=*/true) ||
|
||||
signature.GetPassingMode(i) ==
|
||||
callee_info.signature->GetPassingMode(i) ==
|
||||
SemIR::ClangDeclSignature::PassingMode::ByVar) {
|
||||
return true;
|
||||
}
|
||||
@@ -327,15 +279,13 @@ static auto BuildThunkParameterTypes(clang::ASTContext& ast_context,
|
||||
-> llvm::SmallVector<clang::QualType> {
|
||||
llvm::SmallVector<clang::QualType> thunk_param_types;
|
||||
thunk_param_types.reserve(callee_info.num_thunk_params());
|
||||
if (callee_info.has_implicit_object_parameter()) {
|
||||
thunk_param_types.push_back(callee_info.implicit_object_parameter_type);
|
||||
if (callee_info.callee_param_to_carbon_param_offset() > 0) {
|
||||
thunk_param_types.push_back(callee_info.self_param_type);
|
||||
}
|
||||
|
||||
const auto* function_type =
|
||||
callee_info.decl->getType()->castAs<clang::FunctionProtoType>();
|
||||
for (int i : llvm::seq(callee_info.num_params)) {
|
||||
thunk_param_types.push_back(
|
||||
GetThunkParameterType(ast_context, function_type->getParamType(i)));
|
||||
for (int i : llvm::seq(callee_info.num_callee_params)) {
|
||||
thunk_param_types.push_back(GetThunkParameterType(
|
||||
ast_context, callee_info.function_type->getParamType(i)));
|
||||
}
|
||||
|
||||
if (!callee_info.has_simple_return_type) {
|
||||
@@ -360,7 +310,7 @@ static auto BuildThunkParameters(clang::ASTContext& ast_context,
|
||||
unsigned num_thunk_params = thunk_function_decl->getNumParams();
|
||||
thunk_params.reserve(num_thunk_params);
|
||||
|
||||
if (callee_info.has_implicit_object_parameter()) {
|
||||
if (callee_info.callee_param_to_carbon_param_offset() > 0) {
|
||||
clang::ParmVarDecl* thunk_param =
|
||||
clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc,
|
||||
clang_loc, &ast_context.Idents.get("this"),
|
||||
@@ -369,23 +319,24 @@ static auto BuildThunkParameters(clang::ASTContext& ast_context,
|
||||
thunk_params.push_back(thunk_param);
|
||||
}
|
||||
|
||||
for (int i : llvm::seq(callee_info.num_params)) {
|
||||
for (int i : llvm::seq(callee_info.num_callee_params)) {
|
||||
clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
|
||||
ast_context, thunk_function_decl, clang_loc, clang_loc,
|
||||
callee_info.decl->getParamDecl(i)->getIdentifier(),
|
||||
callee_info.GetCalleeParamIdentifier(i),
|
||||
thunk_function_proto_type->getParamType(
|
||||
callee_info.GetThunkParamIndex(i)),
|
||||
i + callee_info.callee_param_to_carbon_param_offset()),
|
||||
nullptr, clang::SC_None, nullptr);
|
||||
thunk_params.push_back(thunk_param);
|
||||
}
|
||||
|
||||
if (!callee_info.has_simple_return_type) {
|
||||
clang::ParmVarDecl* thunk_param =
|
||||
clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc,
|
||||
clang_loc, &ast_context.Idents.get("return"),
|
||||
thunk_function_proto_type->getParamType(
|
||||
callee_info.GetThunkReturnParamIndex()),
|
||||
nullptr, clang::SC_None, nullptr);
|
||||
int thunk_return_index = callee_info.num_callee_params +
|
||||
callee_info.callee_param_to_carbon_param_offset();
|
||||
clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
|
||||
ast_context, thunk_function_decl, clang_loc, clang_loc,
|
||||
&ast_context.Idents.get("return"),
|
||||
thunk_function_proto_type->getParamType(thunk_return_index), nullptr,
|
||||
clang::SC_None, nullptr);
|
||||
thunk_params.push_back(thunk_param);
|
||||
}
|
||||
|
||||
@@ -440,7 +391,7 @@ static auto CreateThunkFunctionDecl(
|
||||
llvm::ArrayRef<clang::QualType> thunk_param_types) -> clang::FunctionDecl* {
|
||||
clang::ASTContext& ast_context = context.ast_context();
|
||||
clang::DeclarationName name =
|
||||
GetDeclNameForThunk(ast_context, callee_info.decl->getDeclName());
|
||||
GetDeclNameForThunk(ast_context, callee_info.decl_name);
|
||||
|
||||
auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo();
|
||||
clang::QualType thunk_function_type = ast_context.getFunctionType(
|
||||
@@ -468,7 +419,7 @@ static auto CreateThunkFunctionDecl(
|
||||
thunk_function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
|
||||
ast_context,
|
||||
GenerateThunkMangledName(context.cpp_context()->clang_mangle_context(),
|
||||
*callee_info.decl, *callee_info.signature),
|
||||
callee_info.decl, *callee_info.signature),
|
||||
clang_loc));
|
||||
|
||||
// Set function declaration type source info.
|
||||
@@ -516,11 +467,12 @@ static auto BuildParamRefForCalleeArg(clang::Sema& sema,
|
||||
clang::FunctionDecl* thunk_function_decl,
|
||||
CalleeFunctionInfo callee_info,
|
||||
unsigned callee_index) -> clang::Expr* {
|
||||
unsigned thunk_index = callee_info.GetThunkParamIndex(callee_index);
|
||||
unsigned thunk_index =
|
||||
callee_index + callee_info.callee_param_to_carbon_param_offset();
|
||||
return BuildThunkParamRef(
|
||||
sema, thunk_function_decl, thunk_index,
|
||||
callee_info.signature->GetPassingMode(callee_index),
|
||||
callee_info.decl->getParamDecl(callee_index)->getType());
|
||||
callee_info.function_type->getParamType(callee_index));
|
||||
}
|
||||
|
||||
// Builds an argument list for the callee function by creating suitable uses of
|
||||
@@ -530,12 +482,11 @@ static auto BuildCalleeArgs(clang::Sema& sema,
|
||||
CalleeFunctionInfo callee_info)
|
||||
-> llvm::SmallVector<clang::Expr*> {
|
||||
llvm::SmallVector<clang::Expr*> call_args;
|
||||
// The object parameter is always passed as `self`, not in the callee argument
|
||||
// list, so the first argument corresponds to the second parameter if there is
|
||||
// an explicit object parameter and the first parameter otherwise.
|
||||
int first_param = callee_info.has_explicit_object_parameter();
|
||||
call_args.reserve(callee_info.num_params - first_param);
|
||||
for (unsigned callee_index : llvm::seq(first_param, callee_info.num_params)) {
|
||||
call_args.reserve(callee_info.num_callee_params -
|
||||
callee_info.callee_arg_to_callee_param_offset());
|
||||
for (unsigned callee_index :
|
||||
llvm::seq(callee_info.callee_arg_to_callee_param_offset(),
|
||||
callee_info.num_callee_params)) {
|
||||
call_args.push_back(BuildParamRefForCalleeArg(sema, thunk_function_decl,
|
||||
callee_info, callee_index));
|
||||
}
|
||||
@@ -556,31 +507,40 @@ static auto BuildThunkBody(CppContext& cpp_context, clang::Sema& sema,
|
||||
// If the callee has an object parameter, build a member access expression as
|
||||
// the callee. Otherwise, build a regular reference to the function.
|
||||
clang::ExprResult callee;
|
||||
if (callee_info.has_object_parameter) {
|
||||
clang::QualType object_param_type =
|
||||
cast<clang::CXXMethodDecl>(callee_info.decl)
|
||||
->getFunctionObjectParameterReferenceType();
|
||||
auto* object_param_ref = BuildThunkParamRef(
|
||||
sema, thunk_function_decl, 0, callee_info.signature->self_passing_mode,
|
||||
object_param_type);
|
||||
constexpr bool IsArrow = false;
|
||||
auto object =
|
||||
sema.PerformMemberExprBaseConversion(object_param_ref, IsArrow);
|
||||
if (object.isInvalid()) {
|
||||
return clang::StmtError();
|
||||
switch (callee_info.self_param_kind) {
|
||||
case CalleeFunctionInfo::SelfParamKind::ExplicitObjectParam:
|
||||
case CalleeFunctionInfo::SelfParamKind::ImplicitObjectParam: {
|
||||
clang::QualType object_param_type =
|
||||
cast<clang::CXXMethodDecl>(callee_info.decl)
|
||||
->getFunctionObjectParameterReferenceType();
|
||||
auto* object_param_ref = BuildThunkParamRef(
|
||||
sema, thunk_function_decl, 0,
|
||||
callee_info.signature->self_passing_mode, object_param_type);
|
||||
constexpr bool IsArrow = false;
|
||||
auto object =
|
||||
sema.PerformMemberExprBaseConversion(object_param_ref, IsArrow);
|
||||
if (object.isInvalid()) {
|
||||
return clang::StmtError();
|
||||
}
|
||||
callee = sema.BuildMemberExpr(
|
||||
object.get(), IsArrow, clang_loc, clang::NestedNameSpecifierLoc(),
|
||||
clang::SourceLocation(), callee_info.decl,
|
||||
clang::DeclAccessPair::make(callee_info.decl, clang::AS_public),
|
||||
/*HadMultipleCandidates=*/false,
|
||||
clang::DeclarationNameInfo(callee_info.decl->getDeclName(),
|
||||
clang_loc),
|
||||
sema.getASTContext().BoundMemberTy, clang::VK_PRValue,
|
||||
clang::OK_Ordinary);
|
||||
break;
|
||||
}
|
||||
callee = sema.BuildMemberExpr(
|
||||
object.get(), IsArrow, clang_loc, clang::NestedNameSpecifierLoc(),
|
||||
clang::SourceLocation(), callee_info.decl,
|
||||
clang::DeclAccessPair::make(callee_info.decl, clang::AS_public),
|
||||
/*HadMultipleCandidates=*/false,
|
||||
clang::DeclarationNameInfo(callee_info.decl->getDeclName(), clang_loc),
|
||||
sema.getASTContext().BoundMemberTy, clang::VK_PRValue,
|
||||
clang::OK_Ordinary);
|
||||
} else if (!isa<clang::CXXConstructorDecl>(callee_info.decl)) {
|
||||
callee =
|
||||
sema.BuildDeclRefExpr(callee_info.decl, callee_info.decl->getType(),
|
||||
clang::VK_PRValue, clang_loc);
|
||||
case CalleeFunctionInfo::SelfParamKind::None:
|
||||
if (isa<clang::CXXConstructorDecl>(callee_info.decl)) {
|
||||
break;
|
||||
}
|
||||
callee =
|
||||
sema.BuildDeclRefExpr(callee_info.decl, callee_info.decl->getType(),
|
||||
clang::VK_PRValue, clang_loc);
|
||||
break;
|
||||
}
|
||||
|
||||
if (callee.isInvalid()) {
|
||||
@@ -592,7 +552,8 @@ static auto BuildThunkBody(CppContext& cpp_context, clang::Sema& sema,
|
||||
BuildCalleeArgs(sema, thunk_function_decl, callee_info);
|
||||
|
||||
clang::ExprResult call;
|
||||
if (auto info = clang::getConstructorInfo(callee_info.decl);
|
||||
if (auto info = callee_info.decl ? clang::getConstructorInfo(callee_info.decl)
|
||||
: clang::ConstructorInfo{};
|
||||
info.Constructor) {
|
||||
// In C++, there are no direct calls to constructors, only initialization,
|
||||
// so we need to type-check and build the call ourselves.
|
||||
@@ -620,9 +581,11 @@ static auto BuildThunkBody(CppContext& cpp_context, clang::Sema& sema,
|
||||
return sema.BuildReturnStmt(clang_loc, call.get());
|
||||
}
|
||||
|
||||
auto* return_object_addr = BuildThunkParamRef(
|
||||
sema, thunk_function_decl, callee_info.GetThunkReturnParamIndex(),
|
||||
SemIR::ClangDeclSignature::PassingMode::ByValue);
|
||||
int return_thunk_index = callee_info.num_callee_params +
|
||||
callee_info.callee_param_to_carbon_param_offset();
|
||||
auto* return_object_addr =
|
||||
BuildThunkParamRef(sema, thunk_function_decl, return_thunk_index,
|
||||
SemIR::ClangDeclSignature::PassingMode::ByValue);
|
||||
auto return_type = callee_info.effective_return_type.getNonReferenceType();
|
||||
auto* return_type_info =
|
||||
sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc);
|
||||
@@ -648,37 +611,20 @@ static auto BuildThunkBody(CppContext& cpp_context, clang::Sema& sema,
|
||||
return sema.ActOnExprStmt(placement_new, /*DiscardedValue=*/true);
|
||||
}
|
||||
|
||||
auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
|
||||
auto BuildCppThunk(Context& context, const CalleeFunctionInfo& callee_info)
|
||||
-> clang::FunctionDecl* {
|
||||
auto clang_decl_key =
|
||||
context.clang_decls().Lookup(callee_function.first_decl_id())->key;
|
||||
clang::FunctionDecl* callee_function_decl =
|
||||
clang_decl_key.decl->getAsFunction();
|
||||
CARBON_CHECK(callee_function_decl);
|
||||
|
||||
// TODO: The signature kind doesn't affect the thunk that we build, so we
|
||||
// shouldn't consider it here. However, to do that, we would need to cache the
|
||||
// thunks we build so that we don't build the same thunk multiple times if
|
||||
// it's used with multiple different signature kinds.
|
||||
const auto& signature =
|
||||
context.clang_decl_signatures().Get(clang_decl_key.signature_id);
|
||||
CalleeFunctionInfo callee_info(callee_function_decl, &signature);
|
||||
|
||||
clang::SourceLocation clang_loc = callee_function_decl->getLocation();
|
||||
CARBON_CHECK(clang_loc.isValid(), "Missing location for function");
|
||||
|
||||
// Build the thunk function declaration.
|
||||
auto thunk_param_types =
|
||||
BuildThunkParameterTypes(context.ast_context(), callee_info);
|
||||
clang::FunctionDecl* thunk_function_decl = CreateThunkFunctionDecl(
|
||||
context, callee_info, clang_loc, thunk_param_types);
|
||||
context, callee_info, callee_info.clang_loc, thunk_param_types);
|
||||
|
||||
// Build the thunk function body.
|
||||
clang::Sema& sema = context.clang_sema();
|
||||
clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
|
||||
sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
|
||||
clang::StmtResult body =
|
||||
BuildThunkBody(*context.cpp_context(), sema, clang_loc,
|
||||
BuildThunkBody(*context.cpp_context(), sema, callee_info.clang_loc,
|
||||
thunk_function_decl, callee_info);
|
||||
sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
|
||||
if (body.isInvalid()) {
|
||||
|
||||
+139
-6
@@ -5,23 +5,156 @@
|
||||
#ifndef CARBON_TOOLCHAIN_CHECK_CPP_THUNK_H_
|
||||
#define CARBON_TOOLCHAIN_CHECK_CPP_THUNK_H_
|
||||
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/TypeBase.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// Information about a C++ callee in the process of being imported. One of
|
||||
// this class's key responsibilities is to track the mapping between several
|
||||
// different calling conventions:
|
||||
// - The native C++ function, which may or may not have an object parameter,
|
||||
// and that object parameter may or may not be present in the parameter list
|
||||
// (but is never present in the corresponding argument list).
|
||||
// - The thunk that wraps it with a simple ABI, so that it can be called from
|
||||
// Carbon.
|
||||
// - The Carbon function that is actually called by user code, and maps its
|
||||
// parameters to the simple ABI.
|
||||
struct CalleeFunctionInfo {
|
||||
// Constructs a CalleeFunctionInfo that represents the given C++ function with
|
||||
// the given signature.
|
||||
explicit CalleeFunctionInfo(Context& context, clang::FunctionDecl* decl,
|
||||
SemIR::ClangDeclSignatureId signature_id);
|
||||
|
||||
// Returns the offset I such that callee parameter N corresponds to
|
||||
// parameter N+I of the imported Carbon function.
|
||||
auto callee_param_to_carbon_param_offset() const -> int {
|
||||
switch (self_param_kind) {
|
||||
case SelfParamKind::ImplicitObjectParam:
|
||||
return 1;
|
||||
case SelfParamKind::None:
|
||||
case SelfParamKind::ExplicitObjectParam:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the offset I such that callee argument N corresponds to callee
|
||||
// parameter N+I.
|
||||
auto callee_arg_to_callee_param_offset() const -> int {
|
||||
switch (self_param_kind) {
|
||||
case SelfParamKind::ExplicitObjectParam:
|
||||
return 1;
|
||||
case SelfParamKind::ImplicitObjectParam:
|
||||
case SelfParamKind::None:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of parameters that the imported Carbon function should
|
||||
// have.
|
||||
auto num_carbon_params() const -> int {
|
||||
return num_callee_params + callee_param_to_carbon_param_offset();
|
||||
}
|
||||
|
||||
// Returns the number of parameters the simple-ABI thunk should have.
|
||||
auto num_thunk_params() const -> unsigned {
|
||||
return num_carbon_params() + !has_simple_return_type;
|
||||
}
|
||||
|
||||
// Returns true if the imported Carbon function should have a self parameter.
|
||||
auto carbon_has_self_param() const -> bool {
|
||||
return self_param_kind != SelfParamKind::None;
|
||||
}
|
||||
|
||||
// Returns true if the callee has an object parameter (i.e. `this`).
|
||||
auto callee_has_object_param() const -> bool {
|
||||
return self_param_kind == SelfParamKind::ImplicitObjectParam ||
|
||||
self_param_kind == SelfParamKind::ExplicitObjectParam;
|
||||
}
|
||||
|
||||
// Returns the identifier for the i-th callee parameter, or null if it doesn't
|
||||
// have one.
|
||||
auto GetCalleeParamIdentifier(int i) const -> clang::IdentifierInfo*;
|
||||
|
||||
// Returns the location of the i-th callee parameter declaration.
|
||||
auto GetCalleeParamLocation(int i) const -> clang::SourceLocation;
|
||||
|
||||
// Information about the C++ parameter that corresponds to the `self`
|
||||
// parameter in the Carbon function.
|
||||
enum class SelfParamKind {
|
||||
// The C++ callee is an ordinary function, so it has no `self` parameter in
|
||||
// Carbon. The Nth Carbon parameter corresponds to the Nth callee argument,
|
||||
// and to the Nth callee parameter.
|
||||
None,
|
||||
// The callee is a traditional C++ method, so the Carbon `self` parameter
|
||||
// corresponds to the C++ object parameter, which is not present in
|
||||
// the callee parameter or argument list, but is passed at the callsite
|
||||
// using member access syntax. Consequently, the Nth Carbon parameter
|
||||
// corresponds to the N-1th callee argument, and the N-1th callee parameter.
|
||||
ImplicitObjectParam,
|
||||
// The callee is a C++ method with an explicit object parameter, such as
|
||||
// `F(this Foo& self)`, so the first Carbon parameter corresponds to the
|
||||
// object parameter, which is present in the callee parameter list but
|
||||
// passed at the callsite using member access syntax rather than the
|
||||
// argument list. Consequently, the Nth Carbon parameter corresponds to the
|
||||
// N-1th callee argument, and the Nth callee parameter.
|
||||
ExplicitObjectParam,
|
||||
};
|
||||
SelfParamKind self_param_kind;
|
||||
|
||||
// The callee function's declaration, or null if it doesn't have one.
|
||||
clang::FunctionDecl* decl;
|
||||
|
||||
// The name of the callee function.
|
||||
clang::DeclarationName decl_name;
|
||||
|
||||
// The location of the callee function declaration.
|
||||
clang::SourceLocation clang_loc;
|
||||
|
||||
// The SemIR representation of `clang_loc`.
|
||||
SemIR::LocId sem_ir_loc;
|
||||
|
||||
// The function type of the callee. This is never null.
|
||||
const clang::FunctionProtoType* function_type;
|
||||
|
||||
// The ID of `signature`.
|
||||
SemIR::ClangDeclSignatureId signature_id;
|
||||
|
||||
// The signature of the function being imported.
|
||||
const SemIR::ClangDeclSignature* signature;
|
||||
|
||||
// The number of explicit parameters to import. This may be less than the
|
||||
// number of parameters that the function has if default arguments are being
|
||||
// used.
|
||||
int num_callee_params;
|
||||
|
||||
// The type of the callee parameter that is treated as `self` in the Carbon
|
||||
// function, or null if there isn't one.
|
||||
clang::QualType self_param_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;
|
||||
};
|
||||
|
||||
// Returns whether the given C++ imported function requires a C++ thunk to be
|
||||
// used to call it. A C++ thunk is required for functions whose ABI uses any
|
||||
// type except void, pointer and reference types, and signed 32-bit and 64-bit
|
||||
// integers.
|
||||
auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
|
||||
auto IsCppThunkRequired(Context& context, const CalleeFunctionInfo& callee_info)
|
||||
-> bool;
|
||||
|
||||
// Given a function signature and a callee function, builds a C++ thunk with
|
||||
// simple ABI (pointers, i32 and i64 types) that calls the specified callee.
|
||||
// Assumes `IsCppThunkRequired()` return true for `callee_function`. Returns
|
||||
// `nullptr` on failure.
|
||||
auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
|
||||
// Builds a C++ thunk with simple ABI (pointers, i32 and i64 types) that calls
|
||||
// the specified callee. Assumes `IsCppThunkRequired()` return true for
|
||||
// `callee_info`. Returns `nullptr` on failure.
|
||||
auto BuildCppThunk(Context& context, const CalleeFunctionInfo& callee_info)
|
||||
-> clang::FunctionDecl*;
|
||||
|
||||
// Builds a call to a thunk function that forwards a call argument list built
|
||||
|
||||
@@ -52,6 +52,8 @@ fn G(x: Cpp.X) {
|
||||
// CHECK:STDOUT: import_ir_inst2: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000002}
|
||||
// CHECK:STDOUT: import_ir_inst3: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000003}
|
||||
// CHECK:STDOUT: import_ir_inst4: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000004}
|
||||
// CHECK:STDOUT: import_ir_inst5: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000005}
|
||||
// CHECK:STDOUT: import_ir_inst6: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000006}
|
||||
// CHECK:STDOUT: clang_decls:
|
||||
// CHECK:STDOUT: clang_decl_id50000000: {key: {decl: "namespace Carbon {\n}"}, inst_id: inst(Package)}
|
||||
// CHECK:STDOUT: clang_decl_id50000001: {key: {decl: "<translation unit>"}, inst_id: inst50000011}
|
||||
|
||||
Reference in New Issue
Block a user