Improve interop for classes with multiple inheritance. (#6130)

If there's a unique "preferred" base class, then treat that as "the"
base class for Carbon's purposes. In particular:

* If there's exactly one polymorphic base class, that's our preferred
base class.
* If there's exactly one non-empty base class, that's our perferred base
class.
* (Degenerate case) If there's exactly one base class, that's our
preferred base class.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
Richard Smith
2025-09-25 20:25:48 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent f194acbf96
commit 949ec17da2
2 changed files with 147 additions and 9 deletions
+27 -9
View File
@@ -828,6 +828,16 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id,
// TODO: Import vptr(s).
// The kind of base class we've picked so far. These are ordered in increasing
// preference order.
enum class BaseKind {
None,
Empty,
NonEmpty,
Polymorphic,
};
BaseKind base_kind = BaseKind::None;
// Import bases.
for (const auto& base : clang_def->bases()) {
CARBON_CHECK(!base.isVirtual(),
@@ -849,19 +859,27 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id,
.base_type_inst_id = base_type_inst_id,
.index = SemIR::ElementIndex(fields.size())}));
// If there's exactly one base class, treat it as a Carbon base class too.
// TODO: Improve handling for the case where the class has multiple base
// classes.
if (clang_def->getNumBases() == 1) {
auto& class_info = context.classes().Get(class_id);
CARBON_CHECK(!class_info.base_id.has_value());
class_info.base_id = base_decl_id;
}
auto* base_class = base.getType()->getAsCXXRecordDecl();
CARBON_CHECK(base_class, "Base class {0} is not a class",
base.getType().getAsString());
// If there's a unique "best" base class, treat it as a Carbon base class
// too.
// TODO: Improve handling for the case where the class has multiple base
// classes.
BaseKind kind = base_class->isPolymorphic() ? BaseKind::Polymorphic
: base_class->isEmpty() ? BaseKind::Empty
: BaseKind::NonEmpty;
auto& class_info = context.classes().Get(class_id);
if (kind > base_kind) {
// This base is better than the previous best.
class_info.base_id = base_decl_id;
base_kind = kind;
} else if (kind == base_kind) {
// Multiple base classes of this kind: no unique best.
class_info.base_id = SemIR::InstId::None;
}
auto base_offset = base.isVirtual()
? clang_layout.getVBaseClassOffset(base_class)
: clang_layout.getBaseClassOffset(base_class);