[cxx-interop] Fix IRGen for C++ types that use tail padding of their bases

In C++, a field of a derived class might be placed into the tail padding of a base class. Swift was not handling this case correctly, causing an asserts-disabled compiler to run out of RAM, and an asserts-enabled compiler to fail with an assertion.

Fixes this IRGen assertion:
```
Assertion failed: (offset >= NextOffset && "adding fields out of order"), function addField, file GenStruct.cpp, line 1509.
```

rdar://138764929
This commit is contained in:
Egor Zhdan
2024-11-04 13:44:43 +00:00
parent 108ad6f115
commit d3e43bbe7b
3 changed files with 31 additions and 5 deletions

View File

@@ -1423,7 +1423,8 @@ private:
auto sfi = swiftProperties.begin(), sfe = swiftProperties.end();
// When collecting fields from the base subobjects, we do not have corresponding swift
// stored properties.
if (decl != ClangDecl)
bool isBaseSubobject = decl != ClangDecl;
if (isBaseSubobject)
sfi = swiftProperties.end();
while (cfi != cfe) {
@@ -1475,10 +1476,14 @@ private:
assert(sfi == sfe && "more Swift fields than there were Clang fields?");
Size objectTotalStride = Size(layout.getSize().getQuantity());
// We never take advantage of tail padding, because that would prevent
// us from passing the address of the object off to C, which is a pretty
// likely scenario for imported C types.
auto objectSize = isBaseSubobject ? layout.getDataSize() : layout.getSize();
Size objectTotalStride = Size(objectSize.getQuantity());
// Unless this is a base subobject of a C++ type, we do not take advantage
// of tail padding, because that would prevent us from passing the address
// of the object off to C, which is a pretty likely scenario for imported C
// types.
// In C++, fields of a derived class might get placed into tail padding of a
// base class, in which case we should not add extra padding here.
assert(NextOffset <= SubobjectAdjustment + objectTotalStride);
assert(SpareBits.size() <= SubobjectAdjustment.getValueInBits() +
objectTotalStride.getValueInBits());

View File

@@ -118,3 +118,17 @@ struct InheritFromStructsWithVirtualMethod: HasOneFieldWithVirtualMethod, HasTwo
int d;
virtual ~InheritFromStructsWithVirtualMethod() = default;
};
// MARK: Types that pack their fields into tail padding of a base class.
struct BaseAlign8 {
long long field8 = 123;
}; // sizeof=8, dsize=8, align=8
struct DerivedHasTailPadding : public BaseAlign8 {
int field4 = 456;
}; // sizeof=16, dsize=12, align=8
struct DerivedUsesBaseTailPadding : public DerivedHasTailPadding {
short field2 = 789;
}; // sizeof=16, dsize=14, align=8

View File

@@ -117,4 +117,11 @@ FieldsTestSuite.test("Structs with virtual methods") {
expectEqual(derived.d, 42)
}
FieldsTestSuite.test("Field in tail padding of base class") {
let usesBaseTailPadding = DerivedUsesBaseTailPadding()
expectEqual(usesBaseTailPadding.field2, 789)
expectEqual(usesBaseTailPadding.field4, 456)
expectEqual(usesBaseTailPadding.field8, 123)
}
runAllTests()