Skip to content

Commit 799dba1

Browse files
committed
[move-only] Suppress moveonly errors when an inout is captured by an escaping closure since we will already emit a more specific capture error.
1 parent 33c7ddd commit 799dba1

8 files changed

+171
-135
lines changed

lib/SILGen/SILGenFunction.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,11 @@ void SILGenFunction::emitCaptures(SILLocation loc,
410410
} else {
411411
// If we have a mutable binding for a 'let', such as 'self' in an
412412
// 'init' method, load it.
413+
if (Val->getType().isMoveOnly()) {
414+
Val = B.createMarkMustCheckInst(
415+
loc, Val,
416+
MarkMustCheckInst::CheckKind::AssignableButNotConsumable);
417+
}
413418
Val = emitLoad(loc, Val, tl, SGFContext(), IsNotTake).forward(*this);
414419
}
415420

lib/SILGen/SILGenProlog.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,12 @@ static void emitCaptureArguments(SILGenFunction &SGF,
645645
auto *fArg = SGF.F.begin()->createFunctionArgument(ty, VD);
646646
fArg->setClosureCapture(true);
647647
SILValue arg = SILValue(fArg);
648-
if (isInOut && (ty.isMoveOnly() && !ty.isMoveOnlyWrapped())) {
648+
649+
// If our capture is no escape and we have a noncopyable value, insert a
650+
// consumable and assignable. If we have an escaping closure, we are going
651+
// to emit an error later in SIL since it is illegal to capture an inout
652+
// value in an escaping closure.
653+
if (isInOut && ty.isPureMoveOnly() && capture.isNoEscape()) {
649654
arg = SGF.B.createMarkMustCheckInst(
650655
Loc, arg, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
651656
}

test/SILGen/moveonly.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,3 +772,25 @@ func testGlobalAssign() {
772772
varGlobal.nonTrivialStruct2 = NonTrivialStruct2()
773773
varGlobal.nonTrivialStruct2 = NonTrivialStruct2()
774774
}
775+
776+
/////////////////////////////////
777+
// MARK: Closure Capture Tests //
778+
/////////////////////////////////
779+
780+
// Make sure that we insert a mark_must_check on the capture value.
781+
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly28checkMarkMustCheckOnCaptured1xyAA2FDVn_tF : $@convention(thin) (@owned FD) -> () {
782+
// CHECK: bb0([[ARG:%.*]] : @owned
783+
// CHECK: [[BOX:%.*]] = alloc_box
784+
// CHECK: [[PROJECT:%.*]] = project_box [[BOX]]
785+
// CHECK: store [[ARG]] to [init] [[PROJECT]]
786+
//
787+
// CHECK: [[FN:%.*]] = function_ref @$s8moveonly28checkMarkMustCheckOnCaptured1xyAA2FDVn_tFyyXEfU_ : $@convention(thin) @substituted <τ_0_0> (@guaranteed FD) -> @out τ_0_0 for <()>
788+
// CHECK: [[PROJECT:%.*]] = project_box [[BOX]]
789+
// CHECK: [[MARK:%.*]] = mark_must_check [assignable_but_not_consumable] [[PROJECT]]
790+
// CHECK: [[VALUE:%.*]] = load [copy] [[MARK]]
791+
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[FN]]([[VALUE]])
792+
// CHECK: } // end sil function '$s8moveonly28checkMarkMustCheckOnCaptured1xyAA2FDVn_tF'
793+
func checkMarkMustCheckOnCaptured(x: __owned FD) {
794+
func clodger<T>(_: () -> T) {}
795+
clodger({ consumeVal(x) })
796+
}

test/SILOptimizer/moveonly_addresschecker_diagnostics.swift

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,17 +1938,12 @@ public func closureCaptureClassUseAfterConsumeError() {
19381938

19391939
public func closureCaptureClassArgUseAfterConsume(_ x2: inout Klass) {
19401940
// expected-error @-1 {{'x2' consumed but not reinitialized before end of function}}
1941-
// expected-error @-2 {{'x2' consumed in closure but not reinitialized before end of closure}}
1942-
// expected-error @-3 {{'x2' consumed more than once}}
1943-
// expected-note @-4 {{'x2' is declared 'inout'}}
1941+
// expected-note @-2 {{'x2' is declared 'inout'}}
19441942
let f = { // expected-note {{consuming use here}}
19451943
// expected-error @-1 {{escaping closure captures 'inout' parameter 'x2'}}
19461944
borrowVal(x2) // expected-note {{captured here}}
19471945
consumeVal(x2) // expected-note {{captured here}}
1948-
// expected-note @-1 {{consuming use here}}
19491946
consumeVal(x2) // expected-note {{captured here}}
1950-
// expected-note @-1 {{consuming use here}}
1951-
// expected-note @-2 {{consuming use here}}
19521947
}
19531948
f()
19541949
}
@@ -2121,28 +2116,21 @@ public func closureAndClosureCaptureClassUseAfterConsume2() {
21212116

21222117
public func closureAndClosureCaptureClassArgUseAfterConsume(_ x2: inout Klass) {
21232118
// expected-error @-1 {{'x2' consumed but not reinitialized before end of function}}
2124-
// expected-error @-2 {{'x2' consumed in closure but not reinitialized before end of closure}}
2125-
// expected-error @-3 {{'x2' consumed in closure but not reinitialized before end of closure}}
2126-
// expected-error @-4 {{'x2' consumed more than once}}
2127-
// expected-note @-5 {{'x2' is declared 'inout'}}
2128-
// expected-note @-6 {{'x2' is declared 'inout'}}
2119+
// expected-note @-2 {{'x2' is declared 'inout'}}
2120+
// expected-note @-3 {{'x2' is declared 'inout'}}
21292121
let f = { // expected-error {{escaping closure captures 'inout' parameter 'x2'}}
21302122
// expected-note @-1 {{consuming use here}}
21312123
let g = { // expected-error {{escaping closure captures 'inout' parameter 'x2'}}
2132-
// expected-note @-1 {{consuming use here}}
2133-
// expected-note @-2 {{captured indirectly by this call}}
2124+
// expected-note @-1 {{captured indirectly by this call}}
21342125
borrowVal(x2)
21352126
// expected-note @-1 {{captured here}}
21362127
// expected-note @-2 {{captured here}}
21372128
consumeVal(x2)
21382129
// expected-note @-1 {{captured here}}
21392130
// expected-note @-2 {{captured here}}
2140-
// expected-note @-3 {{consuming use here}}
21412131
consumeVal(x2)
21422132
// expected-note @-1 {{captured here}}
21432133
// expected-note @-2 {{captured here}}
2144-
// expected-note @-3 {{consuming use here}}
2145-
// expected-note @-4 {{consuming use here}}
21462134
}
21472135
g()
21482136
}

test/SILOptimizer/moveonly_nonescaping_closures.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ func c(x: __owned M) {
6060

6161
func d(x: __owned M) { // expected-error {{'x' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
6262
clodger({ consume(x) })
63-
// expected-error @-1 {{copy of noncopyable typed value. This is a compiler bug. Please file a bug with a small example of the bug}}
64-
// expected-note @-2 {{consuming use here}}
63+
// expected-note @-1 {{consuming use here}}
6564
}
6665

6766
func e(x: inout M) {

test/SILOptimizer/moveonly_objectchecker_diagnostics.swift

Lines changed: 86 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2135,6 +2135,48 @@ public func closureCaptureClassUseAfterConsume(_ x: __shared Klass) { // expecte
21352135
f()
21362136
}
21372137

2138+
public func closureCaptureClassUseAfterConsume1(_ x: __shared Klass) { // expected-error {{'x' has guaranteed ownership but was consumed}}
2139+
var x2 = x // expected-note {{consuming use here}}
2140+
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable mutable capture of an escaping closure. One can only read from it or assign over it}}
2141+
// expected-error @-2 {{'x2' was consumed but it is illegal to consume a noncopyable mutable capture of an escaping closure. One can only read from it or assign over it}}
2142+
x2 = x // expected-note {{consuming use here}}
2143+
2144+
let f = {
2145+
borrowVal(x2)
2146+
consumeVal(x2)
2147+
// expected-note @-1 {{consuming use here}}
2148+
consumeVal(x2)
2149+
// expected-note @-1 {{consuming use here}}
2150+
}
2151+
f()
2152+
}
2153+
2154+
public func closureCaptureClassUseAfterConsume2(_ x2: inout Klass) {
2155+
// expected-note @-1 {{'x2' is declared 'inout'}}
2156+
// expected-error @-2 {{'x2' consumed but not reinitialized before end of function}}
2157+
let f = { // expected-error {{escaping closure captures 'inout' parameter 'x2'}}
2158+
// expected-note @-1 {{consuming use here}}
2159+
borrowVal(x2) // expected-note {{captured here}}
2160+
consumeVal(x2) // expected-note {{captured here}}
2161+
consumeVal(x2) // expected-note {{captured here}}
2162+
}
2163+
f()
2164+
}
2165+
2166+
public func closureCaptureClassUseAfterConsume3(_ x2: inout Klass) {
2167+
// expected-error @-1 {{'x2' consumed more than once}}
2168+
// expected-error @-2 {{'x2' consumed in closure but not reinitialized before end of closure}}
2169+
func useClosure(_ x: () -> ()) {}
2170+
2171+
useClosure {
2172+
borrowVal(x2)
2173+
consumeVal(x2) // expected-note {{consuming use here}}
2174+
consumeVal(x2) // expected-note {{consuming use here}}
2175+
// expected-note @-1 {{consuming use here}}
2176+
}
2177+
}
2178+
2179+
21382180
public func closureCaptureClassUseAfterConsumeError(_ x: __shared Klass) { // expected-error {{'x' has guaranteed ownership but was consumed}}
21392181
let x2 = x
21402182
// expected-error @-1 {{'x2' consumed more than once}}
@@ -2190,28 +2232,27 @@ public func closureCaptureClassOwnedArgUseAfterConsume2(_ x2: __owned Klass) {
21902232

21912233
public func deferCaptureClassUseAfterConsume(_ x: __shared Klass) { // expected-error {{'x' has guaranteed ownership but was consumed}}
21922234
let x2 = x // expected-note {{consuming use here}}
2235+
// expected-error @-1 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
21932236
defer {
21942237
borrowVal(x2)
2195-
consumeVal(x2)
2196-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2197-
consumeVal(x2)
2198-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2238+
// TODO: Defer can only run once, so this error shouldn't occur.
2239+
consumeVal(x2) // expected-note {{consuming use here}}
2240+
consumeVal(x2) // expected-note {{consuming use here}}
21992241
}
22002242
consumeVal(x) // expected-note {{consuming use here}}
22012243
}
22022244

22032245
public func deferCaptureClassUseAfterConsume2(_ x: __shared Klass) { // expected-error {{'x' has guaranteed ownership but was consumed}}
22042246
let x2 = x
22052247
// expected-note @-1 {{consuming use here}}
2206-
defer {
2248+
// expected-error @-2 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
2249+
// expected-error @-3 {{'x2' used after consume}}
2250+
defer { // expected-note {{non-consuming use here}}
22072251
borrowVal(x2)
2208-
consumeVal(x2)
2209-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2210-
consumeVal(x2)
2211-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2252+
consumeVal(x2) // expected-note {{consuming use here}}
2253+
consumeVal(x2) // expected-note {{consuming use here}}
22122254
}
2213-
let x3 = x2
2214-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2255+
let x3 = x2 // expected-note {{consuming use here}}
22152256
let _ = x3
22162257
}
22172258

@@ -2227,37 +2268,34 @@ public func deferCaptureClassArgUseAfterConsume(_ x2: __shared Klass) {
22272268
}
22282269

22292270
public func deferCaptureClassOwnedArgUseAfterConsume(_ x2: __owned Klass) {
2271+
// expected-error @-1 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
22302272
defer {
22312273
borrowVal(x2)
2232-
consumeVal(x2)
2233-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2234-
consumeVal(x2)
2235-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2274+
consumeVal(x2) // expected-note {{consuming use here}}
2275+
consumeVal(x2) // expected-note {{consuming use here}}
22362276
}
22372277
print("foo")
22382278
}
22392279

22402280
public func deferCaptureClassOwnedArgUseAfterConsume2(_ x2: __owned Klass) {
2241-
defer {
2281+
// expected-error @-1 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
2282+
// expected-error @-2 {{'x2' used after consume}}
2283+
defer { // expected-note {{non-consuming use here}}
22422284
borrowVal(x2)
2243-
consumeVal(x2)
2244-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2245-
consumeVal(x2)
2246-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2285+
consumeVal(x2) // expected-note {{consuming use here}}
2286+
consumeVal(x2) // expected-note {{consuming use here}}
22472287
}
2248-
consumeVal(x2)
2249-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2288+
consumeVal(x2) // expected-note {{consuming use here}}
22502289
}
22512290

22522291
public func closureAndDeferCaptureClassUseAfterConsume(_ x: __shared Klass) { // expected-error {{'x' has guaranteed ownership but was consumed}}
22532292
let x2 = x // expected-note {{consuming use here}}
2293+
// expected-error @-1 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
22542294
let f = {
22552295
defer {
22562296
borrowVal(x2)
2257-
consumeVal(x2)
2258-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2259-
consumeVal(x2)
2260-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2297+
consumeVal(x2) // expected-note {{consuming use here}}
2298+
consumeVal(x2) // expected-note {{consuming use here}}
22612299
}
22622300
print("foo")
22632301
}
@@ -2266,15 +2304,14 @@ public func closureAndDeferCaptureClassUseAfterConsume(_ x: __shared Klass) { //
22662304

22672305
public func closureAndDeferCaptureClassUseAfterConsume2(_ x: __shared Klass) { // expected-error {{'x' has guaranteed ownership but was consumed}}
22682306
let x2 = x // expected-note {{consuming use here}}
2307+
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2308+
// expected-error @-2 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
22692309
let f = {
2270-
consumeVal(x2)
2271-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2310+
consumeVal(x2) // expected-note {{consuming use here}}
22722311
defer {
22732312
borrowVal(x2)
2274-
consumeVal(x2)
2275-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2276-
consumeVal(x2)
2277-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2313+
consumeVal(x2) // expected-note {{consuming use here}}
2314+
consumeVal(x2) // expected-note {{consuming use here}}
22782315
}
22792316
print("foo")
22802317
}
@@ -2284,21 +2321,20 @@ public func closureAndDeferCaptureClassUseAfterConsume2(_ x: __shared Klass) { /
22842321
public func closureAndDeferCaptureClassUseAfterConsume3(_ x: __shared Klass) { // expected-error {{'x' has guaranteed ownership but was consumed}}
22852322
let x2 = x
22862323
// expected-note @-1 {{consuming use here}}
2287-
let f = {
2288-
consumeVal(x2)
2289-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2324+
// expected-error @-2 {{'x2' consumed more than once}}
2325+
// expected-error @-3 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2326+
// expected-error @-4 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
2327+
let f = { // expected-note {{consuming use here}}
2328+
consumeVal(x2) // expected-note {{consuming use here}}
22902329
defer {
22912330
borrowVal(x2)
2292-
consumeVal(x2)
2293-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2294-
consumeVal(x2)
2295-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2331+
consumeVal(x2) // expected-note {{consuming use here}}
2332+
consumeVal(x2) // expected-note {{consuming use here}}
22962333
}
22972334
print("foo")
22982335
}
22992336
f()
2300-
consumeVal(x2)
2301-
// expected-error @-1 {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2337+
consumeVal(x2) // expected-note {{consuming use here}}
23022338
}
23032339

23042340
public func closureAndDeferCaptureClassArgUseAfterConsume(_ x2: __shared Klass) {
@@ -2316,28 +2352,31 @@ public func closureAndDeferCaptureClassArgUseAfterConsume(_ x2: __shared Klass)
23162352
}
23172353

23182354
public func closureAndDeferCaptureClassOwnedArgUseAfterConsume(_ x2: __owned Klass) {
2355+
// expected-error @-1 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
23192356
let f = {
23202357
defer {
23212358
borrowVal(x2)
2322-
consumeVal(x2) // expected-error {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2323-
consumeVal(x2) // expected-error {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2359+
consumeVal(x2) // expected-note {{consuming use here}}
2360+
consumeVal(x2) // expected-note {{consuming use here}}
23242361
}
23252362
print("foo")
23262363
}
23272364
f()
23282365
}
23292366

23302367
public func closureAndDeferCaptureClassOwnedArgUseAfterConsume2(_ x2: __owned Klass) {
2331-
let f = {
2368+
// expected-error @-1 {{'x2' consumed more than once}}
2369+
// expected-error @-2 {{'x2' consumed in closure. This is illegal since if the closure is invoked more than once the binding will be uninitialized on later invocations}}
2370+
let f = { // expected-note {{consuming use here}}
23322371
defer {
23332372
borrowVal(x2)
2334-
consumeVal(x2) // expected-error {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2335-
consumeVal(x2) // expected-error {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2373+
consumeVal(x2) // expected-note {{consuming use here}}
2374+
consumeVal(x2) // expected-note {{consuming use here}}
23362375
}
23372376
print("foo")
23382377
}
23392378
f()
2340-
consumeVal(x2) // expected-error {{'x2' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it}}
2379+
consumeVal(x2) // expected-note {{consuming use here}}
23412380
}
23422381

23432382
public func closureAndClosureCaptureClassUseAfterConsume(_ x: __shared Klass) { // expected-error {{'x' has guaranteed ownership but was consumed}}

0 commit comments

Comments
 (0)