Superficial support for exporting complete class types to C++. (#7029)

We don't yet populate the bases or fields, so the class types show up as
empty classes in C++ for now. But we do allow calls to static member
functions.
This commit is contained in:
Richard Smith
2026-04-08 19:29:25 +00:00
committed by GitHub
parent 9f1a0c816c
commit b74e0d1260
7 changed files with 417 additions and 37 deletions
+73 -2
View File
@@ -32,6 +32,7 @@
#include "toolchain/check/cpp/import.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/type_completion.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/diagnostics/emitter.h"
#include "toolchain/diagnostics/format_providers.h"
@@ -324,14 +325,15 @@ class CarbonExternalASTSource : public clang::ExternalASTSource {
clang::ASTContext* ast_context)
: context_(context), ast_context_(ast_context) {}
auto StartTranslationUnit(clang::ASTConsumer* consumer) -> void override;
// Look up decls for `decl_name` inside `decl_context`, adding the decls to
// `decl_context`. Returns true if any decls were added.
auto FindExternalVisibleDeclsByName(
const clang::DeclContext* decl_context, clang::DeclarationName decl_name,
const clang::DeclContext* original_decl_context) -> bool override;
// See clang::ExternalASTSource.
auto StartTranslationUnit(clang::ASTConsumer* consumer) -> void override;
auto CompleteType(clang::TagDecl* tag_decl) -> void override;
private:
// Map a Carbon entity to a Clang NamedDecl. Returns null if the entity cannot
@@ -339,6 +341,28 @@ class CarbonExternalASTSource : public clang::ExternalASTSource {
auto MapInstIdToClangDecl(clang::DeclContext& decl_context,
LookupResult lookup) -> clang::NamedDecl*;
// Get a current best-effort location for the current position within C++
// processing.
auto GetCurrentCppLocId() -> SemIR::LocId {
auto* cpp_context = context_->cpp_context();
CARBON_CHECK(cpp_context);
// Use the current token location when parsing.
auto clang_source_loc = cpp_context->parser().getCurToken().getLocation();
if (auto& code_synthesis_contexts =
cpp_context->sema().CodeSynthesisContexts;
!code_synthesis_contexts.empty()) {
// Use the current point of instantiation during template instantiation.
clang_source_loc = code_synthesis_contexts.back().PointOfInstantiation;
}
// TODO: Refactor with AddImportIRInst in import.cpp.
SemIR::ClangSourceLocId clang_source_loc_id =
context_->sem_ir().clang_source_locs().Add(clang_source_loc);
return context_->import_ir_insts().Add(
SemIR::ImportIRInst(clang_source_loc_id));
}
Check::Context* context_;
clang::ASTContext* ast_context_;
@@ -419,11 +443,19 @@ auto CarbonExternalASTSource::MapInstIdToClangDecl(
GetClangIdentifierInfo(*context_, class_info.name_id);
CARBON_CHECK(identifier_info, "class with non-identifier name {0}",
class_info.name_id);
// TODO: Check whether we've already mapped this class and if so, return
// the prior mapping.
auto* record_decl = clang::CXXRecordDecl::Create(
*ast_context_, clang::TagTypeKind::Class, &decl_context,
clang::SourceLocation(), clang::SourceLocation(), identifier_info);
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(record_decl);
context_->clang_decls().Add({.key = key, .inst_id = target_inst_id});
record_decl->setHasExternalLexicalStorage();
record_decl->setHasExternalVisibleStorage();
if (isa<clang::CXXRecordDecl>(decl_context)) {
// TODO: Map Carbon access to C++ access.
record_decl->setAccess(clang::AS_public);
}
return record_decl;
}
case SemIR::StructValue::Kind: {
@@ -536,6 +568,45 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName(
return true;
}
auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
auto* class_decl = dyn_cast<clang::CXXRecordDecl>(tag_decl);
if (!class_decl) {
// TODO: If we start producing clang EnumTypes, we may have to handle them
// here too.
return;
}
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(tag_decl->getFirstDecl());
auto clang_decl_id = context_->clang_decls().Lookup(key);
if (!clang_decl_id.has_value()) {
return;
}
auto inst_id = context_->clang_decls().Get(clang_decl_id).inst_id;
auto const_id = context_->constant_values().Get(inst_id);
if (!const_id.has_value()) {
return;
}
auto class_type =
context_->constant_values().TryGetInstAs<SemIR::ClassType>(const_id);
if (!class_type) {
return;
}
auto class_type_id = context_->types().GetTypeIdForTypeConstantId(const_id);
auto context_fn = [](DiagnosticContextBuilder& /*builder*/) -> void {};
if (!RequireCompleteType(*context_, class_type_id, GetCurrentCppLocId(),
context_fn)) {
return;
}
class_decl->startDefinition();
// TODO: Import base class and fields, plus any special member functions that
// affect class properties.
class_decl->completeDefinition();
}
// Parses a sequence of top-level declarations and forms a corresponding
// representation in the Clang AST. Unlike clang::ParseAST, does not finish the
// translation unit when EOF is reached.