Skip to content

Commit ce3fa73

Browse files
author
Gabor Horvath
committed
[cxx-interop] Fix wrong field offsets with value types using inheritance
Instead of adding opaque fields for the base subobjects this patch introduces a recursive walk to add all the base fields to the generated Swift struct. rdar://126754931
1 parent a89c5a0 commit ce3fa73

File tree

3 files changed

+60
-25
lines changed

3 files changed

+60
-25
lines changed

lib/IRGen/GenStruct.cpp

+36-25
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,19 @@
2929
#include "swift/SIL/SILModule.h"
3030
#include "clang/AST/ASTContext.h"
3131
#include "clang/AST/Attr.h"
32+
#include "clang/AST/CharUnits.h"
3233
#include "clang/AST/Decl.h"
34+
#include "clang/AST/DeclCXX.h"
3335
#include "clang/AST/GlobalDecl.h"
3436
#include "clang/AST/Mangle.h"
3537
#include "clang/AST/RecordLayout.h"
3638
#include "clang/CodeGen/CodeGenABITypes.h"
3739
#include "clang/CodeGen/SwiftCallingConv.h"
3840
#include "clang/Sema/Sema.h"
41+
#include "llvm/ADT/STLExtras.h"
3942
#include "llvm/IR/DerivedTypes.h"
4043
#include "llvm/IR/Function.h"
44+
#include <iterator>
4145

