mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 06:31:03 +01:00
C++ Interop: Basic C++ record (class/struct/union) import support (#5156)
Focus: Calling static C++ function defined in C++ classes. Limitations: * Ignores visibility (public / protected / private). * No support for: dynamic classes, member methods, data members, declarations without definitions, importing inheritance. Based on #5142. C++ Interop Demo with a class: ```c++ // hello_world.h namespace some_namespace { class MyClass { public: static void hello_world(); }; } // namespace some_namespace ``` ```c++ // hello_world.cpp #include "hello_world.h" #include <cstdio> namespace some_namespace { void MyClass::hello_world() { printf("Hello World!\n"); } } // namespace some_namespace ``` ```carbon // main.carbon library "Main"; import Cpp library "hello_world.h"; fn Run() -> i32 { Cpp.some_namespace.MyClass.hello_world(); 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 Hello World! ``` Part of #5150.
This commit is contained in:
@@ -15,9 +15,11 @@
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "toolchain/check/class.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/diagnostic_helpers.h"
|
||||
#include "toolchain/check/eval.h"
|
||||
#include "toolchain/check/import.h"
|
||||
#include "toolchain/check/inst.h"
|
||||
#include "toolchain/check/literal.h"
|
||||
@@ -218,8 +220,8 @@ auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path,
|
||||
|
||||
// Look ups 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::LocId loc_id,
|
||||
SemIR::NameScopeId scope_id, SemIR::NameId name_id)
|
||||
static auto ClangLookup(Context& context, SemIR::NameScopeId scope_id,
|
||||
SemIR::NameId name_id)
|
||||
-> std::optional<clang::LookupResult> {
|
||||
std::optional<llvm::StringRef> name =
|
||||
context.names().GetAsStringIfIdentifier(name_id);
|
||||
@@ -239,16 +241,14 @@ static auto ClangLookup(Context& context, SemIR::LocId loc_id,
|
||||
sema.getPreprocessor().getIdentifierInfo(*name)),
|
||||
clang::SourceLocation()),
|
||||
clang::Sema::LookupNameKind::LookupOrdinaryName);
|
||||
// TODO: Diagnose on access and return the `AccessKind` for storage. We'll
|
||||
// probably need a dedicated `DiagnosticConsumer` because
|
||||
// `TextDiagnosticPrinter` assumes we're processing a C++ source file.
|
||||
lookup.suppressDiagnostics();
|
||||
|
||||
bool found = sema.LookupQualifiedName(
|
||||
lookup, context.name_scopes().Get(scope_id).cpp_decl_context());
|
||||
|
||||
if (lookup.isClassLookup()) {
|
||||
// TODO: To support class lookup, also return the AccessKind for storage.
|
||||
context.TODO(loc_id, "Unsupported: Lookup in Class");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -433,6 +433,108 @@ static auto ImportNamespaceDecl(Context& context,
|
||||
return result.inst_id;
|
||||
}
|
||||
|
||||
// Creates a class declaration for the given class name in the given scope.
|
||||
// Returns the `InstId` for the declaration.
|
||||
static auto BuildClassDecl(Context& context, SemIR::NameScopeId parent_scope_id,
|
||||
SemIR::NameId name_id)
|
||||
-> std::tuple<SemIR::ClassId, SemIR::InstId> {
|
||||
// Add the class declaration.
|
||||
auto class_decl =
|
||||
SemIR::ClassDecl{.type_id = SemIR::TypeType::SingletonTypeId,
|
||||
.class_id = SemIR::ClassId::None,
|
||||
.decl_block_id = SemIR::InstBlockId::None};
|
||||
// TODO: Consider setting a proper location.
|
||||
auto class_decl_id =
|
||||
AddPlaceholderInst(context, SemIR::LocIdAndInst::NoLoc(class_decl));
|
||||
|
||||
SemIR::Class class_info = {
|
||||
{.name_id = name_id,
|
||||
.parent_scope_id = parent_scope_id,
|
||||
.generic_id = SemIR::GenericId::None,
|
||||
.first_param_node_id = Parse::NodeId::None,
|
||||
.last_param_node_id = Parse::NodeId::None,
|
||||
.pattern_block_id = SemIR::InstBlockId::None,
|
||||
.implicit_param_patterns_id = SemIR::InstBlockId::None,
|
||||
.param_patterns_id = SemIR::InstBlockId::None,
|
||||
.is_extern = false,
|
||||
.extern_library_id = SemIR::LibraryNameId::None,
|
||||
.non_owning_decl_id = SemIR::InstId::None,
|
||||
.first_owning_decl_id = class_decl_id},
|
||||
{// `.self_type_id` depends on the ClassType, so is set below.
|
||||
.self_type_id = SemIR::TypeId::None,
|
||||
// TODO: Support Dynamic classes.
|
||||
// TODO: Support Final classes.
|
||||
.inheritance_kind = SemIR::Class::Base}};
|
||||
|
||||
class_decl.class_id = context.classes().Add(class_info);
|
||||
|
||||
// Write the class ID into the ClassDecl.
|
||||
ReplaceInstBeforeConstantUse(context, class_decl_id, class_decl);
|
||||
|
||||
SetClassSelfType(context, class_decl.class_id);
|
||||
|
||||
return {class_decl.class_id, class_decl_id};
|
||||
}
|
||||
|
||||
// Creates a class definition for the given class name in the given scope based
|
||||
// on the information in the given Clang declaration. Returns the `InstId` for
|
||||
// the declaration, which is assumed to be for a class definition. Returns the
|
||||
// new class id and instruction id.
|
||||
static auto BuildClassDefinition(Context& context,
|
||||
SemIR::NameScopeId parent_scope_id,
|
||||
SemIR::NameId name_id,
|
||||
clang::CXXRecordDecl* clang_decl)
|
||||
-> std::tuple<SemIR::ClassId, SemIR::InstId> {
|
||||
auto [class_id, class_decl_id] =
|
||||
BuildClassDecl(context, parent_scope_id, name_id);
|
||||
auto& class_info = context.classes().Get(class_id);
|
||||
StartClassDefinition(context, class_info, class_decl_id);
|
||||
|
||||
context.name_scopes()
|
||||
.Get(class_info.scope_id)
|
||||
.set_cpp_decl_context(clang_decl);
|
||||
|
||||
return {class_id, class_decl_id};
|
||||
}
|
||||
|
||||
// Imports a record declaration from Clang to Carbon. If successful, returns
|
||||
// the new Carbon class declaration `InstId`.
|
||||
// TODO: Change `clang_decl` to `const &` when lookup is using `clang::DeclID`
|
||||
// and we don't need to store the decl for lookup context.
|
||||
static auto ImportCXXRecordDecl(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::NameScopeId parent_scope_id,
|
||||
SemIR::NameId name_id,
|
||||
clang::CXXRecordDecl* clang_decl)
|
||||
-> SemIR::InstId {
|
||||
clang::CXXRecordDecl* clang_def = clang_decl->getDefinition();
|
||||
if (!clang_def) {
|
||||
context.TODO(loc_id,
|
||||
"Unsupported: Record declarations without a definition");
|
||||
return SemIR::ErrorInst::SingletonInstId;
|
||||
}
|
||||
|
||||
if (clang_def->isDynamicClass()) {
|
||||
context.TODO(loc_id, "Unsupported: Dynamic Class");
|
||||
return SemIR::ErrorInst::SingletonInstId;
|
||||
}
|
||||
|
||||
auto [class_id, class_def_id] =
|
||||
BuildClassDefinition(context, parent_scope_id, name_id, clang_def);
|
||||
|
||||
// The class type is now fully defined. Compute its object representation.
|
||||
ComputeClassObjectRepr(context,
|
||||
// TODO: Consider having a proper location here.
|
||||
Parse::NodeId::None, class_id,
|
||||
// TODO: Set fields.
|
||||
/*field_decls=*/{},
|
||||
// TODO: Set vtable.
|
||||
/*vtable_contents=*/{},
|
||||
// TODO: Set block.
|
||||
/*body=*/{});
|
||||
|
||||
return class_def_id;
|
||||
}
|
||||
|
||||
// Imports a declaration from Clang to Carbon. If successful, returns the
|
||||
// instruction for the new Carbon declaration.
|
||||
static auto ImportNameDecl(Context& context, SemIR::LocId loc_id,
|
||||
@@ -448,6 +550,11 @@ static auto ImportNameDecl(Context& context, SemIR::LocId loc_id,
|
||||
return ImportNamespaceDecl(context, scope_id, name_id,
|
||||
clang_namespace_decl);
|
||||
}
|
||||
if (auto* clang_record_decl =
|
||||
clang::dyn_cast<clang::CXXRecordDecl>(clang_decl)) {
|
||||
return ImportCXXRecordDecl(context, loc_id, scope_id, name_id,
|
||||
clang_record_decl);
|
||||
}
|
||||
|
||||
context.TODO(loc_id, llvm::formatv("Unsupported: Declaration type {0}",
|
||||
clang_decl->getDeclKindName())
|
||||
@@ -465,7 +572,7 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
|
||||
builder.Note(loc_id, InCppNameLookup, name_id);
|
||||
});
|
||||
|
||||
auto lookup = ClangLookup(context, loc_id, scope_id, name_id);
|
||||
auto lookup = ClangLookup(context, scope_id, name_id);
|
||||
if (!lookup) {
|
||||
return SemIR::InstId::None;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user