Skip to content

Commit 75ec1fd

Browse files
author
Gabor Horvath
committed
[cxx-interop] Fix over-releasing reference members of trival C++ types
Large trivial types were copied via memcpy instead of doing a field-wise copy. This is incorrect for types with reference fields where we also need to bump the corresponding refcounts. rdar://160315343
1 parent a5c6156 commit 75ec1fd

File tree

3 files changed

+70
-11
lines changed

3 files changed

+70
-11
lines changed

lib/IRGen/GenStruct.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ namespace {
368368
: public StructTypeInfoBase<LoadableClangRecordTypeInfo, LoadableTypeInfo,
369369
ClangFieldInfo> {
370370
const clang::RecordDecl *ClangDecl;
371+
bool HasReferenceField;
371372

372373
public:
373374
LoadableClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
@@ -376,14 +377,14 @@ namespace {
376377
Alignment align,
377378
IsTriviallyDestroyable_t isTriviallyDestroyable,
378379
IsCopyable_t isCopyable,
379-
const clang::RecordDecl *clangDecl)
380+
const clang::RecordDecl *clangDecl,
381+
bool hasReferenceField)
380382
: StructTypeInfoBase(StructTypeInfoKind::LoadableClangRecordTypeInfo,
381383
fields, explosionSize, FieldsAreABIAccessible,
382384
storageType, size, std::move(spareBits), align,
383-
isTriviallyDestroyable,
384-
isCopyable,
385-
IsFixedSize, IsABIAccessible),
386-
ClangDecl(clangDecl) {}
385+
isTriviallyDestroyable, isCopyable, IsFixedSize,
386+
IsABIAccessible),
387+
ClangDecl(clangDecl), HasReferenceField(hasReferenceField) {}
387388

388389
TypeLayoutEntry
389390
*buildTypeLayoutEntry(IRGenModule &IGM,
@@ -447,6 +448,7 @@ namespace {
447448
const ClangFieldInfo &field) const {
448449
llvm_unreachable("non-fixed field in Clang type?");
449450
}
451+
bool hasReferenceField() const { return HasReferenceField; }
450452
};
451453

452454
class AddressOnlyPointerAuthRecordTypeInfo final
@@ -1319,6 +1321,11 @@ class ClangRecordLowering {
13191321
Size NextOffset = Size(0);
13201322
Size SubobjectAdjustment = Size(0);
13211323
unsigned NextExplosionIndex = 0;
1324+
// Types that are trivial in C++ but are containing fields to reference types
1325+
// are not trivial in Swift, they cannot be copied using memcpy as we need to
1326+
// do the proper retain operations.
1327+
bool hasReferenceField = false;
1328+
13221329
public:
13231330
ClangRecordLowering(IRGenModule &IGM, StructDecl *swiftDecl,
13241331
const clang::RecordDecl *clangDecl,
@@ -1359,11 +1366,12 @@ class ClangRecordLowering {
13591366
return LoadableClangRecordTypeInfo::create(
13601367
FieldInfos, NextExplosionIndex, llvmType, TotalStride,
13611368
std::move(SpareBits), TotalAlignment,
1362-
(SwiftDecl && SwiftDecl->getValueTypeDestructor())
1363-
? IsNotTriviallyDestroyable : IsTriviallyDestroyable,
1364-
(SwiftDecl && !SwiftDecl->canBeCopyable())
1365-
? IsNotCopyable : IsCopyable,
1366-
ClangDecl);
1369+
(SwiftDecl &&
1370+
(SwiftDecl->getValueTypeDestructor() || hasReferenceField))
1371+
? IsNotTriviallyDestroyable
1372+
: IsTriviallyDestroyable,
1373+
(SwiftDecl && !SwiftDecl->canBeCopyable()) ? IsNotCopyable : IsCopyable,
1374+
ClangDecl, hasReferenceField);
13671375
}
13681376

13691377
private:
@@ -1488,6 +1496,14 @@ class ClangRecordLowering {
14881496
SwiftType.getFieldType(swiftField, IGM.getSILModule(),
14891497
IGM.getMaximalTypeExpansionContext())));
14901498
addField(swiftField, offset, fieldTI, isZeroSized);
1499+
if (swiftField->getInterfaceType()
1500+
->lookThroughSingleOptionalType()
1501+
->isAnyClassReferenceType())
1502+
hasReferenceField = true;
1503+
else if (const auto *recordTI =
1504+
dyn_cast<LoadableClangRecordTypeInfo>(&fieldTI))
1505+
if (recordTI->hasReferenceField())
1506+
hasReferenceField = true;
14911507
return;
14921508
}
14931509

test/Interop/Cxx/foreign-reference/Inputs/logging-frts.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,24 @@ inline void releaseSharedFRT(SharedFRT *_Nonnull x) {
4141
if (x->_refCount == 0)
4242
delete x;
4343
}
44+
45+
struct LargeStructWithRefCountedField {
46+
void const *a;
47+
void const *b;
48+
unsigned long c;
49+
unsigned d;
50+
SharedFRT *e;
51+
};
52+
53+
struct LargeStructWithRefCountedFieldNested {
54+
int a;
55+
LargeStructWithRefCountedField b;
56+
};
57+
58+
inline LargeStructWithRefCountedField getStruct() {
59+
return {0, 0, 0, 0, new SharedFRT()};
60+
}
61+
62+
inline LargeStructWithRefCountedFieldNested getNestedStruct() {
63+
return {0, {0, 0, 0, 0, new SharedFRT()}};
64+
}

test/Interop/Cxx/foreign-reference/frts-as-fields.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,26 @@ go()
2727
// CHECK-NEXT: RefCount: 2, message: retain
2828
// CHECK-NEXT: RefCount: 1, message: release
2929
// CHECK-NEXT: RefCount: 0, message: release
30-
// CHECK-NEXT: RefCount: 0, message: Dtor
30+
// CHECK-NEXT: RefCount: 0, message: Dtor
31+
32+
func takesLargeStructWithRefCountedField(_ x: LargeStructWithRefCountedField) {
33+
var a = x
34+
}
35+
36+
takesLargeStructWithRefCountedField(getStruct())
37+
// CHECK: RefCount: 1, message: Ctor
38+
// CHECK-NEXT: RefCount: 2, message: retain
39+
// CHECK-NEXT: RefCount: 1, message: release
40+
// CHECK-NEXT: RefCount: 0, message: release
41+
// CHECK-NEXT: RefCount: 0, message: Dtor
42+
43+
func takesLargeStructWithRefCountedFieldNested(_ x: LargeStructWithRefCountedFieldNested) {
44+
var a = x
45+
}
46+
47+
takesLargeStructWithRefCountedFieldNested(getNestedStruct())
48+
// CHECK: RefCount: 1, message: Ctor
49+
// CHECK-NEXT: RefCount: 2, message: retain
50+
// CHECK-NEXT: RefCount: 1, message: release
51+
// CHECK-NEXT: RefCount: 0, message: release
52+
// CHECK-NEXT: RefCount: 0, message: Dtor

0 commit comments

Comments
 (0)