Files
swift-mirror/test/Concurrency/transfernonsendable_initializers.swift
Michael Gottesman 85fafa5b1e [rbi] Begin tracking if a function argument is from a nonisolated(nonsending) parameter and adjust printing as appropriate.
Specifically in terms of printing, if NonisolatedNonsendingByDefault is enabled,
we print out things as nonisolated/task-isolated and @concurrent/@concurrent
task-isolated. If said feature is disabled, we print out things as
nonisolated(nonsending)/nonisolated(nonsending) task-isolated and
nonisolated/task-isolated. This ensures in the default case, diagnostics do not
change and we always print out things to match the expected meaning of
nonisolated depending on the mode.

I also updated the tests as appropriate/added some more tests/added to the
SendNonSendable education notes information about this.

(cherry picked from commit 14634b6847)
2025-07-09 12:25:03 -07:00

180 lines
7.6 KiB
Swift

// RUN: %target-swift-frontend -emit-sil -strict-concurrency=complete -target %target-swift-5.1-abi-triple -swift-version 6 -verify -verify-additional-prefix ni- %s -o /dev/null
// RUN: %target-swift-frontend -emit-sil -strict-concurrency=complete -target %target-swift-5.1-abi-triple -swift-version 6 -verify -verify-additional-prefix ni-ns- %s -o /dev/null -enable-upcoming-feature NonisolatedNonsendingByDefault
// REQUIRES: concurrency
// REQUIRES: asserts
// REQUIRES: swift_feature_NonisolatedNonsendingByDefault
// This test validates the behavior of transfernonsendable around initializers.
////////////////////////
// MARK: Declarations //
////////////////////////
class NonSendableKlass {}
@MainActor func transferToMain<T>(_ t: T) async {}
actor CustomActorInstance {}
@globalActor
struct CustomActor {
static let shared = CustomActorInstance()
}
/////////////////
// MARK: Tests //
/////////////////
actor ActorWithSynchronousNonIsolatedInit {
let k: NonSendableKlass
init(_ newK: NonSendableKlass) {
k = NonSendableKlass()
helper(newK)
// TODO: This should say actor isolated.
let _ = { @MainActor in
print(newK) // expected-error {{sending 'newK' risks causing data races}}
// expected-ni-note @-1 {{task-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
// expected-ni-ns-note @-2 {{task-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
}
}
init(x newK: NonSendableKlass) {
k = newK
helper(newK)
let _ = { @MainActor in
// TODO: Second part should say later 'self'-isolated uses
print(newK) // expected-error {{sending 'newK' risks causing data races}}
// expected-note @-1 {{'self'-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
}
}
init(ns: NonSendableKlass) async {
self.k = NonSendableKlass()
self.isolatedHelper(ns)
}
nonisolated func helper(_ newK: NonSendableKlass) {}
func isolatedHelper(_ newK: NonSendableKlass) {}
}
func initActorWithSyncNonIsolatedInit() {
let k = NonSendableKlass()
// TODO: This should say actor isolated.
_ = ActorWithSynchronousNonIsolatedInit(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{sending 'k' to actor-isolated initializer 'init(_:)' risks causing data races between actor-isolated and local nonisolated uses}}
let _ = { @MainActor in // expected-note {{access can happen concurrently}}
print(k)
}
}
func initActorWithSyncNonIsolatedInit2(_ k: NonSendableKlass) {
// TODO: This should say actor isolated.
_ = ActorWithSynchronousNonIsolatedInit(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{sending task-isolated 'k' to actor-isolated initializer 'init(_:)' risks causing data races between actor-isolated and task-isolated uses}}
let _ = { @MainActor in
print(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{task-isolated 'k' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
}
}
actor ActorWithAsyncIsolatedInit {
init(_ newK: NonSendableKlass) async {
let _ = { @MainActor in
print(newK) // expected-error {{sending 'newK' risks causing data races}}
// expected-note @-1 {{'self'-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later actor-isolated uses}}
}
}
}
func initActorWithAsyncIsolatedInit() async {
let k = NonSendableKlass()
// TODO: This should say actor isolated.
_ = await ActorWithAsyncIsolatedInit(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{sending 'k' to actor-isolated initializer 'init(_:)' risks causing data races between actor-isolated and local nonisolated uses}}
let _ = { @MainActor in // expected-note {{access can happen concurrently}}
print(k)
}
}
func initActorWithAsyncIsolatedInit2(_ k: NonSendableKlass) async {
// TODO: This should say actor isolated.
_ = await ActorWithAsyncIsolatedInit(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{sending task-isolated 'k' to actor-isolated initializer 'init(_:)' risks causing data races between actor-isolated and task-isolated uses}}
let _ = { @MainActor in
print(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{task-isolated 'k' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
}
}
////////////////////////////////
// MARK: Actor Isolated Class //
////////////////////////////////
@CustomActor
class ClassWithSynchronousNonIsolatedInit {
nonisolated init(_ newK: NonSendableKlass) {
// We do not error on this since helper is nonisolated and newK is
// considered task isolated.
helper(newK)
let _ = { @MainActor in
print(newK) // expected-error {{sending 'newK' risks causing data races}}
// expected-note @-1 {{task-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
}
}
nonisolated func helper(_ newK: NonSendableKlass) {}
}
func initClassWithSyncNonIsolatedInit() {
let k = NonSendableKlass()
_ = ClassWithSynchronousNonIsolatedInit(k)
let _ = { @MainActor in
print(k)
}
}
func initClassWithSyncNonIsolatedInit2(_ k: NonSendableKlass) {
_ = ClassWithSynchronousNonIsolatedInit(k)
let _ = { @MainActor in
print(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{task-isolated 'k' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
}
}
@CustomActor
class ClassWithAsyncIsolatedInit {
init(_ newK: NonSendableKlass) async {
let _ = { @MainActor in
print(newK) // expected-error {{sending 'newK' risks causing data races}}
// expected-note @-1 {{global actor 'CustomActor'-isolated 'newK' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later global actor 'CustomActor'-isolated uses}}
}
}
}
func initClassWithAsyncIsolatedInit() async {
let k = NonSendableKlass()
// TODO: Might make sense to emit a more specific error here since the closure
// is MainActor isolated. The actual capture is initially not isolated to
// MainActor.
_ = await ClassWithAsyncIsolatedInit(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{sending 'k' to global actor 'CustomActor'-isolated initializer 'init(_:)' risks causing data races between global actor 'CustomActor'-isolated and local nonisolated uses}}
let _ = { @MainActor in // expected-note {{access can happen concurrently}}
print(k)
}
}
func initClassWithAsyncIsolatedInit2(_ k: NonSendableKlass) async {
_ = await ClassWithAsyncIsolatedInit(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{sending task-isolated 'k' to global actor 'CustomActor'-isolated initializer 'init(_:)' risks causing data races between global actor 'CustomActor'-isolated and task-isolated uses}}
let _ = { @MainActor in
print(k) // expected-error {{sending 'k' risks causing data races}}
// expected-note @-1 {{task-isolated 'k' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
}
}