mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:40:10 +01:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp;
|
||||
|
||||
// CHECK:STDERR: fail_todo_monomorphization_failure.carbon:[[@LINE+4]]:1: error: semantics TODO: `interop with specific class` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_todo_monomorphization_failure.carbon:[[@LINE+4]]:1: error: semantics TODO: `binding maps to a non-type template parameter` [SemanticsTodo]
|
||||
// CHECK:STDERR: class B(N: i32) {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
|
||||
@@ -25,14 +25,10 @@ void F() {
|
||||
}
|
||||
''';
|
||||
|
||||
// --- fail_todo_specific_alias.carbon
|
||||
// --- specific_alias.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp;
|
||||
|
||||
// CHECK:STDERR: fail_todo_specific_alias.carbon:[[@LINE+4]]:1: error: semantics TODO: `interop with specific class` [SemanticsTodo]
|
||||
// CHECK:STDERR: class C(T: type) {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
class C(T: type) {
|
||||
var t: T;
|
||||
}
|
||||
@@ -40,20 +36,36 @@ alias A = C(i32);
|
||||
|
||||
inline Cpp '''
|
||||
void F() {
|
||||
// CHECK:STDERR: fail_todo_specific_alias.carbon:[[@LINE+8]]:11: error: semantics TODO: `interop with unsupported type` [SemanticsTodo]
|
||||
Carbon::A a;
|
||||
a.t = 123;
|
||||
}
|
||||
''';
|
||||
|
||||
// --- fail_unsupported_specific_arg.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp;
|
||||
|
||||
// CHECK:STDERR: fail_unsupported_specific_arg.carbon:[[@LINE+4]]:1: error: semantics TODO: `failed to map specific type arg to C++` [SemanticsTodo]
|
||||
// CHECK:STDERR: class C(T: type) {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
class C(T: type) {
|
||||
var t: T;
|
||||
}
|
||||
alias A = C(());
|
||||
|
||||
inline Cpp '''
|
||||
void F() {
|
||||
// CHECK:STDERR: fail_unsupported_specific_arg.carbon:[[@LINE+8]]:11: error: semantics TODO: `interop with unsupported type` [SemanticsTodo]
|
||||
// CHECK:STDERR: Carbon::A a;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_specific_alias.carbon:[[@LINE+4]]:11: error: no type named 'A' in namespace 'Carbon' [CppInteropParseError]
|
||||
// CHECK:STDERR: fail_unsupported_specific_arg.carbon:[[@LINE+4]]:11: error: no type named 'A' in namespace 'Carbon' [CppInteropParseError]
|
||||
// CHECK:STDERR: 23 | Carbon::A a;
|
||||
// CHECK:STDERR: | ~~~~~~~~^
|
||||
// CHECK:STDERR:
|
||||
Carbon::A a;
|
||||
// CHECK:STDERR: fail_todo_specific_alias.carbon:[[@LINE+4]]:3: error: use of undeclared identifier 'c' [CppInteropParseError]
|
||||
// CHECK:STDERR: 28 | c.t = 123;
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR:
|
||||
c.t = 123;
|
||||
a.t = 123;
|
||||
}
|
||||
''';
|
||||
|
||||
|
||||
@@ -90,11 +90,11 @@ void G() {
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp;
|
||||
|
||||
// CHECK:STDERR: fail_todo_enclosing_generic.carbon:[[@LINE+4]]:1: error: semantics TODO: `interop with specific class` [SemanticsTodo]
|
||||
// CHECK:STDERR: class A(T: type) {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
class A(T: type) {
|
||||
// CHECK:STDERR: fail_todo_enclosing_generic.carbon:[[@LINE+4]]:3: error: semantics TODO: `non-class non-namespace name scope` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F[U: type](x: T, y: U);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F[U: type](x: T, y: U);
|
||||
}
|
||||
alias B = A(i32);
|
||||
@@ -104,15 +104,11 @@ void f() {
|
||||
// TODO: this currently fails because `B` can't be imported, but the intent
|
||||
// is to test a generic function within an enclosing generic scope.
|
||||
//
|
||||
// CHECK:STDERR: fail_todo_enclosing_generic.carbon:[[@LINE+8]]:11: error: semantics TODO: `interop with unsupported type` [SemanticsTodo]
|
||||
// CHECK:STDERR: Carbon::B b;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_enclosing_generic.carbon:[[@LINE+4]]:11: error: no type named 'B' in namespace 'Carbon' [CppInteropParseError]
|
||||
// CHECK:STDERR: 26 | Carbon::B b;
|
||||
// CHECK:STDERR: | ~~~~~~~~^
|
||||
// CHECK:STDERR:
|
||||
Carbon::B b;
|
||||
// CHECK:STDERR: fail_todo_enclosing_generic.carbon:[[@LINE+4]]:5: error: no member named 'F' in 'Carbon::A<int>' [CppInteropParseError]
|
||||
// CHECK:STDERR: 23 | b.F(1, 2);
|
||||
// CHECK:STDERR: | ~ ^
|
||||
// CHECK:STDERR:
|
||||
b.F(1, 2);
|
||||
}
|
||||
''';
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ fn F(x: i512) {
|
||||
// CHECK:STDERR: Cpp.foo(x);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: min_prelude/parts/int.carbon:10:1: error: semantics TODO: `interop with specific class` [SemanticsTodo]
|
||||
// CHECK:STDERR: min_prelude/parts/int.carbon:10:1: error: semantics TODO: `interop with non-identifier package name` [SemanticsTodo]
|
||||
// CHECK:STDERR: class Int(N: IntLiteral) {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
|
||||
Reference in New Issue
Block a user