Skip to content

Commit 1823a1e

Browse files
author
Gabor Horvath
committed
[cxx-interop] Fix over-releasing foreign reference members of PODs
Large POD 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 1823a1e

File tree

3 files changed

+35
-5
lines changed

3 files changed

+35
-5
lines changed

lib/IRGen/GenStruct.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,10 @@ class ClangRecordLowering {
13191319
Size NextOffset = Size(0);
13201320
Size SubobjectAdjustment = Size(0);
13211321
unsigned NextExplosionIndex = 0;
1322+
// PODs containing fields to reference types are not real PODs, they cannot be
1323+
// copied using memcpy as we need to do the proper retain operations.
1324+
bool hasReferenceField = false;
1325+
13221326
public:
13231327
ClangRecordLowering(IRGenModule &IGM, StructDecl *swiftDecl,
13241328
const clang::RecordDecl *clangDecl,
@@ -1359,10 +1363,11 @@ class ClangRecordLowering {
13591363
return LoadableClangRecordTypeInfo::create(
13601364
FieldInfos, NextExplosionIndex, llvmType, TotalStride,
13611365
std::move(SpareBits), TotalAlignment,
1362-
(SwiftDecl && SwiftDecl->getValueTypeDestructor())
1363-
? IsNotTriviallyDestroyable : IsTriviallyDestroyable,
1364-
(SwiftDecl && !SwiftDecl->canBeCopyable())
1365-
? IsNotCopyable : IsCopyable,
1366+
(SwiftDecl &&
1367+
(SwiftDecl->getValueTypeDestructor() || hasReferenceField))
1368+
? IsNotTriviallyDestroyable
1369+
: IsTriviallyDestroyable,
1370+
(SwiftDecl && !SwiftDecl->canBeCopyable()) ? IsNotCopyable : IsCopyable,
13661371
ClangDecl);
13671372
}
13681373

@@ -1488,6 +1493,10 @@ class ClangRecordLowering {
14881493
SwiftType.getFieldType(swiftField, IGM.getSILModule(),
14891494
IGM.getMaximalTypeExpansionContext())));
14901495
addField(swiftField, offset, fieldTI, isZeroSized);
1496+
if (swiftField->getInterfaceType()
1497+
->lookThroughSingleOptionalType()
1498+
->isAnyClassReferenceType())
1499+
hasReferenceField = true;
14911500
return;
14921501
}
14931502

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,13 @@ inline void releaseSharedFRT(SharedFRT *_Nonnull x) {
4141
if (x->_refCount == 0)
4242
delete x;
4343
}
44+
45+
typedef struct LargePOD {
46+
void const *a;
47+
void const *b;
48+
unsigned long c;
49+
unsigned d;
50+
SharedFRT *e;
51+
} LargePOD;
52+
53+
inline LargePOD getLargePod() { return {0, 0, 0, 0, new SharedFRT()}; }

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,15 @@ 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 takesLargePod(_ x: LargePOD) {
33+
var a = x
34+
}
35+
36+
takesLargePod(getLargePod())
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

0 commit comments

Comments
 (0)