Skip to content

Commit 631a3ef

Browse files
authored
Merge pull request #66092 from gottesmm/pr-6e8d5b3eecd4d51d09bf0be496ecf5a5db426ec3
[move-only] Ban destructuring of noncopyable address only types like we do for loadable types.
2 parents 53aac94 + ec28aa4 commit 631a3ef

File tree

2 files changed

+118
-18
lines changed

2 files changed

+118
-18
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,18 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
18671867
if (!leafRange)
18681868
return false;
18691869

1870+
// Now check if we have a destructure through deinit. If we do, emit an
1871+
// error.
1872+
unsigned numDiagnostics =
1873+
moveChecker.diagnosticEmitter.getDiagnosticCount();
1874+
checkForDestructureThroughDeinit(markedValue, op, *leafRange,
1875+
diagnosticEmitter);
1876+
if (numDiagnostics != moveChecker.diagnosticEmitter.getDiagnosticCount()) {
1877+
LLVM_DEBUG(llvm::dbgs()
1878+
<< "Emitting destructure through deinit error!\n");
1879+
return true;
1880+
}
1881+
18701882
LLVM_DEBUG(llvm::dbgs() << "Pure consuming use: " << *user);
18711883
useState.takeInsts.insert({user, *leafRange});
18721884
return true;

test/SILOptimizer/moveonly_addresschecker_destructure_through_deinit_diagnostics.swift

Lines changed: 106 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,44 +14,50 @@ class MoveOnlyKlass {
1414
var value: Int = 0
1515
}
1616

