Basic support for indirect import of C++ classes. (#7094)

When importing Carbon code that refers to a C++ class, look for a
corresponding C++ class in the current context and import that instead.
This is a workaround for not having proper cross-file C++ import
support. For now, we only support non-templated namespace-scope class
types.

Assisted-by: Gemini via Antigravity
This commit is contained in:
Richard Smith
2026-04-24 00:36:19 +00:00
committed by GitHub
parent 3f63cf4b10
commit 56bd35e7b3
5 changed files with 470 additions and 6 deletions
+102
View File
@@ -40,6 +40,7 @@
#include "toolchain/check/cpp/location.h"
#include "toolchain/check/cpp/macros.h"
#include "toolchain/check/cpp/thunk.h"
#include "toolchain/check/cpp/type_mapping.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/check/eval.h"
#include "toolchain/check/function.h"
@@ -149,6 +150,107 @@ auto ImportCpp(Context& context,
}
}
// Given a declaration in some C++ AST which is *not* expected to be `context`,
// find the corresponding declaration in `context`, if there is one.
// TODO: Make this non-recursive, or remove it once we support importing C++
// ASTs for cross file imports.
// NOLINTNEXTLINE(misc-no-recursion)
static auto FindCorrespondingDecl(clang::ASTContext& context,
const clang::Decl* decl) -> clang::Decl* {
if (const auto* named_decl = dyn_cast<clang::NamedDecl>(decl)) {
auto* parent = dyn_cast_or_null<clang::DeclContext>(FindCorrespondingDecl(
context, cast<clang::Decl>(named_decl->getDeclContext())));
if (!parent) {
return nullptr;
}
clang::DeclarationName name;
if (auto* identifier = named_decl->getDeclName().getAsIdentifierInfo()) {
name = &context.Idents.get(identifier->getName());
} else {
// TODO: Handle more name kinds.
return nullptr;
}
auto decls = parent->lookup(name);
// TODO: If there are multiple results, try to pick the right one.
if (!decls.isSingleResult() ||
decls.front()->getKind() != named_decl->getKind()) {
// TODO: If we were looking for a non-template and found a template, try
// to form a matching template specialization.
return nullptr;
}
return decls.front();
}
if (isa<clang::TranslationUnitDecl>(decl)) {
return context.getTranslationUnitDecl();
}
return nullptr;
}
auto ImportCppDeclFromFile(Context& context, SemIR::LocId loc_id,
const SemIR::File& file,
SemIR::ClangDeclId clang_decl_id)
-> SemIR::ConstantId {
CARBON_CHECK(clang_decl_id.has_value());
auto key = file.clang_decls().Get(clang_decl_id).key;
const auto* decl = key.decl;
auto* corresponding = FindCorrespondingDecl(context.ast_context(), decl);
if (!corresponding) {
// TODO: This needs a proper diagnostic.
context.TODO(
loc_id,
"use of imported C++ declaration with no corresponding local import");
return SemIR::ErrorInst::ConstantId;
}
key.decl = corresponding;
auto imported_inst_id = ImportCppDecl(context, loc_id, key);
auto imported_const_id = context.constant_values().Get(imported_inst_id);
if (!imported_const_id.is_constant()) {
context.TODO(loc_id, "imported C++ declant is not constant");
return SemIR::ErrorInst::ConstantId;
}
return imported_const_id;
}
auto ImportCppConstantFromFile(Context& context, SemIR::LocId loc_id,
const SemIR::File& file, SemIR::InstId inst_id)
-> SemIR::ConstantId {
// TODO: We should perform cross-file imports by importing the C++ AST. For
// now we require the C++ declaration to already be imported into the
// destination file, and find the corresponding declaration there and import
// that.
if (!context.cpp_context()) {
context.TODO(
loc_id, "indirect import of C++ declaration with no direct Cpp import");
return SemIR::ErrorInst::ConstantId;
}
auto const_inst_id = file.constant_values().GetConstantInstId(inst_id);
CARBON_KIND_SWITCH(file.insts().Get(const_inst_id)) {
case CARBON_KIND(SemIR::ClassType class_type): {
const auto& class_info = file.classes().Get(class_type.class_id);
CARBON_CHECK(class_info.scope_id.has_value());
return ImportCppDeclFromFile(
context, loc_id, file,
file.name_scopes().Get(class_info.scope_id).clang_decl_context_id());
}
case CARBON_KIND(SemIR::Namespace namespace_decl): {
return ImportCppDeclFromFile(context, loc_id, file,
file.name_scopes()
.Get(namespace_decl.name_scope_id)
.clang_decl_context_id());
}
default: {
context.TODO(loc_id, "indirect import of unsupported C++ declaration");
return SemIR::ErrorInst::ConstantId;
}
}
}
// Returns the Clang `DeclContext` for the given name scope. Return the
// translation unit decl if no scope is provided.
static auto GetDeclContext(Context& context, SemIR::NameScopeId scope_id)