4246
#include "GenDecl.h"
4347
#include "GenMeta.h"
@@ -1310,6 +1314,7 @@ class ClangRecordLowering {
13101314
SmallVector<llvm::Type *, 8> LLVMFields;
13111315
SmallVector<ClangFieldInfo, 8> FieldInfos;
13121316
Size NextOffset = Size(0);
1317+
Size SubobjectAdjustment = Size(0);
13131318
unsigned NextExplosionIndex = 0;
13141319
public:
13151320
ClangRecordLowering(IRGenModule &IGM, StructDecl *swiftDecl,
@@ -1327,8 +1332,8 @@ class ClangRecordLowering {
13271332
if (ClangDecl->isUnion()) {
13281333
collectUnionFields();
13291334
} else {
1330-
collectBases();
1331-
collectStructFields();
1335+
collectBases(ClangDecl);
1336+
collectStructFields(ClangDecl);
13321337
}
13331338
}
13341339

@@ -1359,10 +1364,9 @@ class ClangRecordLowering {
13591364
return (swiftField->getClangNode().castAsDecl() == clangField);
13601365
}
13611366

1362-
void collectBases() {
1363-
auto &layout = ClangDecl->getASTContext().getASTRecordLayout(ClangDecl);
1364-
1365-
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(ClangDecl)) {
1367+
void collectBases(const clang::RecordDecl *decl) {
1368+
auto &layout = decl->getASTContext().getASTRecordLayout(decl);
1369+
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(decl)) {
13661370
for (auto base : cxxRecord->bases()) {
13671371
if (base.isVirtual())
13681372
continue;
@@ -1375,17 +1379,24 @@ class ClangRecordLowering {
13751379
if (baseCxxRecord->isEmpty())
13761380
continue;
13771381

1378-
auto offset = layout.getBaseClassOffset(baseCxxRecord);
1379-
auto size = ClangDecl->getASTContext().getTypeSizeInChars(baseType);
1380-
addOpaqueField(Size(offset.getQuantity()), Size(size.getQuantity()));
1382+
auto baseOffset = Size(layout.getBaseClassOffset(baseCxxRecord).getQuantity());
1383+
SubobjectAdjustment += baseOffset;
1384+
collectBases(baseCxxRecord);
1385+
collectStructFields(baseCxxRecord);
1386+
SubobjectAdjustment -= baseOffset;
13811387
}
13821388
}
13831389
}
13841390

1385-
void collectStructFields() {
1386-
auto cfi = ClangDecl->field_begin(), cfe = ClangDecl->field_end();
1391+
void collectStructFields(const clang::RecordDecl *decl) {
1392+
auto cfi = decl->field_begin(), cfe = decl->field_end();
1393+
const auto& layout = ClangContext.getASTRecordLayout(decl);
13871394
auto swiftProperties = SwiftDecl->getStoredProperties();
13881395
auto sfi = swiftProperties.begin(), sfe = swiftProperties.end();
1396+
// When collecting fields from the base subobjects, we do not have corresponding swift
1397+
// stored properties.
1398+
if (decl != ClangDecl)
1399+
sfi = swiftProperties.end();
13891400

13901401
while (cfi != cfe) {
13911402
const clang::FieldDecl *clangField = *cfi++;
@@ -1395,13 +1406,13 @@ class ClangRecordLowering {
13951406
if (clangField->isBitField()) {
13961407
// Collect all of the following bitfields.
13971408
unsigned bitStart =
1398-
ClangLayout.getFieldOffset(clangField->getFieldIndex());
1409+
layout.getFieldOffset(clangField->getFieldIndex());
13991410
unsigned bitEnd = bitStart + clangField->getBitWidthValue(ClangContext);
14001411

14011412
while (cfi != cfe && (*cfi)->isBitField()) {
14021413
clangField = *cfi++;
14031414
unsigned nextStart =
1404-
ClangLayout.getFieldOffset(clangField->getFieldIndex());
1415+
layout.getFieldOffset(clangField->getFieldIndex());
14051416
assert(nextStart >= bitEnd && "laying out bit-fields out of order?");
14061417

14071418
// In a heuristic effort to reduce the number of weird-sized
@@ -1419,41 +1430,41 @@ class ClangRecordLowering {
14191430
continue;
14201431
}
14211432

1422-
VarDecl *swiftField;
1433+
VarDecl *swiftField = nullptr;
14231434
if (sfi != sfe) {
14241435
swiftField = *sfi;
14251436
if (isImportOfClangField(swiftField, clangField)) {
14261437
++sfi;
14271438
} else {
14281439
swiftField = nullptr;
14291440
}
1430-
} else {
1431-
swiftField = nullptr;
1432-
}
1441+
}
14331442

14341443
// Try to position this field. If this fails, it's because we
14351444
// didn't lay out padding correctly.
1436-
addStructField(clangField, swiftField);
1445+
addStructField(clangField, swiftField, layout);
14371446
}
14381447

14391448
assert(sfi == sfe && "more Swift fields than there were Clang fields?");
14401449

1450+
Size objectTotalStride = Size(layout.getSize().getQuantity());
14411451
// We never take advantage of tail padding, because that would prevent
14421452
// us from passing the address of the object off to C, which is a pretty
14431453
// likely scenario for imported C types.
1444-
assert(NextOffset <= TotalStride);
1445-
assert(SpareBits.size() <= TotalStride.getValueInBits());
1446-
if (NextOffset < TotalStride) {
1447-
addPaddingField(TotalStride);
1454+
assert(NextOffset <= SubobjectAdjustment + objectTotalStride);
1455+
assert(SpareBits.size() <= SubobjectAdjustment.getValueInBits() +
1456+
objectTotalStride.getValueInBits());
1457+
if (NextOffset < objectTotalStride) {
1458+
addPaddingField(objectTotalStride);
14481459
}
14491460
}
14501461

14511462
/// Place the next struct field at its appropriate offset.
14521463
void addStructField(const clang::FieldDecl *clangField,
1453-
VarDecl *swiftField) {
1454-
unsigned fieldOffset = ClangLayout.getFieldOffset(clangField->getFieldIndex());
1464+
VarDecl *swiftField, const clang::ASTRecordLayout& layout) {
1465+
unsigned fieldOffset = layout.getFieldOffset(clangField->getFieldIndex());
14551466
assert(!clangField->isBitField());
1456-
Size offset(fieldOffset / 8);
1467+
Size offset( SubobjectAdjustment.getValue() + fieldOffset / 8);
14571468

14581469
// If we have a Swift import of this type, use our lowered information.
14591470
if (swiftField) {

test/Interop/Cxx/class/inheritance/Inputs/fields.h

+18
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,21 @@ class CopyTrackedDerivedDerivedClass: public NonEmptyBase, public CopyTrackedDer
100100
public:
101101
CopyTrackedDerivedDerivedClass(int x) : CopyTrackedDerivedClass(x) {}
102102
};
103+
104+
// Types with virtual methods, make sure field offsets are right. rdar://126754931
105+
106+
struct HasOneFieldWithVirtualMethod {
107+
int a;
108+
virtual ~HasOneFieldWithVirtualMethod() {}
109+
};
110+
111+
struct HasTwoFieldsWithVirtualMethod {
112+
bool b;
113+
bool c;
114+
virtual ~HasTwoFieldsWithVirtualMethod() = default;
115+
};
116+
117+
struct InheritFromStructsWithVirtualMethod: HasOneFieldWithVirtualMethod, HasTwoFieldsWithVirtualMethod {
118+
int d;
119+
virtual ~InheritFromStructsWithVirtualMethod() = default;
120+
};

test/Interop/Cxx/class/inheritance/fields.swift

+6
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,10 @@ FieldsTestSuite.test("Get field without copying base in the getter accessor") {
111111
expectEqual(copyCounter, getCopyCounter().pointee - expectedCopyCountDiff)
112112
}
113113

114+
FieldsTestSuite.test("Structs with virtual methods") {
115+
var derived = InheritFromStructsWithVirtualMethod()
116+
derived.d = 42
117+
expectEqual(derived.d, 42)
118+
}
119+
114120
runAllTests()

0 commit comments

Comments
 (0)