17-
@_moveOnly
18-
struct KlassPair {
17+
struct KlassPair : ~Copyable {
1918
var lhs: Klass
2019
var rhs: MoveOnlyKlass
2120
}
2221

23-
@_moveOnly
24-
struct AggStruct {
22+
struct AggStruct : ~Copyable {
2523
var pair: KlassPair
2624
}
2725

28-
@_moveOnly
29-
struct KlassPair2 {
26+
struct KlassPair2 : ~Copyable {
3027
var lhs: MoveOnlyKlass
3128
var rhs: MoveOnlyKlass
3229
}
3330

34-
@_moveOnly
35-
struct AggStruct2 {
31+
struct AggStruct2 : ~Copyable {
3632
var lhs: MoveOnlyKlass
3733
var pair: KlassPair2
3834
var rhs: MoveOnlyKlass
3935
}
4036

41-
@_moveOnly
42-
struct SingleIntContainingStruct {
37+
struct SingleIntContainingStruct : ~Copyable {
4338
var value: Int = 0
4439
}
4540

41+
struct MoveOnlyPair : ~Copyable {
42+
var first = SingleIntContainingStruct()
43+
var second = SingleIntContainingStruct()
44+
}
45+
46+
protocol P {
47+
static var value: Self { get }
48+
}
49+
50+
func consume<T : P>(_ x: consuming T) {}
51+
func consume(_ x: consuming SingleIntContainingStruct) {}
4652
func consume(_ x: consuming MoveOnlyKlass) {}
53+
func consume(_ x: consuming MoveOnlyPair) {}
4754
func consume(_ x: consuming Klass) {}
4855

4956
////////////////////
50-
// Test Top Level //
57+
// MARK: Loadable //
5158
////////////////////
5259

53-
@_moveOnly
54-
struct DeinitStruct {
60+
struct DeinitStruct : ~Copyable {
5561
var first: Klass
5662
var second: (Klass, Klass)
5763
var third: KlassPair
@@ -98,12 +104,12 @@ func testConsumeNonCopyable4(_ x: consuming DeinitStruct) {
98104
consume(x.fifth) // expected-note {{consuming use here}}
99105
}
100106

101-
/////////////////
102-
// Test Fields //
103-
/////////////////
104107

105-
@_moveOnly
106-
struct StructContainDeinitStruct {
108+
///////////////////////////
109+
// MARK: Loadable Fields //
110+
///////////////////////////
111+
112+
struct StructContainDeinitStruct : ~Copyable {
107113
var first: DeinitStruct
108114
var second: (DeinitStruct, DeinitStruct)
109115
var third: Klass
@@ -153,3 +159,85 @@ func testStructContainStructContainDeinitStructConsumeNonCopyable4(_ x: consumin
153159
// expected-error @-1 {{Cannot partially consume 'x' since it contains field 'x.first' whose type 'DeinitStruct' has a user defined deinit}}
154160
consume(x.first.fifth) // expected-note {{consuming use here}}
155161
}
162+
163+
////////////////////////
164+
// MARK: Address Only //
165+
////////////////////////
166+
167+
struct AddressOnlyDeinitStruct<T : P> : ~Copyable {
168+
var copyable: T = T.value
169+
var moveOnly = SingleIntContainingStruct()
170+
var moveOnlyPair = MoveOnlyPair()
171+
172+
deinit {}
173+
// expected-note @-1 {{deinit declared here}}
174+
// expected-note @-2 {{deinit declared here}}
175+
// expected-note @-3 {{deinit declared here}}
176+
// expected-note @-4 {{deinit declared here}}
177+
// expected-note @-5 {{deinit declared here}}
178+
}
179+
180+
func consume<T : P>(_ x: consuming AddressOnlyDeinitStruct<T>) {}
181+
182+
func testAddressOnlyCanConsumeEntireType<T : P>(_ x: consuming AddressOnlyDeinitStruct<T>) {
183+
// This is ok since we are consuming a copyable value.
184+
consume(x.copyable)
185+
// This is ok since we are consuming the entire value.
186+
consume(x)
187+
}
188+
189+
func testAddressOnlyCannotPartialConsume<T : P>(_ x: consuming AddressOnlyDeinitStruct<T>) {
190+
// expected-error @-1 {{Cannot partially consume 'x' since it has a user defined deinit}}
191+
// expected-error @-2 {{Cannot partially consume 'x' since it has a user defined deinit}}
192+
consume(x.moveOnly) // expected-note {{consuming use here}}
193+
consume(x.moveOnlyPair.first) // expected-note {{consuming use here}}
194+
consume(x.copyable)
195+
}
196+
197+
struct AddressOnlyContainingDeinitStruct<T : P> : ~Copyable {
198+
var a = AddressOnlyDeinitStruct<T>()
199+
}
200+
201+
func testAddressOnlyCannotPartialConsumeEvenIfSingleElt<T : P>(_ x: consuming AddressOnlyContainingDeinitStruct<T>) {
202+
// expected-error @-1 {{Cannot partially consume 'x' since it contains field 'x.a' whose type 'AddressOnlyDeinitStruct' has a user defined deinit}}
203+
204+
// We do not error here since we can partially consume x, but not x.a
205+
consume(x.a)
206+
consume(x.a.moveOnlyPair) // expected-note {{consuming use here}}
207+
}
208+
209+
struct AddressOnlyContainingDeinitSingleField<T : P> : ~Copyable {
210+
var moveOnly = SingleIntContainingStruct()
211+
deinit {}
212+
// expected-note @-1 {{deinit declared here}}
213+
}
214+
215+
struct AddressOnlyContainingDeinitStruct3<T : P> : ~Copyable {
216+
var a = AddressOnlyContainingDeinitSingleField<T>()
217+
}
218+
219+
func consume<T : P>(_ x: consuming AddressOnlyContainingDeinitSingleField<T>) {}
220+
221+
func testAddressOnlyCannotPartialConsumeEvenIfSingleElt<T : P>(_ x: consuming AddressOnlyContainingDeinitStruct3<T>) {
222+
// expected-error @-1 {{Cannot partially consume 'x' since it contains field 'x.a' whose type 'AddressOnlyContainingDeinitSingleField' has a user defined deinit}}
223+
224+
// We do not error here since we can partially consume x, but not x.a
225+
consume(x.a)
226+
consume(x.a.moveOnly) // expected-note {{consuming use here}}
227+
}
228+
229+
230+
struct AddressOnlyContainingDeinitStructPair<T : P> : ~Copyable {
231+
var first = AddressOnlyDeinitStruct<T>()
232+
var second = AddressOnlyDeinitStruct<T>()
233+
}
234+
235+
// Make sure that if the deinit is in an intermediate element of the path that
236+
// we still handle it appropriately.
237+
func testAddressOnlyDeinitInMiddlePath<T : P>(_ x: consuming AddressOnlyContainingDeinitStructPair<T>) {
238+
// expected-error @-1 {{Cannot partially consume 'x' since it contains field 'x.first' whose type 'AddressOnlyDeinitStruct' has a user defined deinit}}
239+
// expected-error @-2 {{Cannot partially consume 'x' since it contains field 'x.first' whose type 'AddressOnlyDeinitStruct' has a user defined deinit}}
240+
consume(x.first.moveOnly) // expected-note {{consuming use here}}
241+
consume(x.first.moveOnlyPair.first) // expected-note {{consuming use here}}
242+
consume(x.first.copyable)
243+
}

0 commit comments

Comments
 (0)