Skip to content

Commit 82ba1a4

Browse files
authored
Support for importing C++ enum types. (#5978)
We import C++ enum types as Carbon class types as adapters for the corresponding builtin integer type, and we import enumerator constants as integer constants of that class type. No operators are supported on such values for now; eventually once we start asking Clang to implement operators on C++-owned types, these types should be handled in the same way. However, they can be converted to the corresponding integer type with `as` via adapter conversion, and integer builtin functions can operate on them.
1 parent 6ba900a commit 82ba1a4

File tree

15 files changed

+1326
-53
lines changed

15 files changed

+1326
-53
lines changed

toolchain/base/int.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ class IntStore {
255255
return AddLarge(value);
256256
}
257257

258+
// Returns the ID corresponding to this integer value.
259+
auto Add(llvm::APSInt value) -> IntId {
260+
return value.isSigned() ? AddSigned(value) : AddUnsigned(value);
261+
}
262+
258263
// Returns the ID corresponding to this signed integer value, storing an
259264
// `APInt` if necessary to represent it.
260265
auto AddSigned(llvm::APInt value) -> IntId {

toolchain/check/import_cpp.cpp

Lines changed: 133 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -656,8 +656,11 @@ static auto LookupClangDeclInstId(const Context& context, clang::Decl* decl)
656656
// Returns the parent of the given declaration. Skips declaration types we
657657
// ignore.
658658
static auto GetParentDecl(clang::Decl* clang_decl) -> clang::Decl* {
659-
return cast<clang::Decl>(
660-
clang_decl->getDeclContext()->getNonTransparentContext());
659+
auto* parent_dc = clang_decl->getDeclContext();
660+
while (!parent_dc->isLookupContext()) {
661+
parent_dc = parent_dc->getParent();
662+
}
663+
return cast<clang::Decl>(parent_dc);
661664
}
662665

663666
// Returns the given declaration's parent scope. Assumes the parent declaration
@@ -756,10 +759,10 @@ static auto BuildClassDecl(Context& context,
756759
return {class_decl.class_id, context.types().GetAsTypeInstId(class_decl_id)};
757760
}
758761

759-
// Imports a record declaration from Clang to Carbon. If successful, returns
760-
// the new Carbon class declaration `InstId`.
761-
static auto ImportCXXRecordDecl(Context& context,
762-
clang::CXXRecordDecl* clang_decl)
762+
// Imports a tag declaration from Clang to Carbon. This covers classes (which
763+
// includes structs and unions) as well as enums. If successful, returns the new
764+
// Carbon class declaration `InstId`.
765+
static auto ImportTagDecl(Context& context, clang::TagDecl* clang_decl)
763766
-> SemIR::InstId {
764767
auto import_ir_inst_id =
765768
AddImportIRInst(context.sem_ir(), clang_decl->getLocation());
@@ -973,8 +976,8 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id,
973976
.layout_id = context.custom_layouts().Add(layout)});
974977
}
975978

