Allow interop with classes with virtual base classes. (#6413)

For now, treat such classes as being final, since we can't correctly
derive from them.

This removes the last category of C++ class that we are entirely unable
to interop with, and is a prerequisite for interop with C++ iostreams
(which have a virtual base class).
This commit is contained in:
Richard Smith
2025-11-21 16:45:38 +00:00
committed by GitHub
parent 8866e39085
commit 13fbe3c1f3
4 changed files with 329 additions and 75 deletions
+20 -13
View File
@@ -742,6 +742,13 @@ static auto GetInheritanceKind(clang::CXXRecordDecl* class_def)
return SemIR::Class::Final;
}
if (class_def->getNumVBases()) {
// TODO: We treat classes with virtual bases as final for now. We use the
// layout of the class including its virtual bases as its Carbon type
// layout, so we wouldn't behave correctly if we derived from it.
return SemIR::Class::Final;
}
if (class_def->isAbstract()) {
// If the class has any abstract members, it's abstract.
return SemIR::Class::Abstract;
@@ -804,8 +811,15 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id,
// Import bases.
for (const auto& base : clang_def->bases()) {
CARBON_CHECK(!base.isVirtual(),
"Should not import definition for class with a virtual base");
if (base.isVirtual()) {
// If the base is virtual, skip it from the layout. We don't know where it
// will actually appear within the complete object layout, as a pointer to
// this class might point to a derived type that puts the vbase in a
// different place.
// TODO: Track that the virtual base existed. Support derived-to-vbase
// conversions by generating a clang AST fragment.
continue;
}
auto [base_type_inst_id, base_type_id] =
ImportTypeAndDependencies(context, import_ir_inst_id, base.getType());
@@ -844,6 +858,10 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id,
class_info.base_id = SemIR::InstId::None;
}
// TODO: If the base class has virtual bases, the size of the type that we
// add to the layout here will be the full size of the class (including
// virtual bases), whereas the size actually occupied by this base class is
// only the nvsize (excluding virtual bases).
auto base_offset = base.isVirtual()
? clang_layout.getVBaseClassOffset(base_class)
: clang_layout.getBaseClassOffset(base_class);
@@ -2447,17 +2465,6 @@ auto ImportClassDefinitionForClangDecl(Context& context, SemIR::LocId loc_id,
auto* class_def = class_decl->getDefinition();
CARBON_CHECK(class_def, "Complete type has no definition");
if (class_def->getNumVBases()) {
// TODO: Handle virtual bases. We don't actually know where they go in the
// layout. We may also want to use a different size in the layout for
// `partial C`, excluding the virtual base. It's also not entirely safe to
// just skip over the virtual base, as the type we would construct would
// have a misleading size. For now, treat a C++ class with vbases as
// incomplete in Carbon.
context.TODO(loc_id, "class with virtual bases");
return false;
}
BuildClassDefinition(context, import_ir_inst_id, class_id, class_inst_id,
class_def);
} else if (auto* enum_decl = dyn_cast<clang::EnumDecl>(clang_decl)) {