Skip to content

Commit ac26e28

Browse files
authored
Merge pull request #84321 from Xazax-hun/unbalanced-refcount-in-pods
[cxx-interop] Fix over-releasing reference members of trival C++ types
2 parents 21e34e3 + e4e5423 commit ac26e28

File tree

3 files changed

+74
-11
lines changed

3 files changed

+74
-11
lines changed

lib/IRGen/GenStruct.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/Decl.h"
2323
#include "swift/AST/IRGenOptions.h"
2424
#include "swift/AST/Pattern.h"
25+
#include "swift/AST/ReferenceCounting.h"
2526
#include "swift/AST/SemanticAttrs.h"
2627
#include "swift/AST/SubstitutionMap.h"
2728
#include "swift/AST/Types.h"
@@ -368,6 +369,7 @@ namespace {
368369
: public StructTypeInfoBase<LoadableClangRecordTypeInfo, LoadableTypeInfo,
369370
ClangFieldInfo> {
370371
const clang::RecordDecl *ClangDecl;
372+
bool HasReferenceField;
371373

372374
public:
373375
LoadableClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
@@ -376,14 +378,14 @@ namespace {
376378
Alignment align,
377379
IsTriviallyDestroyable_t isTriviallyDestroyable,
378380
IsCopyable_t isCopyable,
379-
const clang::RecordDecl *clangDecl)
381+
const clang::RecordDecl *clangDecl,
382+
bool hasReferenceField)
380383
: StructTypeInfoBase(StructTypeInfoKind::LoadableClangRecordTypeInfo,
381384
fields, explosionSize, FieldsAreABIAccessible,
382385
storageType, size, std::move(spareBits), align,
383-
isTriviallyDestroyable,
384-
isCopyable,
385-
IsFixedSize, IsABIAccessible),
386-
ClangDecl(clangDecl) {}
386+
isTriviallyDestroyable, isCopyable, IsFixedSize,
387+
IsABIAccessible),
388+
ClangDecl(clangDecl), HasReferenceField(hasReferenceField) {}
387389

388390
TypeLayoutEntry
389391
*buildTypeLayoutEntry(IRGenModule &IGM,
@@ -447,6 +449,7 @@ namespace {
447449
const ClangFieldInfo &field) const {
448450
llvm_unreachable("non-fixed field in Clang type?");
449451
}
452+
bool hasReferenceField() const { return HasReferenceField; }
450453
};
451454

452455
class AddressOnlyPointerAuthRecordTypeInfo final
@@ -1319,6 +1322,11 @@ class ClangRecordLowering {
13191322
Size NextOffset = Size(0);
13201323
Size SubobjectAdjustment = Size(0);
13211324
unsigned NextExplosionIndex = 0;
1325+
// Types that are trivial in C++ but are containing fields to reference types
1326+
// are not trivial in Swift, they cannot be copied using memcpy as we need to
1327+
// do the proper retain operations.
1328+
bool hasReferenceField = false;
1329+
13221330
public:
13231331
ClangRecordLowering(IRGenModule &IGM, StructDecl *swiftDecl,
13241332
const clang::RecordDecl *clangDecl,
@@ -1359,11 +1367,12 @@ class ClangRecordLowering {
13591367
return LoadableClangRecordTypeInfo::create(
13601368
FieldInfos, NextExplosionIndex, llvmType, TotalStride,
13611369
std::move(SpareBits), TotalAlignment,
1362-
(SwiftDecl && SwiftDecl->getValueTypeDestructor())
1363-
? IsNotTriviallyDestroyable : IsTriviallyDestroyable,
1364-
(SwiftDecl && !SwiftDecl->canBeCopyable())
1365-
? IsNotCopyable : IsCopyable,
1366-
ClangDecl);
1370+
(SwiftDecl &&
1371+
(SwiftDecl->getValueTypeDestructor() || hasReferenceField))
1372+
? IsNotTriviallyDestroyable
1373+
: IsTriviallyDestroyable,
1374+
(SwiftDecl && !SwiftDecl->canBeCopyable()) ? IsNotCopyable : IsCopyable,
1375+
ClangDecl, hasReferenceField);
13671376
}
13681377

13691378
private:
@@ -1488,6 +1497,17 @@ class ClangRecordLowering {
14881497
SwiftType.getFieldType(swiftField, IGM.getSILModule(),
14891498
IGM.getMaximalTypeExpansionContext())));
14901499
addField(swiftField, offset, fieldTI, isZeroSized);
1500+
auto fieldTy =
1501+
swiftField->getInterfaceType()->lookThroughSingleOptionalType();
1502+
if (fieldTy->isAnyClassReferenceType() &&
1503+
fieldTy->getReferenceCounting() != ReferenceCounting::None)
1504+
hasReferenceField = true;
1505+
else if (auto structDecl = fieldTy->getStructOrBoundGenericStruct();
1506+
structDecl && structDecl->hasClangNode() &&
1507+
getStructTypeInfoKind(fieldTI) ==
1508+
StructTypeInfoKind::LoadableClangRecordTypeInfo)
1509+
if (fieldTI.as<LoadableClangRecordTypeInfo>().hasReferenceField())
1510+
hasReferenceField = true;
14911511
return;
14921512
}
14931513

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)