mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 21:51:12 +01:00
Add support for C++ pointers to Carbon methods (#7881)
C++ pointers to Carbon methods are supported using the same mechanisms as for Carbon functions: we export the method to C++ and then form a pointer to the exported method. This change does not support invoking a C++ method pointer from Carbon, which will be more complicated because we need to model instance binding on the Carbon side.
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include "toolchain/check/pattern.h"
|
||||
#include "toolchain/check/thunk.h"
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/sem_ir/generic.h"
|
||||
#include "toolchain/sem_ir/mangler.h"
|
||||
#include "toolchain/sem_ir/pattern.h"
|
||||
@@ -165,6 +166,14 @@ auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(decl_context)) {
|
||||
// We can't use a class as a name scope until it's complete, so we need to
|
||||
// ensure it's complete here.
|
||||
context.clang_sema().RequireCompleteType(
|
||||
GetCppLocation(context, loc_id),
|
||||
context.ast_context().getCanonicalTagType(record_decl),
|
||||
clang::diag::err_incomplete_member_access);
|
||||
}
|
||||
return decl_context;
|
||||
}
|
||||
|
||||
@@ -1591,27 +1600,39 @@ auto ExportFunctionToCppPointerConversion(
|
||||
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>();
|
||||
clang::QualType src_fn_type(src_clang_decl->getFunctionType(), /*Quals=*/0);
|
||||
clang::QualType src_ptr_type;
|
||||
if (const auto* src_method_decl =
|
||||
llvm::dyn_cast<clang::CXXMethodDecl>(src_clang_decl);
|
||||
src_method_decl != nullptr && src_method_decl->isInstance()) {
|
||||
src_ptr_type = context.ast_context().getMemberPointerType(
|
||||
src_fn_type, std::nullopt, src_method_decl->getParent());
|
||||
} else {
|
||||
src_ptr_type = context.ast_context().getPointerType(src_fn_type);
|
||||
}
|
||||
|
||||
// TODO: consider directly diagnosing things like function pointer/method
|
||||
// pointer mismatch, or parent type mismatch, rather than deferring them to
|
||||
// the general pointer type mismatch diagnostic below.
|
||||
|
||||
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>();
|
||||
clang::QualType dest_ptr_type(dest_function_ptr_type_info.clang_type,
|
||||
/*Quals=*/0);
|
||||
|
||||
if (exported_fn_type != dest_fn_type) {
|
||||
if (src_ptr_type.getCanonicalType() != dest_ptr_type.getCanonicalType()) {
|
||||
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(
|
||||
ExportedFunctionPtrTypeMismatch, Error,
|
||||
"can't convert exported {0:member |}function pointer to `{1}`",
|
||||
Diagnostics::BoolAsSelect, CppType);
|
||||
CARBON_DIAGNOSTIC(ExportedFromFunction, Note,
|
||||
"function exported with type `{0}`", CppType);
|
||||
"function exported with pointer type `{0}`", CppType);
|
||||
context.emitter()
|
||||
.Build(src_id, ExportedFunctionPtrTypeMismatch, dest_fn_type)
|
||||
.Note(function.first_decl_id(), ExportedFromFunction,
|
||||
exported_fn_type)
|
||||
.Build(src_id, ExportedFunctionPtrTypeMismatch,
|
||||
src_ptr_type->isMemberFunctionPointerType(), dest_ptr_type)
|
||||
.Note(function.first_decl_id(), ExportedFromFunction, src_ptr_type)
|
||||
.Emit();
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1325,7 +1325,8 @@ static auto MapTagType(Context& context, const clang::TagType& type)
|
||||
|
||||
static auto MapFunctionPointerType(Context& context, SemIR::LocId /*loc_id*/,
|
||||
clang::QualType type) -> TypeExpr {
|
||||
CARBON_CHECK(type->isFunctionPointerType());
|
||||
CARBON_CHECK(type->isFunctionPointerType() ||
|
||||
type->isMemberFunctionPointerType());
|
||||
|
||||
auto clang_type_id = context.clang_function_pointer_types().Lookup(
|
||||
type.getCanonicalType().getTypePtr());
|
||||
@@ -1350,7 +1351,7 @@ static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id,
|
||||
return MapTagType(context, *tag_type);
|
||||
}
|
||||
|
||||
if (type->isFunctionPointerType()) {
|
||||
if (type->isFunctionPointerType() || type->isMemberFunctionPointerType()) {
|
||||
return MapFunctionPointerType(context, loc_id, type);
|
||||
}
|
||||
|
||||
@@ -2105,7 +2106,8 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
|
||||
static auto ImportFunctionPointer(Context& context,
|
||||
const clang::Type* pointer_type)
|
||||
-> SemIR::InstId {
|
||||
CARBON_CHECK(pointer_type->isFunctionPointerType());
|
||||
CARBON_CHECK(pointer_type->isFunctionPointerType() ||
|
||||
pointer_type->isMemberFunctionPointerType());
|
||||
// Allocate an ID for the function pointer type and return it.
|
||||
pointer_type = clang::QualType(pointer_type, /*Quals=*/0)
|
||||
.getCanonicalType()
|
||||
@@ -2130,6 +2132,10 @@ auto ImportFunctionPointerInvoke(
|
||||
if (info.decl_id.has_value()) {
|
||||
return info;
|
||||
}
|
||||
if (info.clang_type->isMemberFunctionPointerType()) {
|
||||
context.TODO(loc_id,
|
||||
"invoking a C++ member function pointer is unsupported");
|
||||
}
|
||||
Diagnostics::AnnotationScope annotate_diagnostics(
|
||||
&context.emitter(), [&](auto& builder) {
|
||||
CARBON_DIAGNOSTIC(InCppFunctionPointerThunk, Note,
|
||||
@@ -2183,7 +2189,7 @@ static auto PushDecl(Context& context, SemIR::ClangDeclKey decl,
|
||||
static auto PushType(Context& context, clang::QualType type,
|
||||
ImportWorklist& worklist) -> void {
|
||||
while (true) {
|
||||
if (type->isFunctionPointerType()) {
|
||||
if (type->isFunctionPointerType() || type->isMemberFunctionPointerType()) {
|
||||
const clang::Type* type_ptr = type.getCanonicalType().getTypePtr();
|
||||
if (!IsImported(context, type_ptr)) {
|
||||
worklist.push_back({.key = type_ptr, .added_dependencies = false});
|
||||
@@ -2418,7 +2424,8 @@ static auto ImportAfterDependencies(Context& context, SemIR::LocId loc_id,
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
case CARBON_KIND(const clang::Type* type): {
|
||||
if (type->isFunctionPointerType()) {
|
||||
if (type->isFunctionPointerType() ||
|
||||
type->isMemberFunctionPointerType()) {
|
||||
return ImportFunctionPointer(context, type);
|
||||
}
|
||||
|
||||
|
||||
@@ -293,8 +293,14 @@ static auto TryMapType(Context& context, SemIR::TypeId type_id)
|
||||
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);
|
||||
clang::QualType clang_ptr_type;
|
||||
if (const auto* method_decl =
|
||||
llvm::dyn_cast<clang::CXXMethodDecl>(clang_decl)) {
|
||||
clang_ptr_type = context.ast_context().getMemberPointerType(
|
||||
clang_fn_type, std::nullopt, method_decl->getParent());
|
||||
} else {
|
||||
clang_ptr_type = context.ast_context().getPointerType(clang_fn_type);
|
||||
}
|
||||
return context.ast_context().getAttributedType(
|
||||
clang::attr::TypeNonNull, clang_ptr_type, clang_ptr_type);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user