Skip to content

Commit 16b1fe1

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
1 parent a1dc63e commit 16b1fe1

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
@@ -132,6 +132,33 @@ int main() {
132132
// CHECK-NEXT: init ClassWithIntField
133133
// CHECK-NEXT: destroy ClassWithIntField
134134
// CHECK-NEXT: ClassWithIntField: 0;
135+
// CHECK-NEXT: destroy ClassWithIntField
136+
137+
{
138+
auto x = returnClassWithIntField();
139+
assert(getRetainCount(x) == 1);
140+
ClassWithIntField x2(std::move(x));
141+
// Moving a Swift class in C++ is not consuming.
142+
assert(getRetainCount(x) == 2);
143+
}
144+
// CHECK-NEXT: init ClassWithIntField
145+
// CHECK-NEXT: destroy ClassWithIntField
146+
147+
{
148+
auto x = returnClassWithIntField();
149+
auto x2 = returnClassWithIntField();
150+
assert(getRetainCount(x) == 1);
151+
assert(getRetainCount(x2) == 1);
152+
x2 = std::move(x);
153+
// Moving a Swift class in C++ is not consuming.
154+
assert(getRetainCount(x) == 2);
155+
assert(getRetainCount(x2) == 2);
156+
takeClassWithIntField(x2);
157+
}
158+
// CHECK-NEXT: init ClassWithIntField
159+
// CHECK-NEXT: init ClassWithIntField
160+
// CHECK-NEXT: destroy ClassWithIntField
161+
// CHECK-NEXT: ClassWithIntField: 0;
135162
// CHECK-NEXT: destroy ClassWithIntField
136163
return 0;
137164
}

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)