Support exporting class specifics to C++ (#7634)

This is similar to the previously-added support for accessing generic
carbon classes from C++, but with the specific defined by Carbon, rather
than being derived from template args supplied by clang in
`LoadExternalSpecializations`.

Example:
```carbon
class C(T: type) {
  var t: T;
}
alias A = C(i32);

inline Cpp '''
void F() {
  Carbon::A a;
  a.t = 123;
}
'''
```
This commit is contained in:
Nicholas Bishop
2026-08-13 23:30:24 +00:00
committed by GitHub
parent 54b7f1345a
commit 3bb245364f
5 changed files with 125 additions and 54 deletions
+91 -28
View File
@@ -168,14 +168,101 @@ auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
return decl_context;
}
static auto GetClassTypeInstId(Context& context, SemIR::ClassId class_id,
SemIR::SpecificId specific_id)
-> SemIR::TypeInstId {
auto type_id = GetClassType(context, class_id, specific_id);
return context.types().GetTypeInstId(type_id);
}
// Creates a `clang::ClassTemplateSpecializationDecl`, and registers it
// with the `ClassTemplateDecl` and `clang_decls`.
static auto CreateClassTemplateSpecializationDecl(
Context& context, clang::ClassTemplateDecl* class_template_decl,
llvm::ArrayRef<clang::TemplateArgument> template_args,
SemIR::TypeInstId class_type_inst_id)
-> clang::ClassTemplateSpecializationDecl* {
auto* class_template_specialization_decl =
clang::ClassTemplateSpecializationDecl::Create(
context.ast_context(),
class_template_decl->getTemplatedDecl()->getTagKind(),
class_template_decl->getDeclContext(),
class_template_decl->getTemplatedDecl()->getBeginLoc(),
class_template_decl->getLocation(), class_template_decl,
template_args,
/*StrictPackMatch=*/false,
/*PrevDecl=*/nullptr);
class_template_decl->AddSpecialization(class_template_specialization_decl,
/*InsertPos=*/nullptr);
class_template_specialization_decl->setHasExternalLexicalStorage();
class_template_specialization_decl->setHasExternalVisibleStorage();
// Create and store the `ClangDecl`.
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(
class_template_specialization_decl);
context.clang_decls().Add({.key = key, .inst_id = class_type_inst_id});
return class_template_specialization_decl;
}
// Exports a specific Carbon class into C++ as a template class specialization.
//
// If the specific class has already been exported, returns the existing C++
// decl. Otherwise, creates a new C++ class template specialization and
// returns it. Returns nullptr if the class could not be exported and an error
// was diagnosed.
static auto ExportClassSpecificToCpp(Context& context, SemIR::LocId loc_id,
SemIR::ClassType class_type)
-> clang::ClassTemplateSpecializationDecl* {
CARBON_CHECK(class_type.specific_id.has_value());
// Use existing export if possible.
auto class_type_inst_id =
GetClassTypeInstId(context, class_type.class_id, class_type.specific_id);
if (const auto* clang_decl =
context.clang_decls().Lookup(class_type_inst_id)) {
return cast<clang::ClassTemplateSpecializationDecl>(clang_decl->decl());
}
// Ensure the generic class is exported, and get its `ClassTemplateDecl`.
auto generic_class_type_id = GetGenericClassType(context, class_type.class_id,
SemIR::SpecificId::None);
auto generic_class_type =
context.types().GetAs<SemIR::GenericClassType>(generic_class_type_id);
auto* class_template_decl =
ExportGenericClassToCpp(context, generic_class_type);
if (!class_template_decl) {
return nullptr;
}
llvm::SmallVector<clang::TemplateArgument> template_args;
const auto specific = context.specifics().Get(class_type.specific_id);
auto specific_args = context.inst_blocks().Get(specific.args_id);
for (auto specific_arg_inst_id : specific_args) {
// TODO: also handle non-type args. Such args can't happen here yet, since
// the `ExportGenericClassToCpp` call above already checks for them.
auto cpp_type = MapToCppType(
context, context.types().GetTypeIdForTypeInstId(specific_arg_inst_id));
if (cpp_type.isNull()) {
context.TODO(loc_id, "failed to map specific type arg to C++");
return nullptr;
}
template_args.push_back(cpp_type);
}
return CreateClassTemplateSpecializationDecl(
context, class_template_decl, template_args, class_type_inst_id);
}
auto ExportClassToCpp(Context& context, SemIR::ClassType class_type)
-> clang::TagDecl* {
const auto& class_info = context.classes().Get(class_type.class_id);
SemIR::LocId loc_id(class_info.first_decl_id());
if (class_type.specific_id.has_value()) {
context.TODO(loc_id, "interop with specific class");
return nullptr;
return ExportClassSpecificToCpp(context, loc_id, class_type);
}
// If this class was produced by importing a C++ declaration or has
@@ -338,13 +425,6 @@ auto ExportGenericClassToCpp(Context& context,
return class_template_decl;
}
static auto GetClassTypeInstId(Context& context, SemIR::ClassId class_id,
SemIR::SpecificId specific_id)
-> SemIR::TypeInstId {
auto type_id = GetClassType(context, class_id, specific_id);
return context.types().GetTypeInstId(type_id);
}
auto ExportClassSpecializationToCpp(
Context& context, clang::ClassTemplateDecl* class_template_decl,
llvm::ArrayRef<clang::TemplateArgument> template_args) -> bool {
@@ -369,27 +449,10 @@ auto ExportClassSpecializationToCpp(
return false;
}
auto* class_template_specialization_decl =
clang::ClassTemplateSpecializationDecl::Create(
context.ast_context(),
class_template_decl->getTemplatedDecl()->getTagKind(),
class_template_decl->getDeclContext(),
class_template_decl->getTemplatedDecl()->getBeginLoc(),
class_template_decl->getLocation(), class_template_decl,
template_args,
/*StrictPackMatch=*/false,
/*PrevDecl=*/nullptr);
class_template_decl->AddSpecialization(class_template_specialization_decl,
/*InsertPos=*/nullptr);
class_template_specialization_decl->setHasExternalLexicalStorage();
class_template_specialization_decl->setHasExternalVisibleStorage();
// Create and store the `ClangDeclId`.
auto class_type_inst_id =
GetClassTypeInstId(context, class_decl.class_id, specific_id);
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(
class_template_specialization_decl);
context.clang_decls().Add({.key = key, .inst_id = class_type_inst_id});
CreateClassTemplateSpecializationDecl(context, class_template_decl,
template_args, class_type_inst_id);
return true;
}