Skip to content

Commit 68884d6

Browse files
committed
[move-only] Add some coroutine tests around closure capture semantics.
1 parent 07979f0 commit 68884d6

File tree

2 files changed

+211
-1
lines changed

2 files changed

+211
-1
lines changed

test/SILGen/moveonly_escaping_closure.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,3 +1319,71 @@ func testConsumingEscapeClosureCaptureOwned(_ f: consuming @escaping () -> (),
13191319
}
13201320
f()
13211321
}
1322+
1323+
//////////////////////////////////
1324+
// MARK: Coroutine Closure Test //
1325+
//////////////////////////////////
1326+
1327+
struct ClosureHolder {
1328+
var f: () -> () = {}
1329+
var fCoroutine: () -> () {
1330+
_read {
1331+
yield f
1332+
}
1333+
_modify {
1334+
yield &f
1335+
}
1336+
}
1337+
}
1338+
1339+
func closureCoroutineAssignmentLetBorrowingArgument(_ e: borrowing Empty) { // expected-error {{'e' has guaranteed ownership but was consumed due to being captured by a closure}}
1340+
let f: () -> () = { // expected-note {{capture here}}
1341+
_ = e
1342+
}
1343+
var c = ClosureHolder()
1344+
c.fCoroutine = f
1345+
}
1346+
1347+
func closureCoroutineAssignmentLetConsumingArgument(_ e: __owned Empty) {
1348+
let f: () -> () = {
1349+
_ = e
1350+
}
1351+
var c = ClosureHolder()
1352+
c.fCoroutine = f
1353+
}
1354+
1355+
func closureCoroutineAssignmentVarConsumingArgument(_ e: consuming Empty) {
1356+
let f: () -> () = {
1357+
_ = e // expected-error {{'e' 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}}
1358+
}
1359+
var c = ClosureHolder()
1360+
c.fCoroutine = f
1361+
}
1362+
1363+
func closureCoroutineAssignmentLetBinding() {
1364+
let e = Empty()
1365+
let f: () -> () = {
1366+
_ = e
1367+
}
1368+
var c = ClosureHolder()
1369+
c.fCoroutine = f
1370+
}
1371+
1372+
func closureCoroutineAssignmentVarBinding() {
1373+
var e = Empty()
1374+
e = Empty()
1375+
let f: () -> () = {
1376+
_ = e // expected-error {{'e' 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}}
1377+
}
1378+
var c = ClosureHolder()
1379+
c.fCoroutine = f
1380+
}
1381+
1382+
func closureCoroutineAssignmentVarArgument(_ e: inout Empty) {
1383+
// expected-note @-1 {{'e' is declared 'inout'}}
1384+
let f: () -> () = { // expected-error {{escaping closure captures 'inout' parameter 'e'}}
1385+
_ = e // expected-note {{captured here}}
1386+
}
1387+
var c = ClosureHolder()
1388+
c.fCoroutine = f
1389+
}

