mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Import C++ constructors of class Type fn Type(...) -> Type (#5879)
Only supports classes with a single (non copy non move) constructor (without default values), until overloading is supported. Based on #5878. C++ Interop Demo: ```c++ // hello_world.h #include <cstdio> class C { public: C(int x, int y) : x_(x), y_(y) {} int x() const { return x_;} int y() const { return y_;} private: int x_; int y_; }; void hello_world(C* _Nonnull c); ``` ```c++ // hello_world.cpp #include "hello_world.h" #include <cstdio> void hello_world(C* _Nonnull c) { printf("C.x = %d. C.y = %d\n", c->x(), c->y()); } ``` ```carbon // main.carbon library "Main"; import Cpp library "hello_world.h"; fn Run() -> i32 { var c : Cpp.C = Cpp.C.C(1, 2); Cpp.hello_world(&c); return 0; } ``` ```shell $ clang -c hello_world.cpp $ bazel-bin/toolchain/carbon compile main.carbon $ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo $ ./demo C.x = 1. C.y = 2 ``` Part of #5880.
This commit is contained in:
+178
-40
@@ -45,6 +45,7 @@
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
#include "toolchain/sem_ir/clang_decl.h"
|
||||
#include "toolchain/sem_ir/class.h"
|
||||
#include "toolchain/sem_ir/function.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/inst.h"
|
||||
@@ -455,10 +456,10 @@ auto ImportCppFiles(Context& context,
|
||||
return std::move(generated_ast);
|
||||
}
|
||||
|
||||
// Look ups the given name in the Clang AST in a specific scope. Returns the
|
||||
// Looks up the given name in the Clang AST in a specific scope. Returns the
|
||||
// lookup result if lookup was successful.
|
||||
static auto ClangLookup(Context& context, SemIR::NameScopeId scope_id,
|
||||
SemIR::NameId name_id)
|
||||
static auto ClangLookupName(Context& context, SemIR::NameScopeId scope_id,
|
||||
SemIR::NameId name_id)
|
||||
-> std::optional<clang::LookupResult> {
|
||||
std::optional<llvm::StringRef> name =
|
||||
context.names().GetAsStringIfIdentifier(name_id);
|
||||
@@ -497,6 +498,112 @@ static auto ClangLookup(Context& context, SemIR::NameScopeId scope_id,
|
||||
return lookup;
|
||||
}
|
||||
|
||||
// Looks up for constructors in the class scope and returns the lookup result.
|
||||
static auto ClangConstructorLookup(const Context& context,
|
||||
SemIR::NameScopeId scope_id)
|
||||
-> clang::DeclContextLookupResult {
|
||||
const SemIR::NameScope& scope = context.sem_ir().name_scopes().Get(scope_id);
|
||||
|
||||
clang::Sema& sema = context.sem_ir().cpp_ast()->getSema();
|
||||
clang::Decl* decl =
|
||||
context.sem_ir().clang_decls().Get(scope.clang_decl_context_id()).decl;
|
||||
return sema.LookupConstructors(cast<clang::CXXRecordDecl>(decl));
|
||||
}
|
||||
|
||||
// Returns true if the given Clang declaration is the implicit injected class
|
||||
// name within the class.
|
||||
static auto IsDeclInjectedClassName(const Context& context,
|
||||
SemIR::NameScopeId scope_id,
|
||||
SemIR::NameId name_id,
|
||||
const clang::NamedDecl* named_decl)
|
||||
-> bool {
|
||||
if (!named_decl->isImplicit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* record_decl = dyn_cast<clang::CXXRecordDecl>(named_decl);
|
||||
if (!record_decl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SemIR::ClangDecl& clang_decl = context.sem_ir().clang_decls().Get(
|
||||
context.sem_ir().name_scopes().Get(scope_id).clang_decl_context_id());
|
||||
const auto* scope_record_decl = cast<clang::CXXRecordDecl>(clang_decl.decl);
|
||||
|
||||
const clang::ASTContext& ast_context =
|
||||
context.sem_ir().cpp_ast()->getASTContext();
|
||||
CARBON_CHECK(
|
||||
ast_context.getCanonicalType(
|
||||
ast_context.getRecordType(scope_record_decl)) ==
|
||||
ast_context.getCanonicalType(ast_context.getRecordType(record_decl)));
|
||||
|
||||
auto class_decl =
|
||||
context.sem_ir().insts().GetAs<SemIR::ClassDecl>(clang_decl.inst_id);
|
||||
CARBON_CHECK(name_id ==
|
||||
context.sem_ir().classes().Get(class_decl.class_id).name_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Looks up the given name in the Clang AST in a specific scope, and returns the
|
||||
// found declaration and its access. If the found declaration is the injected
|
||||
// class name, looks up constructors instead. If not found, returns `nullopt`.
|
||||
// If there's not a single result, returns `nullptr` and default access.
|
||||
// Otherwise, returns the single declaration and its access.
|
||||
static auto ClangLookup(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::NameScopeId scope_id, SemIR::NameId name_id)
|
||||
-> std::optional<std::tuple<clang::NamedDecl*, clang::AccessSpecifier>> {
|
||||
auto lookup = ClangLookupName(context, scope_id, name_id);
|
||||
if (!lookup) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::tuple<clang::NamedDecl*, clang::AccessSpecifier> result{
|
||||
nullptr, clang::AccessSpecifier::AS_none};
|
||||
|
||||
// Access checks are performed separately by the Carbon name lookup logic.
|
||||
lookup->suppressAccessDiagnostics();
|
||||
|
||||
if (!lookup->isSingleResult()) {
|
||||
// Clang will diagnose ambiguous lookup results for us.
|
||||
if (!lookup->isAmbiguous()) {
|
||||
context.TODO(loc_id,
|
||||
llvm::formatv("Unsupported: Lookup succeeded but couldn't "
|
||||
"find a single result; LookupResultKind: {0}",
|
||||
static_cast<int>(lookup->getResultKind())));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!IsDeclInjectedClassName(context, scope_id, name_id,
|
||||
lookup->getFoundDecl())) {
|
||||
result = {lookup->getFoundDecl(), lookup->begin().getAccess()};
|
||||
return result;
|
||||
}
|
||||
|
||||
clang::DeclContextLookupResult constructors_lookup =
|
||||
ClangConstructorLookup(context, scope_id);
|
||||
|
||||
llvm::SmallVector<clang::CXXConstructorDecl*> constructors;
|
||||
for (clang::Decl* decl : constructors_lookup) {
|
||||
auto* constructor = cast<clang::CXXConstructorDecl>(decl);
|
||||
if (constructor->isDeleted() || constructor->isCopyOrMoveConstructor()) {
|
||||
continue;
|
||||
}
|
||||
constructors.push_back(constructor);
|
||||
}
|
||||
if (constructors.size() != 1) {
|
||||
context.TODO(
|
||||
loc_id,
|
||||
llvm::formatv("Unsupported: Constructors lookup succeeded but couldn't "
|
||||
"find a single result; Found {0} constructors",
|
||||
constructors.size()));
|
||||
return result;
|
||||
}
|
||||
result = {constructors[0], constructors[0]->getAccess()};
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns whether `decl` already mapped to an instruction.
|
||||
static auto IsClangDeclImported(const Context& context, clang::Decl* decl)
|
||||
-> bool {
|
||||
@@ -1109,7 +1216,8 @@ static auto MakeImplicitParamPatternsBlockId(
|
||||
Context& context, SemIR::LocId loc_id,
|
||||
const clang::FunctionDecl& clang_decl) -> SemIR::InstBlockId {
|
||||
const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(&clang_decl);
|
||||
if (!method_decl || method_decl->isStatic()) {
|
||||
if (!method_decl || method_decl->isStatic() ||
|
||||
isa<clang::CXXConstructorDecl>(clang_decl)) {
|
||||
return SemIR::InstBlockId::Empty;
|
||||
}
|
||||
|
||||
@@ -1229,22 +1337,54 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
|
||||
return context.inst_blocks().Add(params);
|
||||
}
|
||||
|
||||
// Returns the return type of the given function declaration. In case of an
|
||||
// unsupported return type, it produces a diagnostic and returns
|
||||
// `SemIR::ErrorInst::InstId`.
|
||||
// Returns the return `TypeExpr` of the given function declaration. In case of
|
||||
// an unsupported return type, returns `SemIR::ErrorInst::InstId`. Constructors
|
||||
// are treated as returning a class instance.
|
||||
// TODO: Support more return types.
|
||||
static auto GetReturnType(Context& context, SemIR::LocId loc_id,
|
||||
const clang::FunctionDecl* clang_decl)
|
||||
-> SemIR::InstId {
|
||||
static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id,
|
||||
clang::FunctionDecl* clang_decl) -> TypeExpr {
|
||||
clang::QualType ret_type = clang_decl->getReturnType();
|
||||
if (ret_type->isVoidType()) {
|
||||
return SemIR::InstId::None;
|
||||
if (!ret_type->isVoidType()) {
|
||||
TypeExpr mapped_type = MapType(context, loc_id, ret_type);
|
||||
if (!mapped_type.inst_id.has_value()) {
|
||||
return {.inst_id = SemIR::ErrorInst::TypeInstId,
|
||||
.type_id = SemIR::ErrorInst::TypeId};
|
||||
}
|
||||
return mapped_type;
|
||||
}
|
||||
|
||||
auto [type_inst_id, type_id] = MapType(context, loc_id, ret_type);
|
||||
if (!isa<clang::CXXConstructorDecl>(clang_decl)) {
|
||||
// void.
|
||||
return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
|
||||
}
|
||||
|
||||
// TODO: Make this a `PartialType`.
|
||||
SemIR::TypeInstId record_type_inst_id = context.types().GetAsTypeInstId(
|
||||
context.sem_ir()
|
||||
.clang_decls()
|
||||
.Get(context.sem_ir().clang_decls().Lookup(
|
||||
cast<clang::Decl>(clang_decl->getParent())))
|
||||
.inst_id);
|
||||
return {
|
||||
.inst_id = record_type_inst_id,
|
||||
.type_id = context.types().GetTypeIdForTypeInstId(record_type_inst_id)};
|
||||
}
|
||||
|
||||
// Returns the return pattern of the given function declaration. In case of an
|
||||
// unsupported return type, it produces a diagnostic and returns
|
||||
// `SemIR::ErrorInst::InstId`. Constructors are treated as returning a class
|
||||
// instance.
|
||||
static auto GetReturnPattern(Context& context, SemIR::LocId loc_id,
|
||||
clang::FunctionDecl* clang_decl) -> SemIR::InstId {
|
||||
auto [type_inst_id, type_id] = GetReturnTypeExpr(context, loc_id, clang_decl);
|
||||
if (!type_inst_id.has_value()) {
|
||||
context.TODO(loc_id, llvm::formatv("Unsupported: return type: {0}",
|
||||
ret_type.getAsString()));
|
||||
// void.
|
||||
return SemIR::InstId::None;
|
||||
}
|
||||
if (type_inst_id == SemIR::ErrorInst::TypeInstId) {
|
||||
context.TODO(loc_id,
|
||||
llvm::formatv("Unsupported: return type: {0}",
|
||||
clang_decl->getReturnType().getAsString()));
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
auto pattern_type_id = GetPatternType(context, type_id);
|
||||
@@ -1283,10 +1423,10 @@ struct FunctionParamsInsts {
|
||||
// Produces a diagnostic and returns `std::nullopt` if the function declaration
|
||||
// has an unsupported parameter type.
|
||||
static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
|
||||
const clang::FunctionDecl* clang_decl)
|
||||
clang::FunctionDecl* clang_decl)
|
||||
-> std::optional<FunctionParamsInsts> {
|
||||
if (isa<clang::CXXConstructorDecl, clang::CXXDestructorDecl>(clang_decl)) {
|
||||
context.TODO(loc_id, "Unsupported: Constructor/Destructor");
|
||||
if (isa<clang::CXXDestructorDecl>(clang_decl)) {
|
||||
context.TODO(loc_id, "Unsupported: Destructor");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -1300,7 +1440,7 @@ static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
|
||||
if (!param_patterns_id.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto return_slot_pattern_id = GetReturnType(context, loc_id, clang_decl);
|
||||
auto return_slot_pattern_id = GetReturnPattern(context, loc_id, clang_decl);
|
||||
if (SemIR::ErrorInst::InstId == return_slot_pattern_id) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1343,8 +1483,19 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
|
||||
AddPlaceholderInstInNoBlock(context, Parse::NodeId::None, function_decl);
|
||||
context.imports().push_back(decl_id);
|
||||
|
||||
SemIR::NameId function_name_id =
|
||||
isa<clang::CXXConstructorDecl>(clang_decl)
|
||||
? context.classes()
|
||||
.Get(context.insts()
|
||||
.GetAs<SemIR::ClassDecl>(LookupClangDeclInstId(
|
||||
context,
|
||||
cast<clang::Decl>(clang_decl->getParent())))
|
||||
.class_id)
|
||||
.name_id
|
||||
: AddIdentifierName(context, clang_decl->getName());
|
||||
|
||||
auto function_info = SemIR::Function{
|
||||
{.name_id = AddIdentifierName(context, clang_decl->getName()),
|
||||
{.name_id = function_name_id,
|
||||
.parent_scope_id = GetParentNameScopeId(context, clang_decl),
|
||||
.generic_id = SemIR::GenericId::None,
|
||||
.first_param_node_id = Parse::NodeId::None,
|
||||
@@ -1526,8 +1677,7 @@ static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
|
||||
context.TODO(loc_id, llvm::formatv("Unsupported: Declaration type {0}",
|
||||
clang_decl->getDeclKindName())
|
||||
.str());
|
||||
clang_decl->getDeclKindName()));
|
||||
return SemIR::InstId::None;
|
||||
}
|
||||
|
||||
@@ -1600,31 +1750,19 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
|
||||
builder.Note(loc_id, InCppNameLookup, name_id);
|
||||
});
|
||||
|
||||
auto lookup = ClangLookup(context, scope_id, name_id);
|
||||
if (!lookup) {
|
||||
auto decl_and_access = ClangLookup(context, loc_id, scope_id, name_id);
|
||||
if (!decl_and_access) {
|
||||
return SemIR::ScopeLookupResult::MakeNotFound();
|
||||
}
|
||||
|
||||
// Access checks are performed separately by the Carbon name lookup logic.
|
||||
lookup->suppressAccessDiagnostics();
|
||||
|
||||
if (!lookup->isSingleResult()) {
|
||||
// Clang will diagnose ambiguous lookup results for us.
|
||||
if (!lookup->isAmbiguous()) {
|
||||
context.TODO(loc_id,
|
||||
llvm::formatv("Unsupported: Lookup succeeded but couldn't "
|
||||
"find a single result; LookupResultKind: {0}",
|
||||
static_cast<int>(lookup->getResultKind()))
|
||||
.str());
|
||||
}
|
||||
auto [decl, access] = *decl_and_access;
|
||||
if (!decl) {
|
||||
context.name_scopes().AddRequiredName(scope_id, name_id,
|
||||
SemIR::ErrorInst::InstId);
|
||||
return SemIR::ScopeLookupResult::MakeError();
|
||||
}
|
||||
|
||||
return ImportNameDeclIntoScope(context, loc_id, scope_id, name_id,
|
||||
lookup->getFoundDecl(),
|
||||
lookup->begin().getAccess());
|
||||
return ImportNameDeclIntoScope(context, loc_id, scope_id, name_id, decl,
|
||||
access);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
Reference in New Issue
Block a user