976-
// Creates a class definition based on the information in the given Clang
977-
// declaration, which is assumed to be for a class definition.
979+
// Creates a Carbon class definition based on the information in the given Clang
980+
// class declaration, which is assumed to be for a class definition.
978981
static auto BuildClassDefinition(Context& context,
979982
SemIR::ImportIRInstId import_ir_inst_id,
980983
SemIR::ClassId class_id,
@@ -999,13 +1002,70 @@ static auto BuildClassDefinition(Context& context,
9991002
class_info.body_block_id = context.inst_block_stack().Pop();
10001003
}
10011004

1002-
auto ImportCppClassDefinition(Context& context, SemIR::LocId loc_id,
1003-
SemIR::ClassId class_id,
1004-
SemIR::ClangDeclId clang_decl_id) -> bool {
1005+
// Computes and returns the Carbon type to use as the object representation of
1006+
// the given C++ enum type. This is a builtin int type matching the enum's
1007+
// representation.
1008+
static auto ImportEnumObjectRepresentation(
1009+
Context& context, SemIR::ImportIRInstId import_ir_inst_id,
1010+
clang::EnumDecl* enum_decl) -> SemIR::TypeInstId {
1011+
auto int_type = enum_decl->getIntegerType();
1012+
CARBON_CHECK(!int_type.isNull(), "incomplete enum type {0}",
1013+
enum_decl->getNameAsString());
1014+
1015+
auto int_kind = int_type->isSignedIntegerType() ? SemIR::IntKind::Signed
1016+
: SemIR::IntKind::Unsigned;
1017+
auto bit_width_id = GetOrAddInst<SemIR::IntValue>(
1018+
context, import_ir_inst_id,
1019+
{.type_id = GetSingletonType(context, SemIR::IntLiteralType::TypeInstId),
1020+
.int_id = context.ints().AddUnsigned(
1021+
llvm::APInt(64, context.ast_context().getIntWidth(int_type)))});
1022+
return context.types().GetAsTypeInstId(
1023+
GetOrAddInst(context, SemIR::LocIdAndInst::NoLoc(SemIR::IntType{
1024+
.type_id = SemIR::TypeType::TypeId,
1025+
.int_kind = int_kind,
1026+
.bit_width_id = bit_width_id})));
1027+
}
1028+
1029+
// Creates a Carbon class definition based on the information in the given Clang
1030+
// enum declaration.
1031+
static auto BuildEnumDefinition(Context& context,
1032+
SemIR::ImportIRInstId import_ir_inst_id,
1033+
SemIR::ClassId class_id,
1034+
SemIR::TypeInstId class_inst_id,
1035+
clang::EnumDecl* enum_decl) -> void {
1036+
auto& class_info = context.classes().Get(class_id);
1037+
CARBON_CHECK(!class_info.has_definition_started());
1038+
class_info.definition_id = class_inst_id;
1039+
1040+
context.inst_block_stack().Push();
1041+
1042+
// Don't allow inheritance from C++ enums, to match the behavior in C++.
1043+
class_info.inheritance_kind = SemIR::Class::Final;
1044+
1045+
// Compute the enum type's object representation. An enum is an adapter for
1046+
// the corresponding builtin integer type.
1047+
auto object_repr_id =
1048+
ImportEnumObjectRepresentation(context, import_ir_inst_id, enum_decl);
1049+
class_info.adapt_id = AddInst(
1050+
context, SemIR::LocIdAndInst::UncheckedLoc(
1051+
import_ir_inst_id,
1052+
SemIR::AdaptDecl{.adapted_type_inst_id = object_repr_id}));
1053+
class_info.complete_type_witness_id = AddInst<SemIR::CompleteTypeWitness>(
1054+
context, import_ir_inst_id,
1055+
{.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId),
1056+
.object_repr_type_inst_id = object_repr_id});
1057+
1058+
class_info.body_block_id = context.inst_block_stack().Pop();
1059+
}
1060+
1061+
auto ImportClassDefinitionForClangDecl(Context& context, SemIR::LocId loc_id,
1062+
SemIR::ClassId class_id,
1063+
SemIR::ClangDeclId clang_decl_id)
1064+
-> bool {
10051065
clang::ASTUnit* ast = context.sem_ir().clang_ast_unit();
10061066
CARBON_CHECK(ast);
10071067

1008-
auto* clang_decl = cast<clang::CXXRecordDecl>(
1068+
auto* clang_decl = cast<clang::TagDecl>(
10091069
context.sem_ir().clang_decls().Get(clang_decl_id).decl);
10101070
auto class_inst_id = context.types().GetAsTypeInstId(
10111071
context.classes().Get(class_id).first_owning_decl_id);
@@ -1017,7 +1077,7 @@ auto ImportCppClassDefinition(Context& context, SemIR::LocId loc_id,
10171077
Diagnostics::AnnotationScope annotate_diagnostics(
10181078
&context.emitter(), [&](auto& builder) {
10191079
CARBON_DIAGNOSTIC(InCppTypeCompletion, Note,
1020-
"while completing C++ class type {0}", SemIR::TypeId);
1080+
"while completing C++ type {0}", SemIR::TypeId);
10211081
builder.Note(loc_id, InCppTypeCompletion,
10221082
context.classes().Get(class_id).self_type_id);
10231083
});
@@ -1026,33 +1086,62 @@ auto ImportCppClassDefinition(Context& context, SemIR::LocId loc_id,
10261086
// instantiation if necessary.
10271087
clang::DiagnosticErrorTrap trap(ast->getDiagnostics());
10281088
if (!ast->getSema().isCompleteType(
1029-
loc, context.ast_context().getRecordType(clang_decl))) {
1089+
loc, context.ast_context().getTypeDeclType(clang_decl))) {
10301090
// Type is incomplete. Nothing more to do, but tell the caller if we
10311091
// produced an error.
10321092
return !trap.hasErrorOccurred();
10331093
}
10341094

1035-
clang::CXXRecordDecl* clang_def = clang_decl->getDefinition();
1036-
CARBON_CHECK(clang_def, "Complete type has no definition");
1037-
1038-
if (clang_def->getNumVBases()) {
1039-
// TODO: Handle virtual bases. We don't actually know where they go in the
1040-
// layout. We may also want to use a different size in the layout for
1041-
// `partial C`, excluding the virtual base. It's also not entirely safe to
1042-
// just skip over the virtual base, as the type we would construct would
1043-
// have a misleading size. For now, treat a C++ class with vbases as
1044-
// incomplete in Carbon.
1045-
context.TODO(loc_id, "class with virtual bases");
1046-
return false;
1047-
}
1048-
10491095
auto import_ir_inst_id =
10501096
context.insts().GetCanonicalLocId(class_inst_id).import_ir_inst_id();
1051-
BuildClassDefinition(context, import_ir_inst_id, class_id, class_inst_id,
1052-
clang_def);
1097+
1098+
if (auto* class_decl = dyn_cast<clang::CXXRecordDecl>(clang_decl)) {
1099+
auto* class_def = class_decl->getDefinition();
1100+
CARBON_CHECK(class_def, "Complete type has no definition");
1101+
1102+
if (class_def->getNumVBases()) {
1103+
// TODO: Handle virtual bases. We don't actually know where they go in the
1104+
// layout. We may also want to use a different size in the layout for
1105+
// `partial C`, excluding the virtual base. It's also not entirely safe to
1106+
// just skip over the virtual base, as the type we would construct would
1107+
// have a misleading size. For now, treat a C++ class with vbases as
1108+
// incomplete in Carbon.
1109+
context.TODO(loc_id, "class with virtual bases");
1110+
return false;
1111+
}
1112+
1113+
BuildClassDefinition(context, import_ir_inst_id, class_id, class_inst_id,
1114+
class_def);
1115+
} else if (auto* enum_decl = dyn_cast<clang::EnumDecl>(clang_decl)) {
1116+
BuildEnumDefinition(context, import_ir_inst_id, class_id, class_inst_id,
1117+
enum_decl);
1118+
}
10531119
return true;
10541120
}
10551121

1122+
// Imports an enumerator declaration from Clang to Carbon.
1123+
static auto ImportEnumConstantDecl(Context& context,
1124+
clang::EnumConstantDecl* enumerator_decl)
1125+
-> SemIR::InstId {
1126+
CARBON_CHECK(!IsClangDeclImported(context, enumerator_decl));
1127+
1128+
// Find the enclosing enum type.
1129+
auto type_inst_id = LookupClangDeclInstId(
1130+
context, cast<clang::EnumDecl>(enumerator_decl->getDeclContext()));
1131+
auto type_id = context.types().GetTypeIdForTypeInstId(type_inst_id);
1132+
1133+
// Build a corresponding IntValue.
1134+
auto int_id = context.ints().Add(enumerator_decl->getInitVal());
1135+
auto loc_id =
1136+
AddImportIRInst(context.sem_ir(), enumerator_decl->getLocation());
1137+
auto inst_id = AddInstInNoBlock<SemIR::IntValue>(
1138+
context, loc_id, {.type_id = type_id, .int_id = int_id});
1139+
context.imports().push_back(inst_id);
1140+
context.sem_ir().clang_decls().Add(
1141+
{.decl = enumerator_decl->getCanonicalDecl(), .inst_id = inst_id});
1142+
return inst_id;
1143+
}
1144+
10561145
// Mark the given `Decl` as failed in `clang_decls`.
10571146
static auto MarkFailedDecl(Context& context, clang::Decl* clang_decl) {
10581147
context.sem_ir().clang_decls().Add({.decl = clang_decl->getCanonicalDecl(),
@@ -1114,18 +1203,16 @@ static auto MapBuiltinType(Context& context, SemIR::LocId loc_id,
11141203
return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
11151204
}
11161205

1117-
// Maps a C++ record type to a Carbon type.
1118-
static auto MapRecordType(Context& context, const clang::RecordType& type)
1206+
// Maps a C++ tag type (class, struct, union, enum) to a Carbon type.
1207+
static auto MapTagType(Context& context, const clang::TagType& type)
11191208
-> TypeExpr {
1120-
auto* record_decl = dyn_cast<clang::CXXRecordDecl>(type.getDecl());
1121-
if (!record_decl) {
1122-
return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
1123-
}
1209+
auto* tag_decl = type.getDecl();
1210+
CARBON_CHECK(tag_decl);
11241211

11251212
// Check if the declaration is already mapped.
1126-
SemIR::InstId record_inst_id = LookupClangDeclInstId(context, record_decl);
1213+
SemIR::InstId record_inst_id = LookupClangDeclInstId(context, tag_decl);
11271214
if (!record_inst_id.has_value()) {
1128-
record_inst_id = ImportCXXRecordDecl(context, record_decl);
1215+
record_inst_id = ImportTagDecl(context, tag_decl);
11291216
}
11301217
SemIR::TypeInstId record_type_inst_id =
11311218
context.types().GetAsTypeInstId(record_inst_id);
@@ -1143,8 +1230,8 @@ static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id,
11431230
return MapBuiltinType(context, loc_id, type, *builtin_type);
11441231
}
11451232

1146-
if (const auto* record_type = type->getAs<clang::RecordType>()) {
1147-
return MapRecordType(context, *record_type);
1233+
if (const auto* tag_type = type->getAs<clang::TagType>()) {
1234+
return MapTagType(context, *tag_type);
11481235
}
11491236

11501237
CARBON_CHECK(!type.hasQualifiers() && !type->isPointerType(),
@@ -1640,8 +1727,8 @@ static auto AddDependentUnimportedTypeDecls(const Context& context,
16401727
}
16411728
}
16421729

1643-
if (const auto* record_type = type->getAs<clang::RecordType>()) {
1644-
AddDependentDecl(context, record_type->getDecl(), worklist);
1730+
if (const auto* tag_type = type->getAs<clang::TagType>()) {
1731+
AddDependentDecl(context, tag_type->getDecl(), worklist);
16451732
}
16461733
}
16471734

@@ -1666,7 +1753,7 @@ static auto AddDependentUnimportedDecls(const Context& context,
16661753
AddDependentUnimportedFunctionDecls(context, *clang_function_decl,
16671754
worklist);
16681755
} else if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
1669-
if (!isa<clang::RecordDecl>(clang_decl)) {
1756+
if (!isa<clang::TagDecl>(clang_decl)) {
16701757
AddDependentUnimportedTypeDecls(
16711758
context, type_decl->getASTContext().getTypeDeclType(type_decl),
16721759
worklist);
@@ -1713,6 +1800,9 @@ static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
17131800
"Unsupported: field declaration has unhandled type or kind");
17141801
return SemIR::ErrorInst::InstId;
17151802
}
1803+
if (auto* enum_const_decl = dyn_cast<clang::EnumConstantDecl>(clang_decl)) {
1804+
return ImportEnumConstantDecl(context, enum_const_decl);
1805+
}
17161806

17171807
context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
17181808
llvm::formatv("Unsupported: Declaration type {0}",

toolchain/check/import_cpp.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
3131
SemIR::NameScopeId scope_id, SemIR::NameId name_id)
3232
-> SemIR::ScopeLookupResult;
3333

34-
// Given a class declaration that was imported from C++, attempt to import a
35-
// corresponding class definition. Returns true if nothing went wrong (whether
36-
// or not a definition could be imported), false if a diagnostic was produced.
37-
auto ImportCppClassDefinition(Context& context, SemIR::LocId loc_id,
38-
SemIR::ClassId class_id,
39-
SemIR::ClangDeclId clang_decl_id) -> bool;
34+
// Given a Carbon class declaration that was imported from some kind of C++
35+
// declaration, such as a class or enum, attempt to import a corresponding class
36+
// definition. Returns true if nothing went wrong (whether or not a definition
37+
// could be imported), false if a diagnostic was produced.
38+
auto ImportClassDefinitionForClangDecl(Context& context, SemIR::LocId loc_id,
39+
SemIR::ClassId class_id,
40+
SemIR::ClangDeclId clang_decl_id)
41+
-> bool;
4042

4143
} // namespace Carbon::Check
4244

toolchain/check/testdata/interop/cpp/class/base.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ fn Convert(p: Cpp.B*) -> Cpp.A* {
181181
// CHECK:STDERR: fail_todo_use_virtual_inheritance.carbon:[[@LINE+14]]:3: error: semantics TODO: `class with virtual bases` [SemanticsTodo]
182182
// CHECK:STDERR: return p;
183183
// CHECK:STDERR: ^~~~~~~~~
184-
// CHECK:STDERR: fail_todo_use_virtual_inheritance.carbon:[[@LINE+11]]:3: note: while completing C++ class type `Cpp.B` [InCppTypeCompletion]
184+
// CHECK:STDERR: fail_todo_use_virtual_inheritance.carbon:[[@LINE+11]]:3: note: while completing C++ type `Cpp.B` [InCppTypeCompletion]
185185
// CHECK:STDERR: return p;
186186
// CHECK:STDERR: ^~~~~~~~~
187187
// CHECK:STDERR:

toolchain/check/testdata/interop/cpp/class/template.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ import Cpp library "instantiation_error.h";
7373
//@dump-sem-ir-begin
7474
var x: Cpp.XY.r#type = 0;
7575

76-
// CHECK:STDERR: fail_use_instantiation_error.carbon:[[@LINE+4]]:8: note: while completing C++ class type `Cpp.X` [InCppTypeCompletion]
76+
// CHECK:STDERR: fail_use_instantiation_error.carbon:[[@LINE+4]]:8: note: while completing C++ type `Cpp.X` [InCppTypeCompletion]
7777
// CHECK:STDERR: var y: Cpp.Xint.r#type = 0;
7878
// CHECK:STDERR: ^~~~~~~~~~~~~
7979
// CHECK:STDERR:

0 commit comments

Comments
 (0)