Skip to content

Commit de56acc

Browse files
authored
Merge pull request #64397 from hyp/eng/destroy-before-assigncopytake
[interop] C++ destination record should be destroyed before being cop…
2 parents 2bc92a6 + 3e4669b commit de56acc

File tree

6 files changed

+211
-2
lines changed

6 files changed

+211
-2
lines changed

lib/IRGen/GenStruct.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,7 @@ namespace {
772772
void assignWithCopy(IRGenFunction &IGF, Address destAddr, Address srcAddr,
773773
SILType T, bool isOutlined) const override {
774774
if (auto copyConstructor = findCopyConstructor()) {
775+
destroy(IGF, destAddr, T, isOutlined);
775776
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
776777
srcAddr.getAddress(),
777778
destAddr.getAddress());
@@ -800,6 +801,7 @@ namespace {
800801
void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T,
801802
bool isOutlined) const override {
802803
if (auto copyConstructor = findCopyConstructor()) {
804+
destroy(IGF, dest, T, isOutlined);
803805
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
804806
src.getAddress(),
805807
dest.getAddress());
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#pragma once
2+
3+
struct InstanceBalanceCounter {
4+
static inline int &theCounterValue() {
5+
static int counterValue = 0;
6+
return counterValue;
7+
}
8+
static inline int getCounterValue() {
9+
return theCounterValue();
10+
}
11+
12+
__attribute__((optnone))
13+
InstanceBalanceCounter() {
14+
++theCounterValue();
15+
}
16+
__attribute__((optnone))
17+
InstanceBalanceCounter(const InstanceBalanceCounter &) {
18+
++theCounterValue();
19+
}
20+
__attribute__((optnone))
21+
InstanceBalanceCounter(InstanceBalanceCounter &&) {
22+
++theCounterValue();
23+
}
24+
__attribute__((optnone))
25+
~InstanceBalanceCounter() {
26+
--theCounterValue();
27+
}
28+
};
29+
30+
__attribute__((optnone))
31+
inline void someFunc() {}
32+
33+
struct NonTrivialCopyAssign {
34+
__attribute__((optnone))
35+
NonTrivialCopyAssign(): copyAssignCounter(0) {}
36+
__attribute__((optnone))
37+
~NonTrivialCopyAssign() {
38+
someFunc();
39+
}
40+
41+
__attribute__((optnone))
42+
NonTrivialCopyAssign &operator =(const NonTrivialCopyAssign &) {
43+
++copyAssignCounter;
44+
return *this;
45+
}
46+
47+
int copyAssignCounter;
48+
InstanceBalanceCounter instanceBalancer;
49+
};
50+
51+
struct NonTrivialMoveAssign {
52+
__attribute__((optnone))
53+
NonTrivialMoveAssign(): moveAssignCounter(0) {}
54+
__attribute__((optnone))
55+
NonTrivialMoveAssign(const NonTrivialMoveAssign &) = default;
56+
__attribute__((optnone))
57+
~NonTrivialMoveAssign() {
58+
someFunc();
59+
}
60+
61+
__attribute__((optnone))
62+
NonTrivialMoveAssign &operator =(NonTrivialMoveAssign &&) {
63+
++moveAssignCounter;
64+
return *this;
65+
}
66+
67+
int moveAssignCounter;
68+
InstanceBalanceCounter instanceBalancer;
69+
};
70+
71+
struct NonTrivialCopyAndCopyMoveAssign {
72+
__attribute__((optnone))
73+
NonTrivialCopyAndCopyMoveAssign(): assignCounter(0) {}
74+
__attribute__((optnone))
75+
NonTrivialCopyAndCopyMoveAssign(const NonTrivialCopyAndCopyMoveAssign &other) : assignCounter(other.assignCounter), instanceBalancer(other.instanceBalancer) {
76+
someFunc();
77+
}
78+
__attribute__((optnone))
79+
NonTrivialCopyAndCopyMoveAssign( NonTrivialCopyAndCopyMoveAssign &&other) : assignCounter(other.assignCounter), instanceBalancer(other.instanceBalancer) {
80+
someFunc();
81+
}
82+
__attribute__((optnone))
83+
~NonTrivialCopyAndCopyMoveAssign() {
84+
someFunc();
85+
}
86+
87+
__attribute__((optnone))
88+
NonTrivialCopyAndCopyMoveAssign &operator =(const NonTrivialCopyAndCopyMoveAssign &) {
89+
++assignCounter;
90+
return *this;
91+
}
92+
__attribute__((optnone))
93+
NonTrivialCopyAndCopyMoveAssign &operator =(NonTrivialCopyAndCopyMoveAssign &&) {
94+
++assignCounter;
95+
return *this;
96+
}
97+
98+
int assignCounter;
99+
InstanceBalanceCounter instanceBalancer;
100+
};

test/Interop/Cxx/class/Inputs/module.modulemap

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,9 @@ module ForwardDeclaredInNamespace {
135135
module ConformsTo {
136136
header "conforms-to.h"
137137
requires cplusplus
138-
}
138+
}
139+
140+
module CopyMoveAssignment {
141+
header "copy-move-assignment.h"
142+
requires cplusplus
143+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop)
2+
//
3+
// Re-test with optimizations:
4+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop -O)
5+
//
6+
// REQUIRES: executable_test
7+
8+
import StdlibUnittest
9+
import CopyMoveAssignment
10+
11+
var CxxCopyMoveAssignTestSuite = TestSuite("CxxCopyMoveAssignTestSuite")
12+
13+
@inline(never)
14+
func takeValue<T>(_ x: T) {
15+
let _ = x
16+
}
17+
18+
CxxCopyMoveAssignTestSuite.test("NonTrivialCopyAssign") {
19+
do {
20+
var instance = NonTrivialCopyAssign()
21+
expectEqual(0, instance.copyAssignCounter)
22+
let instance2 = NonTrivialCopyAssign()
23+
instance = instance2
24+
// `operator=` isn't called.
25+
expectEqual(0, instance.copyAssignCounter)
26+
takeValue(instance2)
27+
}
28+
// The number of construcors and destructors called for `NonTrivialCopyAssign` must be balanced.
29+
expectEqual(0, InstanceBalanceCounter.getCounterValue())
30+
}
31+
32+
CxxCopyMoveAssignTestSuite.test("NonTrivialMoveAssign") {
33+
do {
34+
var instance = NonTrivialMoveAssign()
35+
expectEqual(0, instance.moveAssignCounter)
36+
instance = NonTrivialMoveAssign()
37+
// `operator=` isn't called.
38+
expectEqual(0, instance.moveAssignCounter)
39+
}
40+
// The number of construcors and destructors called for `NonTrivialCopyAssign` must be balanced.
41+
expectEqual(0, InstanceBalanceCounter.getCounterValue())
42+
}
43+
44+
CxxCopyMoveAssignTestSuite.test("NonTrivialCopyAndCopyMoveAssign") {
45+
do {
46+
var instance = NonTrivialCopyAndCopyMoveAssign()
47+
expectEqual(0, instance.assignCounter)
48+
let instance2 = NonTrivialCopyAndCopyMoveAssign()
49+
instance = instance2
50+
// `operator=` isn't called.
51+
expectEqual(0, instance.assignCounter)
52+
takeValue(instance2)
53+
}
54+
// The number of construcors and destructors called for `NonTrivialCopyAndCopyMoveAssign` must be balanced.
55+
expectEqual(0, InstanceBalanceCounter.getCounterValue())
56+
}
57+
58+
runAllTests()
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs -enable-experimental-cxx-interop -emit-ir %s -Xcc -fignore-exceptions -O | %FileCheck %s
2+
3+
import CopyMoveAssignment
4+
5+
@inline(never)
6+
func takeValue<T>(_ x: T) {
7+
let _ = x
8+
}
9+
10+
@inline(never)
11+
public func copyAssign() {
12+
var instance = NonTrivialCopyAndCopyMoveAssign()
13+
let instance2 = NonTrivialCopyAndCopyMoveAssign()
14+
instance = instance2
15+
takeValue(instance2)
16+
takeValue(instance)
17+
}
18+
19+
// CHECK-LABEL: define {{.*}}copyAssign
20+
// CHECK: call {{void|\%struct.NonTrivialCopyAndCopyMoveAssign\*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignC2Ev|"\?\?0NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[COPY_INSTANCE:.*]])
21+
// CHECK: call {{void|\%struct.NonTrivialCopyAndCopyMoveAssign\*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignC2Ev|"\?\?0NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[COPY_INSTANCE2:.*]])
22+
// CHECK: call {{void|\%struct.NonTrivialCopyAndCopyMoveAssign\*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[COPY_INSTANCE]])
23+
// CHECK: call {{void|\%struct.NonTrivialCopyAndCopyMoveAssign\*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1ERKS_|_ZN31NonTrivialCopyAndCopyMoveAssignC2ERKS_|"\?\?0NonTrivialCopyAndCopyMoveAssign@@QEAA@AEBU0@@Z"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[COPY_INSTANCE]], %struct.NonTrivialCopyAndCopyMoveAssign* %[[COPY_INSTANCE2]])
24+
// CHECK: call {{.*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[COPY_INSTANCE2]])
25+
// CHECK: call {{.*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[COPY_INSTANCE]])
26+
27+
// CHECK-LABEL: define {{.*}}takeAssign
28+
// CHECK: call {{void|\%struct.NonTrivialCopyAndCopyMoveAssign\*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignC2Ev|"\?\?0NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[MOVE_INSTANCE:.*]])
29+
// CHECK: call {{.*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[MOVE_INSTANCE]])
30+
// CHECK: call {{.*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[MOVE_INSTANCE]])
31+
32+
// CHECK-LABEL: }
33+
34+
@inline(never)
35+
public func takeAssign() {
36+
var instance = NonTrivialCopyAndCopyMoveAssign()
37+
instance = NonTrivialCopyAndCopyMoveAssign()
38+
takeValue(instance)
39+
}
40+
41+
copyAssign()
42+
takeAssign()

test/Interop/Cxx/value-witness-table/witness-lifetime-operations-irgen.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ let h = Holder(holding: NonTrivial())
2828
// $sSo10NonTrivialVwca ---> assignWithCopy value witness for __C.NonTrivia
2929
// CHECK-LABEL: define linkonce_odr hidden %swift.opaque* @"$sSo10NonTrivialVwca"
3030
// CHECK-NOT: call
31+
// CHECK: call void @{{_ZN10NonTrivialD(1|2)Ev|"\?\?1NonTrivial@@QEAA@XZ"}}(%struct.NonTrivial* %{{[0-9]+}})
3132
// CHECK: call {{void|\%struct\.NonTrivial\*}} @{{_ZN10NonTrivialC(1|2)ERKS_|"\?\?0NonTrivial@@QEAA@AEBU0@@Z"}}(%struct.NonTrivial* %{{[0-9]+}}, %struct.NonTrivial* %{{[0-9]+}})
3233
// CHECK-NOT: call
3334
// CHECK: ret %swift.opaque*
@@ -44,8 +45,9 @@ let h = Holder(holding: NonTrivial())
4445
// $sSo10NonTrivialVwta ---> assignWithTake value witness for __C.NonTrivial
4546
// CHECK-LABEL: define linkonce_odr hidden %swift.opaque* @"$sSo10NonTrivialVwta"
4647
// CHECK-NOT: call
48+
// CHECK: call void @{{_ZN10NonTrivialD(1|2)Ev|"\?\?1NonTrivial@@QEAA@XZ"}}(%struct.NonTrivial* %{{[0-9]+}})
4749
// CHECK: call {{void|\%struct\.NonTrivial\*}} @{{_ZN10NonTrivialC(1|2)ERKS_|"\?\?0NonTrivial@@QEAA@AEBU0@@Z"}}(%struct.NonTrivial* %{{[0-9]+}}, %struct.NonTrivial* %{{[0-9]+}})
4850
// CHECK-NOT: call
4951
// CHECK: call void @{{_ZN10NonTrivialD(1|2)Ev|"\?\?1NonTrivial@@QEAA@XZ"}}(%struct.NonTrivial* %{{[0-9]+}})
5052
// CHECK-NOT: call
51-
// CHECK: ret %swift.opaque*
53+
// CHECK: ret %swift.opaque*

0 commit comments

Comments
 (0)