Skip to content

Commit 56eef74

Browse files
committed
[interop][SwiftToCxx] moving a Swift class in C++ performs a copy
C++ does not have a consuming move, so fallback to copy instead Resolves #64702 (cherry picked from commit 16b1fe1)
1 parent d1973d6 commit 56eef74

File tree

3 files changed

+53
-5
lines changed

3 files changed

+53
-5
lines changed

lib/PrintAsClang/_SwiftCxxInteroperability.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,18 +159,28 @@ class RefCountedClass {
159159
: _opaquePointer(other._opaquePointer) {
160160
swift_retain(_opaquePointer);
161161
}
162+
SWIFT_INLINE_THUNK RefCountedClass(RefCountedClass &&other) noexcept
163+
: _opaquePointer(other._opaquePointer) {
164+
// Moving a Swift class reference is a copy
165+
// in C++. This allows C++ to avoid liveness
166+
// checks to see if the pointer is `null` or not,
167+
// as C++'s move is not consuming, unlike Swift's.
168+
swift_retain(_opaquePointer);
169+
}
162170
SWIFT_INLINE_THUNK RefCountedClass &
163171
operator=(const RefCountedClass &other) noexcept {
164172
swift_retain(other._opaquePointer);
165173
swift_release(_opaquePointer);
166174
_opaquePointer = other._opaquePointer;
167175
return *this;
168176
}
169-
#pragma clang diagnostic push
170-
#pragma clang diagnostic ignored "-Wmissing-noreturn"
171-
// FIXME: implement 'move'?
172-
SWIFT_INLINE_THUNK RefCountedClass(RefCountedClass &&) noexcept { abort(); }
173-
#pragma clang diagnostic pop
177+
SWIFT_INLINE_THUNK RefCountedClass &
178+
operator=(RefCountedClass &&other) noexcept {
179+
swift_retain(other._opaquePointer);
180+
swift_release(_opaquePointer);
181+
_opaquePointer = other._opaquePointer;
182+
return *this;
183+
}
174184

175185
protected:
176186
SWIFT_INLINE_THUNK RefCountedClass(void *_Nonnull ptr) noexcept

test/Interop/SwiftToCxx/class/swift-class-execution.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,33 @@ int main() {
102102
// CHECK-NEXT: init ClassWithIntField
103103
// CHECK-NEXT: destroy ClassWithIntField
104104
// CHECK-NEXT: ClassWithIntField: 0;
105+
// CHECK-NEXT: destroy ClassWithIntField
106+
107+
{
108+
auto x = returnClassWithIntField();
109+
assert(getRetainCount(x) == 1);
110+
ClassWithIntField x2(std::move(x));
111+
// Moving a Swift class in C++ is not consuming.
112+
assert(getRetainCount(x) == 2);
113+
}
114+
// CHECK-NEXT: init ClassWithIntField
115+
// CHECK-NEXT: destroy ClassWithIntField
116+
117+
{
118+
auto x = returnClassWithIntField();
119+
auto x2 = returnClassWithIntField();
120+
assert(getRetainCount(x) == 1);
121+
assert(getRetainCount(x2) == 1);
122+
x2 = std::move(x);
123+
// Moving a Swift class in C++ is not consuming.
124+
assert(getRetainCount(x) == 2);
125+
assert(getRetainCount(x2) == 2);
126+
takeClassWithIntField(x2);
127+
}
128+
// CHECK-NEXT: init ClassWithIntField
129+
// CHECK-NEXT: init ClassWithIntField
130+
// CHECK-NEXT: destroy ClassWithIntField
131+
// CHECK-NEXT: ClassWithIntField: 0;
105132
// CHECK-NEXT: destroy ClassWithIntField
106133
return 0;
107134
}

test/Interop/SwiftToCxx/class/swift-class-inheritance-execution.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@ int main() {
9595
// CHECK-NEXT: useDerivedClass, type=Class.DerivedDerivedClass
9696
// CHECK-NEXT: destroy DerivedDerivedClass
9797
// CHECK-NEXT: destroy DerivedClass
98+
// CHECK-NEXT: destroy BaseClass
99+
100+
{
101+
BaseClass x = returnDerivedClass();
102+
assert(getRetainCount(x) == 1);
103+
useBaseClass(x);
104+
}
105+
// CHECK-NEXT: init BaseClass
106+
// CHECK-NEXT: init DerivedClass
107+
// CHECK-NEXT: useBaseClass, type=Class.DerivedClass
108+
// CHECK-NEXT: destroy DerivedClass
98109
// CHECK-NEXT: destroy BaseClass
99110
return 0;
100111
}

0 commit comments

Comments
 (0)