|
|
@@ -4,6 +4,7 @@
|
|
|
|
|
|
#include "toolchain/check/cpp/export.h"
|
|
|
|
|
|
+#include "llvm/Support/Casting.h"
|
|
|
#include "toolchain/check/cpp/import.h"
|
|
|
#include "toolchain/check/cpp/location.h"
|
|
|
#include "toolchain/check/cpp/type_mapping.h"
|
|
|
@@ -165,6 +166,70 @@ auto ExportClassToCpp(Context& context, SemIR::LocId loc_id,
|
|
|
return record_decl;
|
|
|
}
|
|
|
|
|
|
+namespace {
|
|
|
+struct FunctionInfo {
|
|
|
+ explicit FunctionInfo(Context& context, SemIR::FunctionId function_id,
|
|
|
+ const SemIR::Function& function,
|
|
|
+ clang::DeclContext* decl_context)
|
|
|
+ : function_id(function_id),
|
|
|
+ function(function),
|
|
|
+ decl_context(decl_context) {
|
|
|
+ auto function_params =
|
|
|
+ context.inst_blocks().Get(function.call_param_patterns_id);
|
|
|
+
|
|
|
+ // Get the function's `self` parameter type, if present.
|
|
|
+ if (function.call_param_ranges.implicit_size() > 0) {
|
|
|
+ CARBON_CHECK(function.call_param_ranges.implicit_size() == 1);
|
|
|
+
|
|
|
+ auto param_inst_id =
|
|
|
+ function_params[function.call_param_ranges.implicit_begin().index];
|
|
|
+ auto scrutinee_type_id = ExtractScrutineeType(
|
|
|
+ context.sem_ir(), context.insts().Get(param_inst_id).type_id());
|
|
|
+ self_type_id = scrutinee_type_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get the function's explicit parameter types.
|
|
|
+ function_params =
|
|
|
+ function_params.drop_front(function.call_param_ranges.implicit_size());
|
|
|
+ function_params =
|
|
|
+ function_params.drop_back(function.call_param_ranges.return_size());
|
|
|
+ for (auto param_inst_id : function_params) {
|
|
|
+ auto scrutinee_type_id = ExtractScrutineeType(
|
|
|
+ context.sem_ir(), context.insts().Get(param_inst_id).type_id());
|
|
|
+ param_type_ids.push_back(scrutinee_type_id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get the `StorageClass` to use for `CXXMethodDecl`s.
|
|
|
+ auto GetStorageClass() const -> clang::StorageClass {
|
|
|
+ if (has_self()) {
|
|
|
+ return clang::SC_None;
|
|
|
+ } else {
|
|
|
+ return clang::SC_Static;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Whether the function has a `self` parameter.
|
|
|
+ auto has_self() const -> bool { return self_type_id != SemIR::TypeId::None; }
|
|
|
+
|
|
|
+ SemIR::FunctionId function_id;
|
|
|
+ const SemIR::Function& function;
|
|
|
+
|
|
|
+ // Parent scope in the C++ AST where a C++ thunk for this function can
|
|
|
+ // be created. If the function is a method, this will be a
|
|
|
+ // `CXXRecordDecl`.
|
|
|
+ clang::DeclContext* decl_context;
|
|
|
+
|
|
|
+ // Types of the function's explicit parameters (excludes implicit
|
|
|
+ // parameters and return parameters).
|
|
|
+ llvm::SmallVector<SemIR::TypeId> param_type_ids;
|
|
|
+
|
|
|
+ // Type of the function's `self` parameter, or `None` if the function
|
|
|
+ // is not a method.
|
|
|
+ SemIR::TypeId self_type_id = SemIR::TypeId::None;
|
|
|
+};
|
|
|
+} // namespace
|
|
|
+
|
|
|
// Create a `clang::FunctionDecl` for the given Carbon function. This
|
|
|
// can be used to call the Carbon function from C++. The Carbon
|
|
|
// function's ABI must be compatible with C++.
|
|
|
@@ -178,15 +243,21 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context,
|
|
|
auto clang_loc = GetCppLocation(context, loc_id);
|
|
|
|
|
|
const SemIR::Function& function = context.functions().Get(function_id);
|
|
|
+ FunctionInfo callee(context, function_id, function, nullptr);
|
|
|
|
|
|
// Get parameters types.
|
|
|
- auto carbon_function_params =
|
|
|
- context.inst_blocks().Get(function.call_param_patterns_id);
|
|
|
llvm::SmallVector<clang::QualType> cpp_param_types;
|
|
|
- for (auto param_inst_id : carbon_function_params) {
|
|
|
- auto scrutinee_type_id = ExtractScrutineeType(
|
|
|
- context.sem_ir(), context.insts().Get(param_inst_id).type_id());
|
|
|
- auto cpp_type = MapToCppType(context, scrutinee_type_id);
|
|
|
+ if (callee.has_self()) {
|
|
|
+ auto cpp_type = MapToCppType(context, callee.self_type_id);
|
|
|
+ if (cpp_type.isNull()) {
|
|
|
+ context.TODO(loc_id, "failed to map Carbon self type to C++");
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ cpp_type = context.ast_context().getLValueReferenceType(cpp_type);
|
|
|
+ cpp_param_types.push_back(cpp_type);
|
|
|
+ }
|
|
|
+ for (auto param_type_id : callee.param_type_ids) {
|
|
|
+ auto cpp_type = MapToCppType(context, param_type_id);
|
|
|
if (cpp_type.isNull()) {
|
|
|
context.TODO(loc_id, "failed to map Carbon type to C++");
|
|
|
return nullptr;
|
|
|
@@ -206,7 +277,7 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context,
|
|
|
CARBON_CHECK(identifier_info, "function with non-identifier name {0}",
|
|
|
function.name_id);
|
|
|
|
|
|
- auto* function_decl = clang::FunctionDecl::Create(
|
|
|
+ clang::FunctionDecl* function_decl = clang::FunctionDecl::Create(
|
|
|
context.ast_context(), context.ast_context().getTranslationUnitDecl(),
|
|
|
/*StartLoc=*/clang_loc, /*NLoc=*/clang_loc, identifier_info,
|
|
|
cpp_function_type, /*TInfo=*/nullptr, clang::SC_Extern);
|
|
|
@@ -233,10 +304,9 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context,
|
|
|
|
|
|
// Create the declaration of the C++ thunk.
|
|
|
static auto BuildCppToCarbonThunkDecl(
|
|
|
- Context& context, SemIR::LocId loc_id, clang::DeclContext* decl_context,
|
|
|
+ Context& context, SemIR::LocId loc_id, const FunctionInfo& target,
|
|
|
clang::DeclarationName thunk_name,
|
|
|
- llvm::ArrayRef<clang::QualType> thunk_param_types,
|
|
|
- SemIR::TypeId return_type_id) -> clang::FunctionDecl* {
|
|
|
+ llvm::ArrayRef<clang::QualType> thunk_param_types) -> clang::FunctionDecl* {
|
|
|
clang::ASTContext& ast_context = context.ast_context();
|
|
|
|
|
|
auto clang_loc = GetCppLocation(context, loc_id);
|
|
|
@@ -244,6 +314,7 @@ static auto BuildCppToCarbonThunkDecl(
|
|
|
// Get the C++ return type (this corresponds to the return type of the
|
|
|
// target Carbon function).
|
|
|
clang::QualType cpp_return_type = context.ast_context().VoidTy;
|
|
|
+ auto return_type_id = target.function.GetDeclaredReturnType(context.sem_ir());
|
|
|
if (return_type_id != SemIR::TypeId::None) {
|
|
|
cpp_return_type = MapToCppType(context, return_type_id);
|
|
|
if (cpp_return_type.isNull()) {
|
|
|
@@ -267,21 +338,22 @@ static auto BuildCppToCarbonThunkDecl(
|
|
|
auto trailing_requires_clause = clang::AssociatedConstraint();
|
|
|
|
|
|
clang::FunctionDecl* thunk_function_decl = nullptr;
|
|
|
- if (auto* parent_class = dyn_cast<clang::CXXRecordDecl>(decl_context)) {
|
|
|
- // TODO: Support non-static methods.
|
|
|
+ if (auto* parent_class =
|
|
|
+ dyn_cast<clang::CXXRecordDecl>(target.decl_context)) {
|
|
|
thunk_function_decl = clang::CXXMethodDecl::Create(
|
|
|
ast_context, parent_class, clang_loc, name_info, thunk_function_type,
|
|
|
- tinfo, clang::SC_Static, uses_fp_intrin, inline_specified,
|
|
|
+ tinfo, target.GetStorageClass(), uses_fp_intrin, inline_specified,
|
|
|
constexpr_kind, clang_loc, trailing_requires_clause);
|
|
|
// TODO: Map Carbon access to C++ access.
|
|
|
thunk_function_decl->setAccess(clang::AS_public);
|
|
|
} else {
|
|
|
thunk_function_decl = clang::FunctionDecl::Create(
|
|
|
- ast_context, decl_context, clang_loc, name_info, thunk_function_type,
|
|
|
- tinfo, clang::SC_None, uses_fp_intrin, inline_specified,
|
|
|
+ ast_context, target.decl_context, clang_loc, name_info,
|
|
|
+ thunk_function_type, tinfo, clang::SC_None, uses_fp_intrin,
|
|
|
+ inline_specified,
|
|
|
/*hasWrittenPrototype=*/true, constexpr_kind, trailing_requires_clause);
|
|
|
}
|
|
|
- decl_context->addDecl(thunk_function_decl);
|
|
|
+ target.decl_context->addHiddenDecl(thunk_function_decl);
|
|
|
|
|
|
llvm::SmallVector<clang::ParmVarDecl*> param_var_decls;
|
|
|
for (auto [i, type] : llvm::enumerate(thunk_param_types)) {
|
|
|
@@ -305,6 +377,7 @@ static auto BuildCppToCarbonThunkDecl(
|
|
|
// Create the body of a C++ thunk that calls a Carbon thunk. The
|
|
|
// arguments are passed by reference to the callee.
|
|
|
static auto BuildCppToCarbonThunkBody(clang::Sema& sema,
|
|
|
+ const FunctionInfo& target,
|
|
|
clang::FunctionDecl* function_decl,
|
|
|
clang::FunctionDecl* callee_function_decl)
|
|
|
-> clang::StmtResult {
|
|
|
@@ -342,6 +415,20 @@ static auto BuildCppToCarbonThunkBody(clang::Sema& sema,
|
|
|
clang_loc);
|
|
|
|
|
|
llvm::SmallVector<clang::Expr*> call_args;
|
|
|
+ // For methods, pass the `this` pointer as the first argument to the callee.
|
|
|
+ if (target.has_self()) {
|
|
|
+ auto* parent_class = cast<clang::CXXRecordDecl>(target.decl_context);
|
|
|
+ clang::QualType class_type =
|
|
|
+ sema.getASTContext().getCanonicalTagType(parent_class);
|
|
|
+ auto class_ptr_type = sema.getASTContext().getPointerType(class_type);
|
|
|
+ auto* this_expr = sema.BuildCXXThisExpr(clang_loc, class_ptr_type,
|
|
|
+ /*IsImplicit=*/true);
|
|
|
+ this_expr = clang::UnaryOperator::Create(
|
|
|
+ sema.getASTContext(), this_expr, clang::UO_Deref, class_type,
|
|
|
+ clang::ExprValueKind::VK_LValue, clang::ExprObjectKind::OK_Ordinary,
|
|
|
+ clang_loc, /*CanOverflow=*/false, clang::FPOptionsOverride());
|
|
|
+ call_args.push_back(this_expr);
|
|
|
+ }
|
|
|
for (auto* param : function_decl->parameters()) {
|
|
|
clang::Expr* call_arg =
|
|
|
sema.BuildDeclRefExpr(param, param->getType().getNonReferenceType(),
|
|
|
@@ -376,18 +463,22 @@ static auto BuildCppToCarbonThunkBody(clang::Sema& sema,
|
|
|
// parameter types are mapped from the parameters of the target function
|
|
|
// with `MapToCppType`. (Note that the target function here is the
|
|
|
// callee of the Carbon thunk.)
|
|
|
-static auto BuildCppToCarbonThunk(
|
|
|
- Context& context, SemIR::LocId loc_id, clang::DeclContext* decl_context,
|
|
|
- llvm::StringRef base_name, clang::FunctionDecl* carbon_function_decl,
|
|
|
- llvm::ArrayRef<SemIR::TypeId> callee_param_type_ids,
|
|
|
- SemIR::TypeId return_type_id) -> clang::FunctionDecl* {
|
|
|
+static auto BuildCppToCarbonThunk(Context& context, SemIR::LocId loc_id,
|
|
|
+ const FunctionInfo& target,
|
|
|
+ llvm::StringRef base_name,
|
|
|
+ clang::FunctionDecl* carbon_function_decl)
|
|
|
+ -> clang::FunctionDecl* {
|
|
|
// Create the thunk's name.
|
|
|
llvm::SmallString<64> thunk_name = base_name;
|
|
|
- thunk_name += "__cpp_thunk";
|
|
|
+ // TODO: changing the thunk name for methods hits this clang assertion:
|
|
|
+ // https://github.com/llvm/llvm-project/blob/058398c4ceaf/clang/lib/AST/Expr.cpp#L1720
|
|
|
+ if (!target.has_self()) {
|
|
|
+ thunk_name += "__cpp_thunk";
|
|
|
+ }
|
|
|
auto& thunk_ident = context.ast_context().Idents.get(thunk_name);
|
|
|
|
|
|
llvm::SmallVector<clang::QualType> param_types;
|
|
|
- for (auto type_id : callee_param_type_ids) {
|
|
|
+ for (auto type_id : target.param_type_ids) {
|
|
|
auto cpp_type = MapToCppType(context, type_id);
|
|
|
if (cpp_type.isNull()) {
|
|
|
context.TODO(loc_id, "failed to map C++ type to Carbon");
|
|
|
@@ -397,14 +488,14 @@ static auto BuildCppToCarbonThunk(
|
|
|
}
|
|
|
|
|
|
auto* thunk_function_decl = BuildCppToCarbonThunkDecl(
|
|
|
- context, loc_id, decl_context, &thunk_ident, param_types, return_type_id);
|
|
|
+ context, loc_id, target, &thunk_ident, 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 = BuildCppToCarbonThunkBody(sema, thunk_function_decl,
|
|
|
- carbon_function_decl);
|
|
|
+ clang::StmtResult body = BuildCppToCarbonThunkBody(
|
|
|
+ sema, target, thunk_function_decl, carbon_function_decl);
|
|
|
sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
|
|
|
CARBON_CHECK(!body.isInvalid());
|
|
|
|
|
|
@@ -415,13 +506,12 @@ static auto BuildCppToCarbonThunk(
|
|
|
|
|
|
// Create a Carbon thunk that calls `callee`. The thunk's parameters are
|
|
|
// all references to the callee parameter type.
|
|
|
-static auto BuildCarbonToCarbonThunk(
|
|
|
- Context& context, SemIR::LocId loc_id, SemIR::FunctionId callee_function_id,
|
|
|
- const SemIR::Function& callee,
|
|
|
- llvm::ArrayRef<SemIR::TypeId> callee_param_type_ids) -> SemIR::FunctionId {
|
|
|
+static auto BuildCarbonToCarbonThunk(Context& context, SemIR::LocId loc_id,
|
|
|
+ const FunctionInfo& target)
|
|
|
+ -> SemIR::FunctionId {
|
|
|
// Create the thunk's name.
|
|
|
llvm::SmallString<64> thunk_name =
|
|
|
- context.names().GetFormatted(callee.name_id);
|
|
|
+ context.names().GetFormatted(target.function.name_id);
|
|
|
thunk_name += "__carbon_thunk";
|
|
|
auto& ident = context.ast_context().Idents.get(thunk_name);
|
|
|
auto thunk_name_id =
|
|
|
@@ -430,24 +520,27 @@ static auto BuildCarbonToCarbonThunk(
|
|
|
// Get the thunk's parameters. These match the callee parameters, with
|
|
|
// the addition of an output parameter for the callee's return value
|
|
|
// (if it has one).
|
|
|
- llvm::SmallVector<SemIR::TypeId> thunk_param_type_ids(callee_param_type_ids);
|
|
|
- auto callee_return_type_id = callee.GetDeclaredReturnType(context.sem_ir());
|
|
|
+ llvm::SmallVector<SemIR::TypeId> thunk_param_type_ids(target.param_type_ids);
|
|
|
+ auto callee_return_type_id =
|
|
|
+ target.function.GetDeclaredReturnType(context.sem_ir());
|
|
|
if (callee_return_type_id != SemIR::TypeId::None) {
|
|
|
thunk_param_type_ids.push_back(callee_return_type_id);
|
|
|
}
|
|
|
|
|
|
auto carbon_thunk_function_id =
|
|
|
- MakeGeneratedFunctionDecl(context, loc_id,
|
|
|
- {.parent_scope_id = callee.parent_scope_id,
|
|
|
- .name_id = thunk_name_id,
|
|
|
- .param_type_ids = thunk_param_type_ids,
|
|
|
- .params_are_refs = true})
|
|
|
+ MakeGeneratedFunctionDecl(
|
|
|
+ context, loc_id,
|
|
|
+ {.parent_scope_id = target.function.parent_scope_id,
|
|
|
+ .name_id = thunk_name_id,
|
|
|
+ .self_type_id = target.self_type_id,
|
|
|
+ .param_type_ids = thunk_param_type_ids,
|
|
|
+ .params_are_refs = true})
|
|
|
.second;
|
|
|
|
|
|
BuildThunkDefinitionForExport(
|
|
|
- context, carbon_thunk_function_id, callee_function_id,
|
|
|
+ context, carbon_thunk_function_id, target.function_id,
|
|
|
context.functions().Get(carbon_thunk_function_id).first_decl_id(),
|
|
|
- callee.first_decl_id());
|
|
|
+ target.function.first_decl_id());
|
|
|
|
|
|
return carbon_thunk_function_id;
|
|
|
}
|
|
|
@@ -464,13 +557,6 @@ auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
- if (callee.call_param_ranges.implicit_size() != 0) {
|
|
|
- context.TODO(loc_id,
|
|
|
- "unsupported: C++ calling a Carbon function with "
|
|
|
- "an implicit parameter");
|
|
|
- return nullptr;
|
|
|
- }
|
|
|
-
|
|
|
// Map the parent scope into the C++ AST.
|
|
|
auto* decl_context =
|
|
|
ExportNameScopeToCpp(context, loc_id, callee.parent_scope_id);
|
|
|
@@ -478,24 +564,13 @@ auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
- // Get the parameter types of the Carbon function being
|
|
|
- // called. Exclude return params, if present.
|
|
|
- auto callee_function_params =
|
|
|
- context.inst_blocks().Get(callee.call_param_patterns_id);
|
|
|
- callee_function_params =
|
|
|
- callee_function_params.drop_back(callee.call_param_ranges.return_size());
|
|
|
-
|
|
|
- llvm::SmallVector<SemIR::TypeId> callee_param_type_ids;
|
|
|
- for (auto callee_param_inst_id : callee_function_params) {
|
|
|
- auto scrutinee_type_id = ExtractScrutineeType(
|
|
|
- context.sem_ir(), context.insts().Get(callee_param_inst_id).type_id());
|
|
|
- callee_param_type_ids.push_back(scrutinee_type_id);
|
|
|
- }
|
|
|
+ FunctionInfo target_function_info(context, callee_function_id, callee,
|
|
|
+ decl_context);
|
|
|
|
|
|
// Create a Carbon thunk that calls the callee. The thunk's parameters
|
|
|
// are all references so that the ABI is compatible with C++ callers.
|
|
|
- auto carbon_thunk_function_id = BuildCarbonToCarbonThunk(
|
|
|
- context, loc_id, callee_function_id, callee, callee_param_type_ids);
|
|
|
+ auto carbon_thunk_function_id =
|
|
|
+ BuildCarbonToCarbonThunk(context, loc_id, target_function_info);
|
|
|
|
|
|
// Create a `clang::FunctionDecl` that can be used to call the Carbon thunk.
|
|
|
auto* carbon_function_decl = BuildCppFunctionDeclForCarbonFn(
|
|
|
@@ -505,10 +580,9 @@ auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
|
|
}
|
|
|
|
|
|
// Create a C++ thunk that calls the Carbon thunk.
|
|
|
- return BuildCppToCarbonThunk(context, loc_id, decl_context,
|
|
|
+ return BuildCppToCarbonThunk(context, loc_id, target_function_info,
|
|
|
context.names().GetFormatted(callee.name_id),
|
|
|
- carbon_function_decl, callee_param_type_ids,
|
|
|
- callee.GetDeclaredReturnType(context.sem_ir()));
|
|
|
+ carbon_function_decl);
|
|
|
}
|
|
|
|
|
|
} // namespace Carbon::Check
|