Skip to content

Commit 8b7b134

Browse files
committed
[interop] C++ destination record should be destroyed before being copied into during assignWithCopy / assignWithTake
1 parent f824ca1 commit 8b7b134

File tree

5 files changed

+212
-1
lines changed

5 files changed

+212
-1
lines changed

lib/IRGen/GenStruct.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,7 @@ namespace {
769769
void assignWithCopy(IRGenFunction &IGF, Address destAddr, Address srcAddr,
770770
SILType T, bool isOutlined) const override {
771771
if (auto copyConstructor = findCopyConstructor()) {
772+
destroy(IGF, destAddr, T, isOutlined);
772773
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
773774
srcAddr.getAddress(),
774775
destAddr.getAddress());
@@ -797,6 +798,7 @@ namespace {
797798
void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T,
798799
bool isOutlined) const override {
799800
if (auto copyConstructor = findCopyConstructor()) {
801+
destroy(IGF, dest, T, isOutlined);
800802
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
801803
src.getAddress(),
802804
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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
public func copyAssign() {
11+
var instance = NonTrivialCopyAndCopyMoveAssign()
12+
let instance2 = NonTrivialCopyAndCopyMoveAssign()
13+
instance = instance2
14+
takeValue(instance2)
15+
takeValue(instance)
16+
}
17+
18+
// CHECK-LABEL: define {{.*}}copyAssign
19+
// CHECK: call %struct.NonTrivialCopyAndCopyMoveAssign* @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignC2Ev|"\?\?0NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#COPY_INSTANCE:]])
20+
// CHECK: call %struct.NonTrivialCopyAndCopyMoveAssign* @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignC2Ev|"\?\?0NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#COPY_INSTANCE2:]])
21+
// CHECK: call %struct.NonTrivialCopyAndCopyMoveAssign* @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#COPY_INSTANCE]])
22+
// CHECK: call %struct.NonTrivialCopyAndCopyMoveAssign* @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1ERKS_|_ZN31NonTrivialCopyAndCopyMoveAssignC2ERKS_|"\?\?4NonTrivialCopyAndCopyMoveAssign@@QEAAAEAU0@AEBU0@@Z"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#COPY_INSTANCE]], %struct.NonTrivialCopyAndCopyMoveAssign* %[[#COPY_INSTANCE2]])
23+
// CHECK: call swiftcc
24+
// CHECK: call {{.*}}{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#COPY_INSTANCE2]])
25+
// CHECK: call swiftcc
26+
// CHECK: call {{.*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#COPY_INSTANCE]])
27+
// CHECK-NOT: NonTrivialCopyAndCopyMoveAssign
28+
29+
// CHECK-LABEL: define {{.*}}takeAssign
30+
// CHECK: call %struct.NonTrivialCopyAndCopyMoveAssign* @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignC2Ev|"\?\?0NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#MOVE_INSTANCE:]])
31+
// CHECK: call {{.*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#MOVE_INSTANCE]])
32+
// CHECK: call {{.*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignC1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignC2Ev|"\?\?0NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#MOVE_INSTANCE:]])
33+
// CHECK: call swiftcc
34+
// CHECK: call {{.*}} @{{_ZN31NonTrivialCopyAndCopyMoveAssignD1Ev|_ZN31NonTrivialCopyAndCopyMoveAssignD2Ev|"\?\?1NonTrivialCopyAndCopyMoveAssign@@QEAA@XZ"}}(%struct.NonTrivialCopyAndCopyMoveAssign* %[[#MOVE_INSTANCE]])
35+
// CHECK-NOT: NonTrivialCopyAndCopyMoveAssign
36+
37+
// CHECK-LABEL: }
38+
39+
public func takeAssign() {
40+
var instance = NonTrivialCopyAndCopyMoveAssign()
41+
instance = NonTrivialCopyAndCopyMoveAssign()
42+
takeValue(instance)
43+
}
44+
45+
copyAssign()
46+
takeAssign()

0 commit comments

Comments
 (0)