Limited support for indirect import of template specializations. (#7121)

When a class template specialization is indirectly imported, map the
template arguments into the importing File and find the corresponding
local class template specialization. This is a short-term fix:
eventually we should import the C++ AST from the imported file into the
C++ AST for the current file, but we're not ready to do that yet.

So far we only support very simple template arguments: just classes and
builtin types. Unfortunately we can't just map the C++ template
arguments to Carbon types, then import the Carbon types, then map them
back, because mapping from C++ template arguments to Carbon types would
require a `Check::Context` for the imported code, which we don't have.
As this is only a temporary workaround, directly mapping from one C++
AST to another will do for now.

Assisted-by: Gemini via Antigravity
This commit is contained in:
Richard Smith
2026-04-28 02:36:44 +00:00
committed by GitHub
parent 0e308e0739
commit 73adc479e3
2 changed files with 197 additions and 19 deletions
+92 -7
View File
@@ -150,22 +150,62 @@ auto ImportCpp(Context& context,
}
}
// NOLINTNEXTLINE(misc-no-recursion)
static auto FindCorrespondingType(Context& context, SemIR::LocId loc_id,
clang::QualType type) -> clang::QualType;
// Given a class template specialization in some C++ AST which is *not* expected
// to be `context`, find the corresponding declaration in `context`, if there is
// one.
// NOLINTNEXTLINE(misc-no-recursion)
static auto FindCorrespondingTemplateSpecialization(
Context& context, SemIR::LocId loc_id,
const clang::ClassTemplateSpecializationDecl* source_spec,
clang::ClassTemplateDecl* target_template) -> clang::Decl* {
const auto& args = source_spec->getTemplateArgs();
auto loc = GetCppLocation(context, loc_id);
clang::TemplateArgumentListInfo arg_list(loc, loc);
for (unsigned i = 0; i < args.size(); ++i) {
const auto& arg = args[i];
if (arg.getKind() == clang::TemplateArgument::Type) {
auto type = FindCorrespondingType(context, loc_id, arg.getAsType());
if (type.isNull()) {
return nullptr;
}
arg_list.addArgument(clang::TemplateArgumentLoc(
clang::TemplateArgument(type),
context.ast_context().getTrivialTypeSourceInfo(type, loc)));
} else {
return nullptr;
}
}
clang::TemplateName template_name(target_template);
auto clang_type = context.clang_sema().CheckTemplateIdType(
clang::ElaboratedTypeKeyword::None, template_name, loc, arg_list,
/*Scope=*/nullptr, /*ForNestedNameSpecifier=*/false);
if (!clang_type.isNull()) {
return clang_type->getAsCXXRecordDecl();
}
return nullptr;
}
// 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,
static auto FindCorrespondingDecl(Context& context, SemIR::LocId loc_id,
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())));
context, loc_id, 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());
name = &context.ast_context().Idents.get(identifier->getName());
} else {
// TODO: Handle more name kinds.
return nullptr;
@@ -174,20 +214,65 @@ static auto FindCorrespondingDecl(clang::ASTContext& context,
// 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.
if (const auto* source_spec =
dyn_cast<clang::ClassTemplateSpecializationDecl>(named_decl)) {
if (auto* target_template =
dyn_cast<clang::ClassTemplateDecl>(decls.front())) {
if (auto* result = FindCorrespondingTemplateSpecialization(
context, loc_id, source_spec, target_template)) {
return result;
}
}
}
return nullptr;
}
return decls.front();
}
if (isa<clang::TranslationUnitDecl>(decl)) {
return context.getTranslationUnitDecl();
return context.ast_context().getTranslationUnitDecl();
}
return nullptr;
}
// Given a type in some C++ AST which is *not* expected to be `context`,
// find the corresponding type in `context`, if there is one.
// NOLINTNEXTLINE(misc-no-recursion)
static auto FindCorrespondingType(Context& context, SemIR::LocId loc_id,
clang::QualType type) -> clang::QualType {
if (type.isNull()) {
return clang::QualType();
}
if (const auto* builtin = type->getAs<clang::BuiltinType>()) {
switch (builtin->getKind()) {
#define BUILTIN_TYPE(Id, SingletonId) \
case clang::BuiltinType::Id: \
return context.ast_context().SingletonId;
#include "clang/AST/BuiltinTypes.def"
#undef BUILTIN_TYPE
default:
return clang::QualType();
}
}
if (const auto* record = type->getAs<clang::RecordType>()) {
const auto* decl = record->getDecl();
auto* corresponding_decl = FindCorrespondingDecl(context, loc_id, decl);
if (!corresponding_decl) {
return clang::QualType();
}
if (const auto* tag_decl = dyn_cast<clang::TagDecl>(corresponding_decl)) {
return context.ast_context().getTypeDeclType(
cast<clang::TypeDecl>(tag_decl));
}
return clang::QualType();
}
return clang::QualType();
}
auto ImportCppDeclFromFile(Context& context, SemIR::LocId loc_id,
const SemIR::File& file,
SemIR::ClangDeclId clang_decl_id)
@@ -195,7 +280,7 @@ auto ImportCppDeclFromFile(Context& context, SemIR::LocId loc_id,
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);
auto* corresponding = FindCorrespondingDecl(context, loc_id, decl);
if (!corresponding) {
// TODO: This needs a proper diagnostic.
context.TODO(