test/SILOptimizer/moveonly_addresschecker_diagnostics.sil

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public struct AddressOnlyGeneric<T : P> {
7676
sil @get_aggstruct : $@convention(thin) () -> @owned AggStruct
7777
sil @nonConsumingUseKlass : $@convention(thin) (@guaranteed Klass) -> ()
7878
sil @nonConsumingUseNonTrivialStruct : $@convention(thin) (@guaranteed NonTrivialStruct) -> ()
79+
sil @consumingUseNonTrivialStruct : $@convention(thin) (@owned NonTrivialStruct) -> ()
7980
sil @classConsume : $@convention(thin) (@owned Klass) -> () // user: %18
8081
sil @copyableClassConsume : $@convention(thin) (@owned CopyableKlass) -> () // user: %24
8182
sil @copyableClassUseMoveOnlyWithoutEscaping : $@convention(thin) (@guaranteed CopyableKlass) -> () // user: %16
@@ -364,4 +365,145 @@ bb0(%0 : @closureCapture @guaranteed $<τ_0_0 where τ_0_0 : P> { var AddressOnl
364365
end_access %17 : $*AddressOnlyGeneric<T>
365366
%22 = tuple ()
366367
return %22 : $()
367-
}
368+
}
369+
370+
//////////////////////////
371+
// MARK: Coroutine Test //
372+
//////////////////////////
373+
374+
sil [ossa] @coroutine_callee_uses_partial_apply : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields () {
375+
bb0(%0 : @guaranteed $@callee_guaranteed () -> ()):
376+
%1 = tuple ()
377+
yield %1 : $(), resume bb1, unwind bb2
378+
379+
bb1:
380+
return %1 : $()
381+
382+
bb2:
383+
unwind
384+
}
385+
386+
sil private [ossa] @$s23coroutine_partial_applyTf0s_n : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> () {
387+
bb0(%0 : @closureCapture $*NonTrivialStruct):
388+
%1 = mark_must_check [no_consume_or_assign] %0 : $*NonTrivialStruct
389+
debug_value %1 : $*NonTrivialStruct, let, name "e", argno 1, expr op_deref
390+
%3 = load_borrow %1 : $*NonTrivialStruct
391+
end_borrow %3 : $NonTrivialStruct
392+
%5 = tuple ()
393+
return %5 : $()
394+
}
395+
396+
sil [ossa] @coroutine_caller_uses_partial_apply : $@convention(thin) (@owned NonTrivialStruct) -> () {
397+
bb0(%0 : @owned $NonTrivialStruct):
398+
%1 = alloc_stack $NonTrivialStruct, let, name "e"
399+
%2 = mark_must_check [consumable_and_assignable] %1 : $*NonTrivialStruct // expected-error {{'e' used after consume}}
400+
store %0 to [init] %2 : $*NonTrivialStruct
401+
mark_function_escape %2 : $*NonTrivialStruct
402+
403+
%5 = function_ref @$s23coroutine_partial_applyTf0s_n : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
404+
%6 = partial_apply [callee_guaranteed] %5(%2) : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
405+
406+
%7 = function_ref @consumingUseNonTrivialStruct : $@convention(thin) (@owned NonTrivialStruct) -> ()
407+
408+
%8 = function_ref @coroutine_callee_uses_partial_apply : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields ()
409+
(%9, %10) = begin_apply %8(%6) : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields ()
410+
%11 = load [copy] %2 : $*NonTrivialStruct // expected-note {{consuming use here}}
411+
%12 = apply %7(%11) : $@convention(thin) (@owned NonTrivialStruct) -> ()
412+
end_apply %10 // expected-note {{non-consuming use here}}
413+
destroy_value %6 : $@callee_guaranteed () -> ()
414+
destroy_addr %2 : $*NonTrivialStruct
415+
dealloc_stack %1 : $*NonTrivialStruct
416+
%17 = tuple ()
417+
return %17 : $()
418+
}
419+
420+
sil [ossa] @coroutine_caller_uses_partial_apply2 : $@convention(thin) (@owned NonTrivialStruct) -> () {
421+
bb0(%0 : @owned $NonTrivialStruct):
422+
%1 = alloc_stack $NonTrivialStruct, let, name "e"
423+
%2 = mark_must_check [consumable_and_assignable] %1 : $*NonTrivialStruct // expected-error {{'e' used after consume}}
424+
store %0 to [init] %2 : $*NonTrivialStruct
425+
mark_function_escape %2 : $*NonTrivialStruct
426+
427+
%5 = function_ref @$s23coroutine_partial_applyTf0s_n : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
428+
%6 = partial_apply [callee_guaranteed] %5(%2) : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
429+
430+
%7 = function_ref @consumingUseNonTrivialStruct : $@convention(thin) (@owned NonTrivialStruct) -> ()
431+
432+
%8 = function_ref @coroutine_callee_uses_partial_apply : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields ()
433+
(%9, %10) = begin_apply %8(%6) : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields ()
434+
%11 = load [copy] %2 : $*NonTrivialStruct // expected-note {{consuming use here}}
435+
%12 = apply %7(%11) : $@convention(thin) (@owned NonTrivialStruct) -> ()
436+
abort_apply %10 // expected-note {{non-consuming use here}}
437+
destroy_value %6 : $@callee_guaranteed () -> ()
438+
destroy_addr %2 : $*NonTrivialStruct
439+
dealloc_stack %1 : $*NonTrivialStruct
440+
%17 = tuple ()
441+
return %17 : $()
442+
}
443+
444+
// This test has some copy_value that are eliminated by the object checker. We
445+
// are just testing the address checker here so we get a copy of noncopyable
446+
// typed value error since we do not run the object checker. But it is ok since
447+
// it doesn't effect the underlying test case.
448+
sil [ossa] @coroutine_caller_uses_partial_apply_reinit : $@convention(thin) (@guaranteed NonTrivialStruct) -> () {
449+
bb0(%0 : @guaranteed $NonTrivialStruct):
450+
%1 = copy_value %0 : $NonTrivialStruct // expected-error {{copy of noncopyable typed value. This is a compiler bug. Please file a bug with a small example of the bug}}
451+
%2 = mark_must_check [no_consume_or_assign] %1 : $NonTrivialStruct
452+
debug_value %2 : $NonTrivialStruct, let, name "x", argno 1
453+
%4 = alloc_stack $NonTrivialStruct, let, name "e"
454+
%5 = mark_must_check [consumable_and_assignable] %4 : $*NonTrivialStruct
455+
%6 = copy_value %2 : $NonTrivialStruct // expected-error {{copy of noncopyable typed value. This is a compiler bug. Please file a bug with a small example of the bug}}
456+
store %6 to [init] %5 : $*NonTrivialStruct
457+
mark_function_escape %5 : $*NonTrivialStruct
458+
%9 = function_ref @$s23coroutine_partial_applyTf0s_n : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
459+
%10 = partial_apply [callee_guaranteed] %9(%5) : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
460+
%11 = function_ref @consumingUseNonTrivialStruct : $@convention(thin) (@owned NonTrivialStruct) -> ()
461+
%12 = function_ref @coroutine_callee_uses_partial_apply : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields ()
462+
(%13, %14) = begin_apply %12(%10) : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields ()
463+
%15 = load [copy] %5 : $*NonTrivialStruct
464+
%16 = apply %11(%15) : $@convention(thin) (@owned NonTrivialStruct) -> ()
465+
%17 = copy_value %2 : $NonTrivialStruct // expected-error {{copy of noncopyable typed value. This is a compiler bug. Please file a bug with a small example of the bug}}
466+
store %17 to [init] %5 : $*NonTrivialStruct
467+
abort_apply %14
468+
destroy_value %2 : $NonTrivialStruct
469+
destroy_value %10 : $@callee_guaranteed () -> ()
470+
destroy_addr %5 : $*NonTrivialStruct
471+
dealloc_stack %4 : $*NonTrivialStruct
472+
%24 = tuple ()
473+
return %24 : $()
474+
}
475+
476+
sil [ossa] @coroutine_caller_uses_partial_apply_different_cfg : $@convention(thin) (@owned NonTrivialStruct) -> () {
477+
bb0(%0 : @owned $NonTrivialStruct):
478+
%1 = alloc_stack $NonTrivialStruct, let, name "e"
479+
%2 = mark_must_check [consumable_and_assignable] %1 : $*NonTrivialStruct
480+
// expected-error @-1 {{'e' used after consume}}
481+
// expected-error @-2 {{'e' used after consume}}
482+
store %0 to [init] %2 : $*NonTrivialStruct
483+
mark_function_escape %2 : $*NonTrivialStruct
484+
%5 = function_ref @$s23coroutine_partial_applyTf0s_n : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
485+
%6 = partial_apply [callee_guaranteed] %5(%2) : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
486+
%7 = function_ref @consumingUseNonTrivialStruct : $@convention(thin) (@owned NonTrivialStruct) -> ()
487+
%8 = function_ref @coroutine_callee_uses_partial_apply : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields ()
488+
(%9, %10) = begin_apply %8(%6) : $@yield_once @convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> @yields ()
489+
cond_br undef, bb1, bb2
490+
491+
bb1:
492+
%12 = load [copy] %2 : $*NonTrivialStruct // expected-note {{consuming use here}}
493+
%13 = apply %7(%12) : $@convention(thin) (@owned NonTrivialStruct) -> ()
494+
abort_apply %10 // expected-note {{non-consuming use here}}
495+
br bb3
496+
497+
bb2:
498+
%16 = load [copy] %2 : $*NonTrivialStruct // expected-note {{consuming use here}}
499+
%17 = apply %7(%16) : $@convention(thin) (@owned NonTrivialStruct) -> ()
500+
end_apply %10 // expected-note {{non-consuming use here}}
501+
br bb3
502+
503+
bb3:
504+
destroy_value %6 : $@callee_guaranteed () -> ()
505+
destroy_addr %2 : $*NonTrivialStruct
506+
dealloc_stack %1 : $*NonTrivialStruct
507+
%23 = tuple ()
508+
return %23 : $()
509+
}

0 commit comments

Comments
 (0)