mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 09:06:21 +01:00
Support for converting a function to a C++ function pointer (#7789)
This commit is contained in:
@@ -745,7 +745,7 @@ static auto MapToCppThunkParamType(Context& context, SemIR::TypeId type_id)
|
||||
}
|
||||
|
||||
// Build FunctionInfo for an export of the given Carbon function. Exports the
|
||||
// name scope if necessary.
|
||||
// name scope if necessary. Returns `nullopt` if an error was diagnosed.
|
||||
static auto BuildFunctionInfo(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId callee_function_id)
|
||||
-> std::optional<FunctionInfo> {
|
||||
@@ -1512,8 +1512,10 @@ static auto ExportGenericFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
return template_decl;
|
||||
}
|
||||
|
||||
auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId callee_function_id)
|
||||
// Exports the given function to C++ and returns the exported `NamedDecl`,
|
||||
// or `nullptr` if an error was diagnosed.
|
||||
static auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId callee_function_id)
|
||||
-> clang::NamedDecl* {
|
||||
auto target = BuildFunctionInfo(context, loc_id, callee_function_id);
|
||||
if (!target) {
|
||||
@@ -1531,6 +1533,93 @@ auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
return ExportNonGenericFunctionToCpp(context, loc_id, *target);
|
||||
}
|
||||
|
||||
auto GetOrExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId function_id)
|
||||
-> clang::NamedDecl* {
|
||||
SemIR::Function& function = context.functions().Get(function_id);
|
||||
if (auto clang_decl_id =
|
||||
context.clang_decls().LookupId(function.first_decl_id());
|
||||
clang_decl_id.has_value()) {
|
||||
return llvm::cast<clang::NamedDecl>(
|
||||
context.clang_decls().Get(clang_decl_id).decl());
|
||||
}
|
||||
|
||||
auto* named_decl = ExportFunctionToCpp(context, loc_id, function_id);
|
||||
if (!named_decl) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto* function_template_decl =
|
||||
llvm::dyn_cast<clang::FunctionTemplateDecl>(named_decl)) {
|
||||
context.clang_decls().Add(
|
||||
{.key = SemIR::ClangDeclKey::ForNonFunctionDecl(function_template_decl),
|
||||
.inst_id = function.first_decl_id()});
|
||||
return function_template_decl;
|
||||
}
|
||||
|
||||
auto* clang_function_decl = llvm::cast<clang::FunctionDecl>(named_decl);
|
||||
|
||||
SemIR::ClangDeclSignature thunk_signature{
|
||||
.kind = SemIR::ClangDeclSignature::Normal,
|
||||
.num_params = static_cast<int32_t>(clang_function_decl->getNumParams())};
|
||||
thunk_signature.passing_modes.assign(
|
||||
thunk_signature.num_params,
|
||||
SemIR::ClangDeclSignature::PassingMode::ByValue);
|
||||
context.clang_decls().Add(
|
||||
{.key = SemIR::ClangDeclKey::ForFunctionDecl(
|
||||
clang_function_decl,
|
||||
context.clang_decl_signatures().Add(std::move(thunk_signature))),
|
||||
.inst_id = function.first_decl_id()});
|
||||
return clang_function_decl;
|
||||
}
|
||||
|
||||
auto ExportFunctionToCppPointerConversion(
|
||||
Context& context, SemIR::InstId src_id, SemIR::FunctionType src_type,
|
||||
SemIR::CppFunctionPointerType dest_type, bool diagnose) -> bool {
|
||||
if (src_type.specific_id.has_value()) {
|
||||
context.TODO(
|
||||
src_id,
|
||||
"can't convert generic function specific to a C++ function pointer");
|
||||
return false;
|
||||
}
|
||||
auto* src_clang_decl = GetOrExportFunctionToCpp(context, SemIR::LocId(src_id),
|
||||
src_type.function_id);
|
||||
if (src_clang_decl == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CARBON_CHECK(!src_clang_decl->isTemplateDecl(),
|
||||
"can't form a pointer to a template");
|
||||
|
||||
const auto* exported_fn_type =
|
||||
src_clang_decl->getFunctionType()
|
||||
->getAsCanonical<clang::FunctionProtoType>();
|
||||
auto dest_function_ptr_type_info =
|
||||
context.clang_function_pointer_types().Get(dest_type.clang_type_id);
|
||||
const auto* dest_fn_type =
|
||||
dest_function_ptr_type_info.clang_type->getPointeeType()
|
||||
->getAsCanonical<clang::FunctionProtoType>();
|
||||
|
||||
if (exported_fn_type != dest_fn_type) {
|
||||
if (diagnose) {
|
||||
auto function = context.functions().Get(src_type.function_id);
|
||||
CARBON_DIAGNOSTIC(ExportedFunctionPtrTypeMismatch, Error,
|
||||
"can't convert exported function type to `{0}`",
|
||||
CppType);
|
||||
CARBON_DIAGNOSTIC(ExportedFromFunction, Note,
|
||||
"function exported with type `{0}`", CppType);
|
||||
context.emitter()
|
||||
.Build(src_id, ExportedFunctionPtrTypeMismatch, dest_fn_type)
|
||||
.Note(function.first_decl_id(), ExportedFromFunction,
|
||||
exported_fn_type)
|
||||
.Emit();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns whether the given class has any abstract methods.
|
||||
static auto HasAnyAbstractMethods(Context& context,
|
||||
const SemIR::Class& class_info,
|
||||
|
||||
@@ -77,11 +77,21 @@ auto ExportFieldToCpp(Context& context, SemIR::InstId field_inst_id,
|
||||
SemIR::FieldDecl field_decl,
|
||||
SemIR::SpecificId specific_id) -> clang::FieldDecl*;
|
||||
|
||||
// Get a `clang::FunctionDecl` that can be used to call a Carbon function.
|
||||
// If the function is generic, a `clang::FunctionTemplateDecl` will be
|
||||
// created instead.
|
||||
auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId function_id) -> clang::NamedDecl*;
|
||||
// Returns the `ClangDeclId` of a `clang::FunctionDecl` or
|
||||
// `clang::FunctionTemplateDecl` that can be used to call the given function.
|
||||
// Returns null if an error was diagnosed.
|
||||
auto GetOrExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId function_id)
|
||||
-> clang::NamedDecl*;
|
||||
|
||||
// Exports the necessary declarations to permit conversion from the given
|
||||
// Carbon function type to the given C++ function pointer type. If the
|
||||
// conversion would be invalid, this will return false, and if `diagnose` is
|
||||
// true it will also emit one or more diagnostics explaining the reason it would
|
||||
// be invalid. In those diagnostics, `src_id` is the source of the conversion.
|
||||
auto ExportFunctionToCppPointerConversion(
|
||||
Context& context, SemIR::InstId src_id, SemIR::FunctionType src_type,
|
||||
SemIR::CppFunctionPointerType dest_type, bool diagnose) -> bool;
|
||||
|
||||
// Exports a Carbon virtual function as a C++ `clang::FunctionDecl` declaration.
|
||||
// Does not emit a definition.
|
||||
|
||||
@@ -165,9 +165,6 @@ class CarbonExternalASTSource : public SemIR::ReadOnlyASTSource {
|
||||
auto MapInstIdToClangDeclOrType(LookupResult lookup)
|
||||
-> std::variant<clang::NamedDecl*, clang::QualType>;
|
||||
|
||||
auto GetOrExportFunctionToCpp(SemIR::InstId target_inst_id,
|
||||
SemIR::FunctionId function_id)
|
||||
-> clang::NamedDecl*;
|
||||
// Get a current best-effort location for the current position within C++
|
||||
// processing.
|
||||
auto GetCurrentCppLocId() -> SemIR::LocId {
|
||||
@@ -233,7 +230,7 @@ auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup)
|
||||
context_->types().GetTypeInstId(target_inst.type_id());
|
||||
auto callee = GetCallee(context_->sem_ir(), target_inst_id);
|
||||
if (auto* callee_function = std::get_if<SemIR::CalleeFunction>(&callee)) {
|
||||
return GetOrExportFunctionToCpp(target_inst_id,
|
||||
return GetOrExportFunctionToCpp(*context_, SemIR::LocId(target_inst_id),
|
||||
callee_function->function_id);
|
||||
} else if (auto generic_class =
|
||||
context_->insts().TryGetAs<SemIR::GenericClassType>(
|
||||
@@ -255,46 +252,6 @@ auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup)
|
||||
}
|
||||
}
|
||||
|
||||
auto CarbonExternalASTSource::GetOrExportFunctionToCpp(
|
||||
SemIR::InstId target_inst_id, SemIR::FunctionId function_id)
|
||||
-> clang::NamedDecl* {
|
||||
SemIR::Function& function = context_->functions().Get(function_id);
|
||||
if (const auto* clang_decl =
|
||||
context_->clang_decls().Lookup(function.first_decl_id())) {
|
||||
return cast<clang::NamedDecl>(clang_decl->decl());
|
||||
}
|
||||
|
||||
auto* named_decl =
|
||||
ExportFunctionToCpp(*context_, SemIR::LocId(target_inst_id), function_id);
|
||||
if (!named_decl) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto* function_template_decl =
|
||||
llvm::dyn_cast<clang::FunctionTemplateDecl>(named_decl)) {
|
||||
context_->clang_decls().Add(
|
||||
{.key = SemIR::ClangDeclKey::ForNonFunctionDecl(function_template_decl),
|
||||
.inst_id = function.first_decl_id()});
|
||||
return function_template_decl;
|
||||
}
|
||||
|
||||
auto* clang_function_decl = llvm::cast<clang::FunctionDecl>(named_decl);
|
||||
|
||||
SemIR::ClangDeclSignature thunk_signature;
|
||||
thunk_signature.kind = SemIR::ClangDeclSignature::Normal;
|
||||
thunk_signature.num_params =
|
||||
static_cast<int32_t>(clang_function_decl->getNumParams());
|
||||
thunk_signature.passing_modes.assign(
|
||||
thunk_signature.num_params,
|
||||
SemIR::ClangDeclSignature::PassingMode::ByValue);
|
||||
context_->clang_decls().Add(
|
||||
{.key = SemIR::ClangDeclKey::ForFunctionDecl(
|
||||
clang_function_decl,
|
||||
context_->clang_decl_signatures().Add(std::move(thunk_signature))),
|
||||
.inst_id = function.first_decl_id()});
|
||||
return clang_function_decl;
|
||||
}
|
||||
|
||||
auto CarbonExternalASTSource::BuildCarbonNamespace() -> void {
|
||||
static const llvm::StringLiteral carbon_namespace_name = "Carbon";
|
||||
auto& ast_context = context_->ast_context();
|
||||
|
||||
@@ -133,6 +133,12 @@ static auto BuildCopyWitness(Context& context, SemIR::LocId loc_id,
|
||||
-> SemIR::InstId {
|
||||
auto& clang_sema = context.clang_sema();
|
||||
|
||||
if (context.constant_values().InstIs<SemIR::CppFunctionPointerType>(
|
||||
query_self_const_id)) {
|
||||
return BuildPrimitiveCopyWitness(context, loc_id, query_self_const_id,
|
||||
query_specific_interface);
|
||||
}
|
||||
|
||||
auto* tag_decl = TypeAsTagDecl(context, query_self_const_id);
|
||||
if (!tag_decl) {
|
||||
return SemIR::InstId::None;
|
||||
|
||||
@@ -1992,13 +1992,6 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
|
||||
static auto DefineAsThunkCall(Context& context, SemIR::LocId loc_id,
|
||||
const CalleeFunctionInfo& callee_info,
|
||||
SemIR::Function& function) -> void {
|
||||
Diagnostics::AnnotationScope annotate_diagnostics(
|
||||
&context.emitter(), [&](auto& builder) {
|
||||
CARBON_DIAGNOSTIC(InCppThunk, Note,
|
||||
"in thunk for C++ function used here");
|
||||
builder.Note(loc_id, InCppThunk);
|
||||
});
|
||||
|
||||
clang::FunctionDecl* thunk_clang_decl = BuildCppThunk(context, callee_info);
|
||||
if (thunk_clang_decl == nullptr) {
|
||||
return;
|
||||
@@ -2075,6 +2068,12 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_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,
|
||||
"in thunk for C++ function used here");
|
||||
builder.Note(loc_id, InCppThunk);
|
||||
});
|
||||
DefineAsThunkCall(context, loc_id, callee_info, imported_function);
|
||||
} else {
|
||||
// Inform Clang that the function has been referenced. This will trigger
|
||||
|
||||
@@ -284,6 +284,20 @@ static auto TryMapType(Context& context, SemIR::TypeId type_id)
|
||||
case SemIR::FloatLiteralType::Kind: {
|
||||
return context.ast_context().DoubleTy;
|
||||
}
|
||||
case CARBON_KIND(SemIR::FunctionType function_type): {
|
||||
auto decl_id =
|
||||
context.functions().Get(function_type.function_id).first_decl_id();
|
||||
const auto* clang_decl = GetOrExportFunctionToCpp(
|
||||
context, SemIR::LocId(decl_id), function_type.function_id);
|
||||
if (clang_decl == nullptr) {
|
||||
return clang::QualType();
|
||||
}
|
||||
clang::QualType clang_fn_type(clang_decl->getFunctionType(), /*Quals=*/0);
|
||||
clang::QualType clang_ptr_type =
|
||||
context.ast_context().getPointerType(clang_fn_type);
|
||||
return context.ast_context().getAttributedType(
|
||||
clang::attr::TypeNonNull, clang_ptr_type, clang_ptr_type);
|
||||
}
|
||||
case CARBON_KIND(SemIR::PointerType pointer_type): {
|
||||
return WrappedType{
|
||||
.inner_type_id =
|
||||
|
||||
Reference in New Issue
Block a user