Support round-tripping entities through C++ and Carbon. (#7022)

Use the same C++ -> Carbon map for both interop directions, and when
importing an entity from Carbon -> C++, check whether it was originally
a C++ entity and if so return the original.

Assisted-by: Gemini via Google Antigravity
This commit is contained in:
Richard Smith
2026-04-07 08:10:21 +00:00
committed by GitHub
parent 013a417ea5
commit cc4fd39238
4 changed files with 205 additions and 21 deletions
+60 -19
View File
@@ -8,6 +8,7 @@
#include <string>
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/FileManager.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -340,12 +341,6 @@ class CarbonExternalASTSource : public clang::ExternalASTSource {
Check::Context* context_;
clang::ASTContext* ast_context_;
// The association between clang DeclContexts and the corresponding
// SemIR::Namespaces in Carbon.
// TODO: reuse the SemIR::File::ClangDeclStore to avoid duplicates, and to
// enable roundtripping through forward and reverse interop (once we have
// syntax/support for that).
Map<clang::DeclContext*, SemIR::InstId> scope_map_;
// Has the "Carbon" C++ namespace been created yet
// (this could be replaced with `!scope_map_.empty()` if Carbon::Map supported
@@ -363,6 +358,21 @@ void CarbonExternalASTSource::StartTranslationUnit(
translation_unit.setHasExternalVisibleStorage();
}
// If the given name scope was produced by importing a C++ declaration, return
// the corresponding Clang declaration.
static auto GetClangDeclForScope(Context& context, SemIR::NameScopeId scope_id)
-> clang::NamedDecl* {
if (!scope_id.has_value()) {
return nullptr;
}
auto& scope = context.name_scopes().Get(scope_id);
if (!scope.is_cpp_scope()) {
return nullptr;
}
return dyn_cast<clang::NamedDecl>(
context.clang_decls().Get(scope.clang_decl_context_id()).key.decl);
}
auto CarbonExternalASTSource::MapInstIdToClangDecl(
clang::DeclContext& decl_context, LookupResult lookup)
-> clang::NamedDecl* {
@@ -372,10 +382,21 @@ auto CarbonExternalASTSource::MapInstIdToClangDecl(
auto target_inst = context_->insts().Get(target_constant);
CARBON_KIND_SWITCH(target_inst) {
case CARBON_KIND(SemIR::Namespace namespace_info): {
if (auto* decl =
GetClangDeclForScope(*context_, namespace_info.name_scope_id)) {
return decl;
}
auto& name_scope =
context_->name_scopes().Get(namespace_info.name_scope_id);
auto* identifier_info =
GetClangIdentifierInfo(*context_, name_scope.name_id());
if (!identifier_info) {
// TODO: Handle keyword package names like `Cpp` and `Core`. These can
// be named from C++ via an alias.
context_->TODO(SemIR::LocId(target_inst_id),
"interop with non-identifier package name");
return nullptr;
}
// TODO: Don't immediately use the decl_context - build any intermediate
// namespaces iteratively.
// Eventually add a mapping and use that/populate it/keep it up to date.
@@ -384,19 +405,26 @@ auto CarbonExternalASTSource::MapInstIdToClangDecl(
auto* namespace_decl = clang::NamespaceDecl::Create(
*ast_context_, &decl_context, false, clang::SourceLocation(),
clang::SourceLocation(), identifier_info, nullptr, false);
auto result = scope_map_.Insert(namespace_decl->getPrimaryContext(),
target_inst_id);
CARBON_CHECK(result.is_inserted(), "Inserting over an existing entry.");
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(namespace_decl);
context_->clang_decls().Add({.key = key, .inst_id = target_inst_id});
namespace_decl->setHasExternalVisibleStorage();
return namespace_decl;
}
case CARBON_KIND(SemIR::ClassType class_type): {
const auto& class_info = context_->classes().Get(class_type.class_id);
if (auto* decl = GetClangDeclForScope(*context_, class_info.scope_id)) {
return decl;
}
auto* identifier_info =
GetClangIdentifierInfo(*context_, class_info.name_id);
return clang::CXXRecordDecl::Create(
CARBON_CHECK(identifier_info, "class with non-identifier name {0}",
class_info.name_id);
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});
return record_decl;
}
case SemIR::StructValue::Kind: {
auto callee = GetCallee(context_->sem_ir(), target_constant);
@@ -406,8 +434,14 @@ auto CarbonExternalASTSource::MapInstIdToClangDecl(
}
const SemIR::Function& function =
context_->functions().Get(callee_function->function_id);
if (function.clang_decl_id.has_value()) {
return cast<clang::NamedDecl>(
context_->clang_decls().Get(function.clang_decl_id).key.decl);
}
auto* identifier_info =
GetClangIdentifierInfo(*context_, function.name_id);
CARBON_CHECK(identifier_info, "function with non-identifier name {0}",
function.name_id);
if (function.call_param_ranges.explicit_size() != 0) {
context_->TODO(target_inst_id,
@@ -480,20 +514,24 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName(
clang::SourceLocation(), &ast_context.Idents.get(carbon_namespace_name),
nullptr, false);
carbon_cpp_namespace->setHasExternalVisibleStorage();
auto result = scope_map_.Insert(carbon_cpp_namespace->getPrimaryContext(),
SemIR::Namespace::PackageInstId);
CARBON_CHECK(result.is_inserted(), "Inserting over an existing entry.");
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(carbon_cpp_namespace);
context_->clang_decls().Add(
{.key = key, .inst_id = SemIR::Namespace::PackageInstId});
SetExternalVisibleDeclsForName(decl_context, decl_name,
{carbon_cpp_namespace});
root_scope_initialized_ = true;
return true;
}
auto decl_context_inst_id =
scope_map_.Lookup(decl_context->getPrimaryContext());
// Find the Carbon declaration corresponding to this Clang declaration.
auto* decl = cast<clang::Decl>(
const_cast<clang::DeclContext*>(decl_context->getPrimaryContext()));
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(decl);
auto decl_id = context_->clang_decls().Lookup(key);
CARBON_CHECK(
decl_context_inst_id,
decl_id.has_value(),
"The DeclContext should already be associated with a Carbon InstId.");
auto decl_context_inst_id = context_->clang_decls().Get(decl_id).inst_id;
llvm::SmallVector<Check::LookupScope> lookup_scopes;
@@ -501,7 +539,7 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName(
// here - completeness should've been checked by clang before this point.
if (!AppendLookupScopesForConstant(
*context_, SemIR::LocId::None,
context_->constant_values().Get(decl_context_inst_id.value()),
context_->constant_values().Get(decl_context_inst_id),
SemIR::ConstantId::None, &lookup_scopes)) {
return false;
}
@@ -524,8 +562,11 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName(
}
// Map the found Carbon entity to a Clang NamedDecl.
// Use the key to reach the owned, mutable copy of decl_context.
auto* clang_decl = MapInstIdToClangDecl(*decl_context_inst_id.key(), result);
// TODO: Stop passing in the `DeclContext` here; the context in which we
// performed the lookup that first found the Carbon declaration should not
// affect the Clang declaration we produce.
auto* clang_decl = MapInstIdToClangDecl(
*const_cast<clang::DeclContext*>(decl_context), result);
if (!clang_decl) {
return false;
}