mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
714 lines
31 KiB
Swift
714 lines
31 KiB
Swift
// RUN: %target-typecheck-verify-swift -emit-sil -strict-concurrency=complete -enable-experimental-feature SendNonSendable -disable-availability-checking
|
|
// REQUIRES: concurrency
|
|
// REQUIRES: asserts
|
|
|
|
/*
|
|
This file tests the early features of the flow-sensitive Sendable checking implemented by the SendNonSendable SIL pass.
|
|
In particular, it tests the behavior of applications that cross isolation domains:
|
|
- non-Sendable values are statically grouped into "regions" of values that may alias or point to each other
|
|
- passing a non-Sendable value to an isolation-crossing call "consumed" the region of that value
|
|
- accessing a non-Sendable value requires its region to have not been consumed
|
|
|
|
All non-Sendable arguments, and "self" for non-Sendable classes, are assumed to be in the same region at the start of
|
|
each function, and that region must be preserved at all times - it cannot be passed to another isolation domain.
|
|
*/
|
|
|
|
class NonSendable {
|
|
var x : Any
|
|
var y : Any
|
|
|
|
init() {
|
|
x = 0;
|
|
y = 0;
|
|
}
|
|
|
|
init(_ z : Any) {
|
|
x = z;
|
|
y = z;
|
|
}
|
|
}
|
|
|
|
actor A {
|
|
func run_closure(_ closure : () -> ()) async {
|
|
closure();
|
|
}
|
|
|
|
func foo(_ ns : Any) {}
|
|
|
|
func foo_multi(_ : Any, _ : Any, _ : Any) {}
|
|
|
|
func foo_vararg(_ : Any ...) {}
|
|
}
|
|
|
|
func foo_noniso(_ ns : Any) {}
|
|
|
|
func foo_noniso_multi(_ : Any, _ : Any, _ : Any) {}
|
|
|
|
func foo_noniso_vararg(_ : Any ...) {}
|
|
|
|
func test_isolation_crossing_sensitivity(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
|
|
//this call does not consume ns0
|
|
foo_noniso(ns0);
|
|
|
|
//this call consumes ns1
|
|
await a.foo(ns1); //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
print(ns0);
|
|
print(ns1); //expected-note{{access here could race}}
|
|
}
|
|
|
|
func test_arg_nonconsumable(a : A, ns_arg : NonSendable) async {
|
|
let ns_let = NonSendable();
|
|
|
|
// safe to consume an rvalue
|
|
await a.foo(NonSendable());
|
|
|
|
// safe to consume an lvalue
|
|
await a.foo(ns_let);
|
|
|
|
// not safe to consume an arg
|
|
await a.foo(ns_arg); //expected-warning{{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
|
|
|
|
// check for no duplicate warnings once self is "consumed"
|
|
await a.foo(NonSendable());
|
|
}
|
|
|
|
func test_closure_capture(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
let ns3 = NonSendable();
|
|
|
|
let captures0 = { print(ns0) };
|
|
let captures12 = { print(ns1); print(ns2)};
|
|
|
|
let captures3 = { print(ns3) };
|
|
let captures3indirect = { captures3() };
|
|
|
|
// all lets should still be accessible - no consumed have happened yet
|
|
print(ns0)
|
|
print(ns1)
|
|
print(ns2)
|
|
print(ns3)
|
|
|
|
// this should consume ns0
|
|
await a.run_closure(captures0) //expected-warning{{passing argument of non-sendable type '() -> ()' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (3 access sites displayed)}}
|
|
|
|
print(ns0) //expected-note{{access here could race}}
|
|
print(ns1)
|
|
print(ns2)
|
|
print(ns3)
|
|
|
|
// this should consume ns1 and ns2
|
|
await a.run_closure(captures12) //expected-warning{{passing argument of non-sendable type '() -> ()' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (4 access sites displayed)}}
|
|
|
|
print(ns0) //expected-note{{access here could race}}
|
|
print(ns1) //expected-note{{access here could race}}
|
|
print(ns2) //expected-note{{access here could race}}
|
|
print(ns3)
|
|
|
|
// this should consume ns3
|
|
await a.run_closure(captures3indirect) //expected-warning{{passing argument of non-sendable type '() -> ()' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
print(ns0) //expected-note{{access here could race}}
|
|
print(ns1) //expected-note{{access here could race}}
|
|
print(ns2) //expected-note{{access here could race}}
|
|
print(ns3) //expected-note{{access here could race}}
|
|
}
|
|
|
|
func test_regions(a : A, b : Bool) async {
|
|
// definitely aliases - should be in the same region
|
|
let ns0_0 = NonSendable();
|
|
let ns0_1 = ns0_0;
|
|
|
|
// possibly aliases - should be in the same region
|
|
let ns1_0 = NonSendable();
|
|
var ns1_1 = NonSendable();
|
|
if (b) {
|
|
ns1_1 = ns1_0;
|
|
}
|
|
|
|
// accessible via pointer - should be in the same region
|
|
let ns2_0 = NonSendable();
|
|
let ns2_1 = NonSendable();
|
|
ns2_0.x = ns2_1;
|
|
|
|
// check for each of the above pairs that consuming half of it consumes the other half
|
|
|
|
if (b) {
|
|
await a.foo(ns0_0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns0_0) //expected-note{{access here could race}}
|
|
print(ns0_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns0_1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns0_0) //expected-note{{access here could race}}
|
|
print(ns0_1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
if (b) {
|
|
await a.foo(ns1_0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns1_0) //expected-note{{access here could race}}
|
|
print(ns1_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns1_1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns1_0) //expected-note{{access here could race}}
|
|
print(ns1_1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
if (b) {
|
|
await a.foo(ns2_0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns2_0) //expected-note{{access here could race}}
|
|
print(ns2_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns2_1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns2_0) //expected-note{{access here could race}}
|
|
print(ns2_1) //expected-note{{access here could race}}
|
|
}
|
|
}
|
|
|
|
func test_indirect_regions(a : A, b : Bool) async {
|
|
// in each of the following, nsX_0 and nsX_1 should be in the same region for various reasons
|
|
|
|
// 1 constructed using 0
|
|
let ns0_0 = NonSendable();
|
|
let ns0_1 = NonSendable(NonSendable(ns0_0));
|
|
|
|
// 1 constructed using 0, indirectly
|
|
let ns1_0 = NonSendable();
|
|
let ns1_aux = NonSendable(ns1_0);
|
|
let ns1_1 = NonSendable(ns1_aux);
|
|
|
|
// 1 and 0 constructed with same aux value
|
|
let ns2_aux = NonSendable();
|
|
let ns2_0 = NonSendable(ns2_aux);
|
|
let ns2_1 = NonSendable(ns2_aux);
|
|
|
|
// 0 points to aux, which points to 1
|
|
let ns3_aux = NonSendable();
|
|
let ns3_0 = NonSendable();
|
|
let ns3_1 = NonSendable();
|
|
ns3_0.x = ns3_aux;
|
|
ns3_aux.x = ns3_1;
|
|
|
|
// 1 and 0 point to same aux value
|
|
let ns4_aux = NonSendable();
|
|
let ns4_0 = NonSendable();
|
|
let ns4_1 = NonSendable();
|
|
ns4_0.x = ns4_aux;
|
|
ns4_1.x = ns4_aux;
|
|
|
|
// same aux value points to both 0 and 1
|
|
let ns5_aux = NonSendable();
|
|
let ns5_0 = ns5_aux.x;
|
|
let ns5_1 = ns5_aux.y;
|
|
|
|
// now check for each pair that consuming half of it consumed the other half
|
|
|
|
if (b) {
|
|
await a.foo(ns0_0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns0_0) //expected-note{{access here could race}}
|
|
print(ns0_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns0_1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns0_0) //expected-note{{access here could race}}
|
|
print(ns0_1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
if (b) {
|
|
await a.foo(ns1_0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns1_0) //expected-note{{access here could race}}
|
|
print(ns1_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns1_1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns1_0) //expected-note{{access here could race}}
|
|
print(ns1_1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
if (b) {
|
|
await a.foo(ns2_0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns2_0) //expected-note{{access here could race}}
|
|
print(ns2_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns2_1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns2_0) //expected-note{{access here could race}}
|
|
print(ns2_1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
if (b) {
|
|
await a.foo(ns3_0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns3_0) //expected-note{{access here could race}}
|
|
print(ns3_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns3_1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns3_0) //expected-note{{access here could race}}
|
|
print(ns3_1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
if (b) {
|
|
await a.foo(ns4_0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns4_0) //expected-note{{access here could race}}
|
|
print(ns4_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns4_1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns4_0) //expected-note{{access here could race}}
|
|
print(ns4_1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
if (b) {
|
|
await a.foo(ns5_0) //expected-warning{{passing argument of non-sendable type 'Any' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns5_0) //expected-note{{access here could race}}
|
|
print(ns5_1) //expected-note{{access here could race}}
|
|
} else {
|
|
await a.foo(ns5_1) //expected-warning{{passing argument of non-sendable type 'Any' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (2 access sites displayed)}}
|
|
|
|
print(ns5_0) //expected-note{{access here could race}}
|
|
print(ns5_1) //expected-note{{access here could race}}
|
|
}
|
|
}
|
|
|
|
// class methods in general cannot consume self, check that this is true except for Sendable classes (and actors)
|
|
|
|
class C_NonSendable {
|
|
func bar() {}
|
|
|
|
func bar(a : A) async {
|
|
let captures_self = { self.bar() }
|
|
|
|
// this is not a cross-isolation call, so it should be permitted
|
|
foo_noniso(captures_self)
|
|
|
|
// this is a cross-isolation call that captures non-Sendable self, so it should not be permitted
|
|
await a.foo(captures_self) //expected-warning{{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
|
|
}
|
|
}
|
|
|
|
final class C_Sendable : Sendable {
|
|
func bar() {}
|
|
|
|
func bar(a : A) async {
|
|
let captures_self = { self.bar() }
|
|
|
|
// this is not a cross-isolation call, so it should be permitted
|
|
foo_noniso(captures_self)
|
|
|
|
// this is a cross-isolation, but self is Sendable, so it should be permitted
|
|
await a.foo(captures_self)
|
|
}
|
|
}
|
|
|
|
actor A_Sendable {
|
|
func bar() {}
|
|
|
|
func bar(a : A) async {
|
|
let captures_self = { self.bar() }
|
|
|
|
// this is not a cross-isolation call, so it should be permitted
|
|
foo_noniso(captures_self)
|
|
|
|
// this is a cross-isolation, but self is Sendable, so it should be permitted
|
|
await a.foo(captures_self)
|
|
}
|
|
}
|
|
|
|
func basic_loopiness(a : A, b : Bool) async {
|
|
let ns = NonSendable()
|
|
|
|
while (b) {
|
|
await a.foo(ns)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
//expected-note@-2{{access here could race}}
|
|
}
|
|
}
|
|
|
|
func basic_loopiness_unsafe(a : A, b : Bool) async {
|
|
var ns0 = NonSendable()
|
|
var ns1 = NonSendable()
|
|
var ns2 = NonSendable()
|
|
var ns3 = NonSendable()
|
|
|
|
//should merge all vars
|
|
while (b) {
|
|
(ns0, ns1, ns2, ns3) = (ns1, ns2, ns3, ns0)
|
|
}
|
|
|
|
await a.foo(ns0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo(ns3) //expected-note{{access here could race}}
|
|
}
|
|
|
|
func basic_loopiness_safe(a : A, b : Bool) async {
|
|
var ns0 = NonSendable()
|
|
var ns1 = NonSendable()
|
|
var ns2 = NonSendable()
|
|
var ns3 = NonSendable()
|
|
|
|
//should keep ns0 and ns3 separate
|
|
while (b) {
|
|
(ns0, ns1, ns2, ns3) = (ns1, ns0, ns3, ns2)
|
|
}
|
|
|
|
await a.foo(ns0)
|
|
await a.foo(ns3)
|
|
}
|
|
|
|
struct StructBox {
|
|
var contents : NonSendable
|
|
|
|
init() {
|
|
contents = NonSendable()
|
|
}
|
|
}
|
|
|
|
func test_struct_assign_doesnt_merge(a : A, b : Bool) async {
|
|
let ns0 = NonSendable()
|
|
let ns1 = NonSendable()
|
|
|
|
//this should merge ns0 and ns1
|
|
var box = StructBox()
|
|
box.contents = ns0
|
|
box.contents = ns1
|
|
|
|
await a.foo(ns0)
|
|
await a.foo(ns1)
|
|
}
|
|
|
|
class ClassBox {
|
|
var contents : NonSendable
|
|
|
|
init() {
|
|
contents = NonSendable()
|
|
}
|
|
}
|
|
|
|
func test_class_assign_merges(a : A, b : Bool) async {
|
|
let ns0 = NonSendable()
|
|
let ns1 = NonSendable()
|
|
|
|
//this should merge ns0 and ns1
|
|
//TODO: check out the warning here more
|
|
var box = ClassBox() //expected-warning {{}}
|
|
box.contents = ns0
|
|
box.contents = ns1
|
|
|
|
await a.foo(ns0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo(ns1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
func test_stack_assign_doesnt_merge(a : A, b : Bool) async {
|
|
let ns0 = NonSendable()
|
|
let ns1 = NonSendable()
|
|
|
|
//this should NOT merge ns0 and ns1
|
|
var contents : NonSendable
|
|
contents = ns0
|
|
contents = ns1
|
|
|
|
foo_noniso(contents) //must use var to avoid warning
|
|
|
|
await a.foo(ns0)
|
|
await a.foo(ns1)
|
|
}
|
|
|
|
func test_stack_assign_and_capture_merges(a : A, b : Bool) async {
|
|
let ns0 = NonSendable()
|
|
let ns1 = NonSendable()
|
|
|
|
//this should NOT merge ns0 and ns1
|
|
var contents = NonSendable()
|
|
|
|
let closure = { print(contents) }
|
|
foo_noniso(closure) //must use let to avoid warning
|
|
|
|
contents = ns0
|
|
contents = ns1
|
|
|
|
await a.foo(ns0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo(ns1) //expected-note{{access here could race}}
|
|
}
|
|
|
|
func test_tuple_formation(a : A, i : Int) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
let ns3 = NonSendable();
|
|
let ns4 = NonSendable();
|
|
let ns012 = (ns0, ns1, ns2);
|
|
let ns13 = (ns1, ns3);
|
|
|
|
switch (i) {
|
|
case 0:
|
|
await a.foo(ns0) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (6 access sites displayed)}}
|
|
|
|
foo_noniso(ns0); //expected-note{{access here could race}}
|
|
foo_noniso(ns1); //expected-note{{access here could race}}
|
|
foo_noniso(ns2); //expected-note{{access here could race}}
|
|
foo_noniso(ns3); //expected-note{{access here could race}}
|
|
foo_noniso(ns4);
|
|
foo_noniso(ns012); //expected-note{{access here could race}}
|
|
foo_noniso(ns13); //expected-note{{access here could race}}
|
|
case 1:
|
|
await a.foo(ns1) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (6 access sites displayed)}}
|
|
|
|
foo_noniso(ns0); //expected-note{{access here could race}}
|
|
foo_noniso(ns1); //expected-note{{access here could race}}
|
|
foo_noniso(ns2); //expected-note{{access here could race}}
|
|
foo_noniso(ns3); //expected-note{{access here could race}}
|
|
foo_noniso(ns4);
|
|
foo_noniso(ns012); //expected-note{{access here could race}}
|
|
foo_noniso(ns13); //expected-note{{access here could race}}
|
|
case 2:
|
|
await a.foo(ns2) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (6 access sites displayed)}}
|
|
|
|
foo_noniso(ns0); //expected-note{{access here could race}}
|
|
foo_noniso(ns1); //expected-note{{access here could race}}
|
|
foo_noniso(ns2); //expected-note{{access here could race}}
|
|
foo_noniso(ns3); //expected-note{{access here could race}}
|
|
foo_noniso(ns4);
|
|
foo_noniso(ns012); //expected-note{{access here could race}}
|
|
foo_noniso(ns13); //expected-note{{access here could race}}
|
|
case 3:
|
|
await a.foo(ns4) //expected-warning{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
foo_noniso(ns0);
|
|
foo_noniso(ns1);
|
|
foo_noniso(ns2);
|
|
foo_noniso(ns4); //expected-note{{access here could race}}
|
|
foo_noniso(ns012);
|
|
foo_noniso(ns13);
|
|
case 4:
|
|
await a.foo(ns012) //expected-warning{{passing argument of non-sendable type '(NonSendable, NonSendable, NonSendable)' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (6 access sites displayed)}}
|
|
|
|
foo_noniso(ns0); //expected-note{{access here could race}}
|
|
foo_noniso(ns1); //expected-note{{access here could race}}
|
|
foo_noniso(ns2); //expected-note{{access here could race}}
|
|
foo_noniso(ns3); //expected-note{{access here could race}}
|
|
foo_noniso(ns4);
|
|
foo_noniso(ns012); //expected-note{{access here could race}}
|
|
foo_noniso(ns13); //expected-note{{access here could race}}
|
|
default:
|
|
await a.foo(ns13) //expected-warning{{passing argument of non-sendable type '(NonSendable, NonSendable)' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (6 access sites displayed)}}
|
|
|
|
foo_noniso(ns0); //expected-note{{access here could race}}
|
|
foo_noniso(ns1); //expected-note{{access here could race}}
|
|
foo_noniso(ns2); //expected-note{{access here could race}}
|
|
foo_noniso(ns3); //expected-note{{access here could race}}
|
|
foo_noniso(ns4);
|
|
foo_noniso(ns012); //expected-note{{access here could race}}
|
|
foo_noniso(ns13); //expected-note{{access here could race}}
|
|
}
|
|
}
|
|
|
|
func reuse_args_safe(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
|
|
// this should be perfectly legal - arguments are assumed to come from the same region
|
|
await a.foo_multi(ns0, ns0, ns0);
|
|
await a.foo_multi(ns1, ns2, ns1);
|
|
}
|
|
|
|
func one_consume_many_require(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
let ns3 = NonSendable();
|
|
let ns4 = NonSendable();
|
|
|
|
await a.foo_multi(ns0, ns1, ns2);
|
|
//expected-warning@-1 3{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
foo_noniso_multi(ns0, ns3, ns4); //expected-note {{access here could race}}
|
|
foo_noniso_multi(ns3, ns1, ns4); //expected-note {{access here could race}}
|
|
foo_noniso_multi(ns4, ns3, ns2); //expected-note {{access here could race}}
|
|
}
|
|
|
|
func one_consume_one_require(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
|
|
await a.foo_multi(ns0, ns1, ns2);
|
|
//expected-warning@-1 3{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
foo_noniso_multi(ns0, ns1, ns2); //expected-note 3{{access here could race}}
|
|
}
|
|
|
|
func many_consume_one_require(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
let ns3 = NonSendable();
|
|
let ns4 = NonSendable();
|
|
let ns5 = NonSendable();
|
|
|
|
await a.foo_multi(ns0, ns3, ns3)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo_multi(ns4, ns1, ns4)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo_multi(ns5, ns5, ns2)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
foo_noniso_multi(ns0, ns1, ns2); //expected-note 3{{access here could race}}
|
|
}
|
|
|
|
func many_consume_many_require(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
let ns3 = NonSendable();
|
|
let ns4 = NonSendable();
|
|
let ns5 = NonSendable();
|
|
let ns6 = NonSendable();
|
|
let ns7 = NonSendable();
|
|
|
|
await a.foo_multi(ns0, ns3, ns3)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo_multi(ns4, ns1, ns4)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo_multi(ns5, ns5, ns2)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'NonSendable' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
foo_noniso_multi(ns0, ns6, ns7); //expected-note {{access here could race}}
|
|
foo_noniso_multi(ns6, ns1, ns7); //expected-note {{access here could race}}
|
|
foo_noniso_multi(ns7, ns6, ns2); //expected-note {{access here could race}}
|
|
}
|
|
|
|
|
|
func reuse_args_safe_vararg(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
|
|
// this should be perfectly legal - arguments are assumed to come from the same region
|
|
await a.foo_vararg(ns0, ns0, ns0);
|
|
await a.foo_vararg(ns1, ns2, ns1);
|
|
}
|
|
|
|
func one_consume_many_require_varag(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
let ns3 = NonSendable();
|
|
let ns4 = NonSendable();
|
|
|
|
//TODO: find a way to make the type used in the diagnostic more specific than the signature type
|
|
await a.foo_vararg(ns0, ns1, ns2);
|
|
//expected-warning@-1 {{passing argument of non-sendable type 'Any...' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (3 access sites displayed)}}
|
|
|
|
foo_noniso_vararg(ns0, ns3, ns4); //expected-note {{access here could race}}
|
|
foo_noniso_vararg(ns3, ns1, ns4); //expected-note {{access here could race}}
|
|
foo_noniso_vararg(ns4, ns3, ns2); //expected-note {{access here could race}}
|
|
}
|
|
|
|
func one_consume_one_require_vararg(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
|
|
await a.foo_vararg(ns0, ns1, ns2);
|
|
//expected-warning@-1 {{passing argument of non-sendable type 'Any...' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (3 access sites displayed)}}
|
|
|
|
foo_noniso_vararg(ns0, ns1, ns2); //expected-note 3{{access here could race}}
|
|
}
|
|
|
|
func many_consume_one_require_vararg(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
let ns3 = NonSendable();
|
|
let ns4 = NonSendable();
|
|
let ns5 = NonSendable();
|
|
|
|
await a.foo_vararg(ns0, ns3, ns3)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'Any...' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo_vararg(ns4, ns1, ns4)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'Any...' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo_vararg(ns5, ns5, ns2)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'Any...' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
foo_noniso_vararg(ns0, ns1, ns2); //expected-note 3{{access here could race}}
|
|
}
|
|
|
|
func many_consume_many_require_vararg(a : A) async {
|
|
let ns0 = NonSendable();
|
|
let ns1 = NonSendable();
|
|
let ns2 = NonSendable();
|
|
let ns3 = NonSendable();
|
|
let ns4 = NonSendable();
|
|
let ns5 = NonSendable();
|
|
let ns6 = NonSendable();
|
|
let ns7 = NonSendable();
|
|
|
|
await a.foo_vararg(ns0, ns3, ns3)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'Any...' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo_vararg(ns4, ns1, ns4)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'Any...' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
await a.foo_vararg(ns5, ns5, ns2)
|
|
//expected-warning@-1{{passing argument of non-sendable type 'Any...' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
|
|
foo_noniso_vararg(ns0, ns6, ns7); //expected-note {{access here could race}}
|
|
foo_noniso_vararg(ns6, ns1, ns7); //expected-note {{access here could race}}
|
|
foo_noniso_vararg(ns7, ns6, ns2); //expected-note {{access here could race}}
|
|
}
|
|
|
|
enum E {
|
|
case E1(NonSendable)
|
|
case E2(NonSendable)
|
|
case E3(NonSendable)
|
|
}
|
|
|
|
func enum_test(a : A) async {
|
|
let e1 = E.E1(NonSendable())
|
|
let e2 = E.E2(NonSendable())
|
|
let e3 = E.E3(NonSendable())
|
|
let e4 = E.E1(NonSendable())
|
|
|
|
switch (e1) {
|
|
|
|
//this case merges e1 and e2
|
|
case let .E1(ns1):
|
|
switch (e2) {
|
|
case let .E2(ns2):
|
|
ns1.x = ns2.x
|
|
default: ()
|
|
}
|
|
|
|
//this case consumes e3
|
|
case .E2:
|
|
switch (e3) {
|
|
case let .E3(ns3):
|
|
await a.foo(ns3.x); //expected-warning{{passing argument of non-sendable type 'Any' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
default: ()
|
|
}
|
|
|
|
//this case does nothing
|
|
case .E3:
|
|
foo_noniso(e4);
|
|
}
|
|
|
|
await a.foo(e1); //expected-warning{{passing argument of non-sendable type 'E' from nonisolated context to actor-isolated context at this call site could yield a race with accesses later in this function (1 access site displayed)}}
|
|
foo_noniso(e2); //expected-note{{access here could race}}
|
|
foo_noniso(e3); //expected-note{{access here could race}}
|
|
foo_noniso(e4);
|
|
}
|