Files
swift-mirror/test/Concurrency/transfernonsendable_region_based_sendability.swift
Erik Eckstein 8da6c8a072 tests: temporarily disable varargs test in transfernonsendable_region_based_sendability.swift
TODO: fix handling of Array initialization in the SendNonSenable pass
2025-10-10 14:20:58 +02:00

963 lines
45 KiB
Swift

// RUN: %target-swift-frontend -strict-concurrency=complete -target %target-swift-5.1-abi-triple -parse-as-library -emit-sil -o /dev/null -verify %s -enable-upcoming-feature GlobalActorIsolatedTypesUsability
// RUN: %target-swift-frontend -strict-concurrency=complete -target %target-swift-5.1-abi-triple -parse-as-library -emit-sil -o /dev/null -verify %s -enable-upcoming-feature GlobalActorIsolatedTypesUsability -enable-upcoming-feature NonisolatedNonsendingByDefault
// REQUIRES: concurrency
// REQUIRES: swift_feature_GlobalActorIsolatedTypesUsability
// REQUIRES: swift_feature_NonisolatedNonsendingByDefault
/*
This file tests the early features of the flow-sensitive Sendable checking implemented by the TransferNonSendable 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 { // expected-complete-note 73{{}}
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 ...) {}
var bool : Bool { false }
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 {{sending 'ns1' risks causing data races}}
// expected-note @-1 {{sending 'ns1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
print(ns0);
print(ns1); // expected-note {{access can happen concurrently}}
}
func test_arg_nonconsumable(a : A, ns_arg : NonSendable) async {
let ns_let = NonSendable();
// Safe to consume an rvalue.
await a.foo(NonSendable()); // expected-complete-warning {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
// Safe to consume an lvalue.
await a.foo(ns_let); // expected-complete-warning {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
// Not safe to consume an arg.
await a.foo(ns_arg); // expected-warning {{sending 'ns_arg' risks causing data races}}
// expected-note @-1 {{sending task-isolated 'ns_arg' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and task-isolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
// Check for no duplicate warnings once self is "consumed"
await a.foo(NonSendable()); // expected-complete-warning {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
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 @-1 {{sending 'captures0' risks causing data races}}
// expected-note @-2 {{sending 'captures0' to actor-isolated instance method 'run_closure' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-1 {{passing argument of non-Sendable type '() -> ()' into actor-isolated context may introduce data races}}
// expected-complete-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
print(ns0) // expected-note {{access can happen concurrently}}
print(ns1)
print(ns2)
print(ns3)
// this should consume ns1 and ns2
await a.run_closure(captures12)
// expected-warning @-1 {{sending 'captures12' risks causing data races}}
// expected-note @-2 {{sending 'captures12' to actor-isolated instance method 'run_closure' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-1 {{passing argument of non-Sendable type '() -> ()' into actor-isolated context may introduce data races}}
// expected-complete-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
// This only touches ns1/ns2 so we get an error on ns1 since it is first.
print(ns0)
print(ns1) // expected-note {{access can happen concurrently}}
print(ns2)
print(ns3)
// this should consume ns3
await a.run_closure(captures3indirect)
// expected-warning @-1 {{sending 'captures3indirect' risks causing data races}}
// expected-note @-2 {{sending 'captures3indirect' to actor-isolated instance method 'run_closure' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-1 {{passing argument of non-Sendable type '() -> ()' into actor-isolated context may introduce data races}}
// expected-complete-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
// This only uses ns3 so we should only emit an error on ns3.
print(ns0)
print(ns1)
print(ns2)
print(ns3) // expected-note {{access can happen concurrently}}
}
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 {{sending 'ns0_0' risks causing data races}}
// expected-note @-1 {{sending 'ns0_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if (b) {
print(ns0_0) // expected-note {{access can happen concurrently}}
} else {
print(ns0_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns0_1) // expected-warning {{sending 'ns0_1' risks causing data races}}
// expected-note @-1 {{sending 'ns0_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns0_0) // expected-note {{access can happen concurrently}}
} else {
print(ns0_1) // expected-note {{access can happen concurrently}}
}
}
if (b) {
await a.foo(ns1_0) // expected-warning {{sending 'ns1_0' risks causing data races}}
// expected-note @-1 {{sending 'ns1_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns1_0) // expected-note {{access can happen concurrently}}
} else {
print(ns1_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns1_1) // expected-warning {{sending 'ns1_1' risks causing data races}}
// expected-note @-1 {{sending 'ns1_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns1_0) // expected-note {{access can happen concurrently}}
} else {
print(ns1_1) // expected-note {{access can happen concurrently}}
}
}
if (b) {
await a.foo(ns2_0) // expected-warning {{sending 'ns2_0' risks causing data races}}
// expected-note @-1 {{sending 'ns2_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns2_0) // expected-note {{access can happen concurrently}}
} else {
print(ns2_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns2_1) // expected-warning {{sending 'ns2_1' risks causing data races}}
// expected-note @-1 {{sending 'ns2_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns2_0) // expected-note {{access can happen concurrently}}
} else {
print(ns2_1) // expected-note {{access can happen concurrently}}
}
}
}
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 {{sending 'ns0_0' risks causing data races}}
// expected-note @-1 {{sending 'ns0_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns0_0) // expected-note {{access can happen concurrently}}
} else {
print(ns0_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns0_1) // expected-warning {{sending 'ns0_1' risks causing data races}}
// expected-note @-1 {{sending 'ns0_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns0_0) // expected-note {{access can happen concurrently}}
} else {
print(ns0_1) // expected-note {{access can happen concurrently}}
}
}
if (b) {
await a.foo(ns1_0) // expected-warning {{sending 'ns1_0' risks causing data races}}
// expected-note @-1 {{sending 'ns1_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns1_0) // expected-note {{access can happen concurrently}}
} else {
print(ns1_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns1_1) // expected-warning {{sending 'ns1_1' risks causing data races}}
// expected-note @-1 {{sending 'ns1_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns1_0) // expected-note {{access can happen concurrently}}
} else {
print(ns1_1) // expected-note {{access can happen concurrently}}
}
}
if (b) {
await a.foo(ns2_0) // expected-warning {{sending 'ns2_0' risks causing data races}}
// expected-note @-1 {{sending 'ns2_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns2_0) // expected-note {{access can happen concurrently}}
} else {
print(ns2_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns2_1) // expected-warning {{sending 'ns2_1' risks causing data races}}
// expected-note @-1 {{sending 'ns2_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns2_0) // expected-note {{access can happen concurrently}}
} else {
print(ns2_1) // expected-note {{access can happen concurrently}}
}
}
if (b) {
await a.foo(ns3_0) // expected-warning {{sending 'ns3_0' risks causing data races}}
// expected-note @-1 {{sending 'ns3_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns3_0) // expected-note {{access can happen concurrently}}
} else {
print(ns3_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns3_1) // expected-warning {{sending 'ns3_1' risks causing data races}}
// expected-note @-1 {{sending 'ns3_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns3_0) // expected-note {{access can happen concurrently}}
} else {
print(ns3_1) // expected-note {{access can happen concurrently}}
}
}
if (b) {
await a.foo(ns4_0) // expected-warning {{sending 'ns4_0' risks causing data races}}
// expected-note @-1 {{sending 'ns4_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns4_0) // expected-note {{access can happen concurrently}}
} else {
print(ns4_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns4_1) // expected-warning {{sending 'ns4_1' risks causing data races}}
// expected-note @-1 {{sending 'ns4_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if b {
print(ns4_0) // expected-note {{access can happen concurrently}}
} else {
print(ns4_1) // expected-note {{access can happen concurrently}}
}
}
if (b) {
await a.foo(ns5_0) // expected-warning {{sending 'ns5_0' risks causing data races}}
// expected-note @-1 {{sending 'ns5_0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'Any' into actor-isolated context may introduce data races}}
if b {
print(ns5_0) // expected-note {{access can happen concurrently}}
} else {
print(ns5_1) // expected-note {{access can happen concurrently}}
}
} else {
await a.foo(ns5_1) // expected-warning {{sending 'ns5_1' risks causing data races}}
// expected-note @-1 {{sending 'ns5_1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'Any' into actor-isolated context may introduce data races}}
if b {
print(ns5_0) // expected-note {{access can happen concurrently}}
} else {
print(ns5_1) // expected-note {{access can happen concurrently}}
}
}
}
// 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 {{sending 'captures_self' risks causing data races}}
// expected-note @-1 {{sending task-isolated 'captures_self' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and task-isolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type '() -> ()' into actor-isolated context may introduce data races}}
// expected-complete-note @-3 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
}
}
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)
// expected-complete-warning @-1 {{passing argument of non-Sendable type '() -> ()' into actor-isolated context may introduce data races}}
// expected-complete-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
}
}
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 call passing a call that should be treated
// as isolated to self since it captures self which is isolated to our
// actor and is non-Sendable. For now, we ban this since we do not
// support the ability to dynamically invoke the synchronous closure on
// the specific actor.
await a.foo(captures_self) // expected-warning {{sending 'captures_self' risks causing data races}}
// expected-note @-1 {{sending 'self'-isolated 'captures_self' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and 'self'-isolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type '() -> ()' into actor-isolated context may introduce data races}}
// expected-complete-note @-3 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
}
}
func basic_loopiness(a : A, b : Bool) async {
let ns = NonSendable()
while (b) {
await a.foo(ns) // expected-warning {{sending 'ns' risks causing data races}}
// expected-note @-1 {{sending 'ns' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-note @-2 {{access can happen concurrently}}
// expected-complete-warning @-3 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
}
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 {{sending 'ns0' risks causing data races}}
// expected-note @-1 {{sending 'ns0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo(ns3) // expected-note {{access can happen concurrently}}
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
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)
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo(ns3)
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
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)
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo(ns1)
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
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.
var box = ClassBox()
box = ClassBox()
box.contents = ns0
box.contents = ns1
await a.foo(ns0) // expected-warning {{sending 'ns0' risks causing data races}}
// expected-note @-1 {{sending 'ns0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo(ns1) // expected-note {{access can happen concurrently}}
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
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)
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo(ns1)
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
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 {{sending 'ns0' risks causing data races}}
// expected-note @-1 {{sending 'ns0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo(ns1) // expected-note {{access can happen concurrently}}
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
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 {{sending 'ns0' risks causing data races}}
// expected-note @-1 {{sending 'ns0' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if bool {
foo_noniso(ns0); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns1); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns2); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns3); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns4);
} else if bool {
foo_noniso(ns012); // expected-note {{access can happen concurrently}}
} else {
foo_noniso(ns13); // expected-note {{access can happen concurrently}}
}
case 1:
await a.foo(ns1) // expected-warning {{sending 'ns1' risks causing data races}}
// expected-note @-1 {{sending 'ns1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if bool {
foo_noniso(ns0); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns1); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns2); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns3); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns4);
} else if bool {
foo_noniso(ns012); // expected-note {{access can happen concurrently}}
} else {
foo_noniso(ns13); // expected-note {{access can happen concurrently}}
}
case 2:
await a.foo(ns2) // expected-warning {{sending 'ns2' risks causing data races}}
// expected-note @-1 {{sending 'ns2' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
if bool {
foo_noniso(ns0); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns1); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns2); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns3); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns4);
} else if bool {
foo_noniso(ns012); // expected-note {{access can happen concurrently}}
} else {
foo_noniso(ns13); // expected-note {{access can happen concurrently}}
}
case 3:
await a.foo(ns4) // expected-warning {{sending 'ns4' risks causing data races}}
// expected-note @-1 {{sending 'ns4' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
foo_noniso(ns0);
foo_noniso(ns1);
foo_noniso(ns2);
foo_noniso(ns4); // expected-note {{access can happen concurrently}}
foo_noniso(ns012);
foo_noniso(ns13);
case 4:
await a.foo(ns012) // expected-warning {{sending value of non-Sendable type '(NonSendable, NonSendable, NonSendable)' risks causing data races}}
// expected-note @-1 {{sending value of non-Sendable type '(NonSendable, NonSendable, NonSendable)' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 3{{passing argument of non-Sendable type '(NonSendable, NonSendable, NonSendable)' into actor-isolated context may introduce data races}}
if bool {
foo_noniso(ns0); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns1); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns2); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns3); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns4);
} else if bool {
foo_noniso(ns012); // expected-note {{access can happen concurrently}}
} else {
foo_noniso(ns13); // expected-note {{access can happen concurrently}}
}
default:
await a.foo(ns13) // expected-warning {{sending value of non-Sendable type '(NonSendable, NonSendable)' risks causing data races}}
// expected-note @-1 {{sending value of non-Sendable type '(NonSendable, NonSendable)' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-1 2{{passing argument of non-Sendable type '(NonSendable, NonSendable)' into actor-isolated context may introduce data races}}
if bool {
foo_noniso(ns0); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns1); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns2); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns3); // expected-note {{access can happen concurrently}}
} else if bool {
foo_noniso(ns4);
} else if bool {
foo_noniso(ns012); // expected-note {{access can happen concurrently}}
} else {
foo_noniso(ns13); // expected-note {{access can happen concurrently}}
}
}
}
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);
// expected-complete-warning @-1 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo_multi(ns1, ns2, ns1);
// expected-complete-warning @-1 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
}
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-complete-warning @-1 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
// expected-warning @-2 {{sending 'ns0' risks causing data races}}
// expected-note @-3 {{sending 'ns0' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-warning @-4 {{sending 'ns1' risks causing data races}}
// expected-note @-5 {{sending 'ns1' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-warning @-6 {{sending 'ns2' risks causing data races}}
// expected-note @-7 {{sending 'ns2' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
foo_noniso_multi(ns0, ns3, ns4); // expected-note {{access can happen concurrently}}
foo_noniso_multi(ns3, ns1, ns4); // expected-note {{access can happen concurrently}}
foo_noniso_multi(ns4, ns3, ns2); // expected-note {{access can happen concurrently}}
}
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-complete-warning @-1 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
// expected-warning @-2 {{sending 'ns0' risks causing data races}}
// expected-note @-3 {{sending 'ns0' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-warning @-4 {{sending 'ns1' risks causing data races}}
// expected-note @-5 {{sending 'ns1' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-warning @-6 {{sending 'ns2' risks causing data races}}
// expected-note @-7 {{sending 'ns2' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
foo_noniso_multi(ns0, ns1, ns2); // expected-note 3{{access can happen concurrently}}
}
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 {{sending 'ns0' risks causing data races}}
// expected-note @-1 {{sending 'ns0' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo_multi(ns4, ns1, ns4) // expected-warning {{sending 'ns1' risks causing data races}}
// expected-note @-1 {{sending 'ns1' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo_multi(ns5, ns5, ns2) // expected-warning {{sending 'ns2' risks causing data races}}
// expected-note @-1 {{sending 'ns2' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
foo_noniso_multi(ns0, ns1, ns2); //expected-note 3{{access can happen concurrently}}
}
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 {{sending 'ns0' risks causing data races}}
// expected-note @-1 {{sending 'ns0' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo_multi(ns4, ns1, ns4) // expected-warning {{sending 'ns1' risks causing data races}}
// expected-note @-1 {{sending 'ns1' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
await a.foo_multi(ns5, ns5, ns2) // expected-warning {{sending 'ns2' risks causing data races}}
// expected-note @-1 {{sending 'ns2' to actor-isolated instance method 'foo_multi' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 3{{passing argument of non-Sendable type 'NonSendable' into actor-isolated context may introduce data races}}
foo_noniso_multi(ns0, ns6, ns7); // expected-note {{access can happen concurrently}}
foo_noniso_multi(ns6, ns1, ns7); // expected-note {{access can happen concurrently}}
foo_noniso_multi(ns7, ns6, ns2); // expected-note {{access can happen concurrently}}
}
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);
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
await a.foo_vararg(ns1, ns2, ns1);
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
}
// The varargs tests are currently disabled because they fail on macos.
// TODO: fix handling of Array initialization in the SendNonSenable pass
/*
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);
// xxpected-warning @-1 {{sending value of non-Sendable type 'Any...' risks causing data races}}
// xxpected-note @-2 {{sending value of non-Sendable type 'Any...' to actor-isolated instance method 'foo_vararg' risks causing data races between actor-isolated and local nonisolated uses}}
// xxpected-complete-warning @-2 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
if bool {
foo_noniso_vararg(ns0, ns3, ns4); // xxpected-note {{access can happen concurrently}}
} else if bool {
foo_noniso_vararg(ns3, ns1, ns4); // xxpected-note {{access can happen concurrently}}
} else {
foo_noniso_vararg(ns4, ns3, ns2); // xxpected-note {{access can happen concurrently}}
}
}
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);
// xxpected-warning @-1 {{sending value of non-Sendable type 'Any...' risks causing data races}}
// xxpected-note @-2 {{sending value of non-Sendable type 'Any...' to actor-isolated instance method 'foo_vararg' risks causing data races between actor-isolated and local nonisolated uses}}
// xxpected-complete-warning @-2 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
foo_noniso_vararg(ns0, ns1, ns2); // xxpected-note 1{{access can happen concurrently}}
}
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)
// xxpected-warning @-1 {{sending value of non-Sendable type 'Any...' risks causing data races}}
// xxpected-note @-2 {{sending value of non-Sendable type 'Any...' to actor-isolated instance method 'foo_vararg' risks causing data races between actor-isolated and local nonisolated uses}}
// xxpected-complete-warning @-2 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
await a.foo_vararg(ns4, ns1, ns4)
// xxpected-warning @-1 {{sending value of non-Sendable type 'Any...' risks causing data races}}
// xxpected-note @-2 {{sending value of non-Sendable type 'Any...' to actor-isolated instance method 'foo_vararg' risks causing data races between actor-isolated and local nonisolated uses}}
// xxpected-complete-warning @-2 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
await a.foo_vararg(ns5, ns5, ns2)
// xxpected-warning @-1 {{sending value of non-Sendable type 'Any...' risks causing data races}}
// xxpected-note @-2 {{sending value of non-Sendable type 'Any...' to actor-isolated instance method 'foo_vararg' risks causing data races between actor-isolated and local nonisolated uses}}
// xxpected-complete-warning @-2 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
foo_noniso_vararg(ns0, ns1, ns2); // xxpected-note 3{{access can happen concurrently}}
}
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)
// xxpected-warning @-1 {{sending value of non-Sendable type 'Any...' risks causing data races}}
// xxpected-note @-2 {{sending value of non-Sendable type 'Any...' to actor-isolated instance method 'foo_vararg' risks causing data races between actor-isolated and local nonisolated uses}}
// xxpected-complete-warning @-2 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
await a.foo_vararg(ns4, ns1, ns4)
// xxpected-warning @-1 {{sending value of non-Sendable type 'Any...' risks causing data races}}
// xxpected-note @-2 {{sending value of non-Sendable type 'Any...' to actor-isolated instance method 'foo_vararg' risks causing data races between actor-isolated and local nonisolated uses}}
// xxpected-complete-warning @-2 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
await a.foo_vararg(ns5, ns5, ns2)
// xxpected-warning @-1 {{sending value of non-Sendable type 'Any...' risks causing data races}}
// xxpected-note @-2 {{sending value of non-Sendable type 'Any...' to actor-isolated instance method 'foo_vararg' risks causing data races between actor-isolated and local nonisolated uses}}
// xxpected-complete-warning @-2 {{passing argument of non-Sendable type 'Any...' into actor-isolated context may introduce data races}}
if bool {
foo_noniso_vararg(ns0, ns6, ns7); // xxpected-note {{access can happen concurrently}}
} else if bool {
foo_noniso_vararg(ns6, ns1, ns7); // xxpected-note {{access can happen concurrently}}
} else {
foo_noniso_vararg(ns7, ns6, ns2); // xxpected-note {{access can happen concurrently}}
}
}
*/
enum E { // expected-complete-note {{consider making enum 'E' conform to the 'Sendable' protocol}}
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 {{sending value of non-Sendable type 'Any' risks causing data races}}
// expected-note @-1 {{sending value of non-Sendable type 'Any' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-1 {{passing argument of non-Sendable type 'Any' into actor-isolated context may introduce data races}}
default: ()
}
//this case does nothing
case .E3:
foo_noniso(e4);
}
await a.foo(e1); // expected-warning {{sending 'e1' risks causing data races}}
// expected-note @-1 {{sending 'e1' to actor-isolated instance method 'foo' risks causing data races between actor-isolated and local nonisolated uses}}
// expected-complete-warning @-2 {{passing argument of non-Sendable type 'E' into actor-isolated context may introduce data races}}
foo_noniso(e2); // expected-note {{access can happen concurrently}}
foo_noniso(e3); // expected-note {{access can happen concurrently}}
foo_noniso(e4);
}