Skip to content

[Sema]: ban @isolated(any) conversions to synchronous function types #80812

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5045,6 +5045,10 @@ ERROR(capture_across_type_decl,none,
"scope",
(const NominalTypeDecl *, const ValueDecl *))

ERROR(isolated_any_conversion_to_synchronous_func,none,
"converting @isolated(any) function of type %0 to synchronous function type %1 is not allowed",
(Type, Type))

ERROR(reference_to_invalid_decl,none,
"cannot reference invalid declaration %0", (const ValueDecl *))

Expand Down
12 changes: 12 additions & 0 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2694,6 +2694,18 @@ namespace {
}
}

// @isolated(any) functions (async or not) cannot be converted to
// synchronous, non-@isolated(any) functions.
if (fromIsolation.isErased() && !toIsolation.isErased() &&
!toFnType->isAsync()) {
ctx.Diags
.diagnose(funcConv->getLoc(),
diag::isolated_any_conversion_to_synchronous_func,
fromFnType, toFnType)
.warnUntilFutureSwiftVersion();
return;
}

// Conversions from non-Sendable types are handled by
// region-based isolation.
// Function conversions are used to inject concurrency attributes
Expand Down
30 changes: 28 additions & 2 deletions test/Concurrency/isolated_any.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,23 @@ func testEraseFromIsolatedArgument() {
requireIsolatedAnyWithActorArgument(hasIsolatedArgument)
}

func requireSendableNonIsolated(_ fn: @Sendable () -> ()) {}
func requireSendableNonIsolated(_ fn: @Sendable () async -> ()) {}
func testConvertIsolatedAnyToNonIsolated(fn: @Sendable @isolated(any) () -> ()) {
requireSendableNonIsolated(fn)
}

func requireSendableNonIsolated_sync(_ fn: @Sendable () -> ()) {}
func testConvertIsolatedAnyToNonIsolated_sync(fn: @Sendable @isolated(any) () -> ()) {
// expected-warning @+1 {{converting @isolated(any) function of type '@isolated(any) @Sendable () -> ()' to synchronous function type '@Sendable () -> ()' is not allowed; this will be an error in a future Swift language mode}}
requireSendableNonIsolated_sync(fn)
}

func requireNonSendableNonIsolated_sync(_ fn: () -> ()) {}
func testConvertIsolatedAnyToNonSendableNonIsolated_sync(fn: @isolated(any) () -> ()) {
// expected-warning @+1 {{converting @isolated(any) function of type '@isolated(any) () -> ()' to synchronous function type '() -> ()' is not allowed; this will be an error in a future Swift language mode}}
requireNonSendableNonIsolated_sync(fn)
}

func requireSendableGlobalActor(_ fn: @Sendable @MainActor () -> ()) {}
func testConvertIsolatedAnyToMainActor(fn: @Sendable @isolated(any) () -> ()) {
// expected-error @+1 {{cannot convert value of type '@isolated(any) @Sendable () -> ()' to expected argument type '@MainActor @Sendable () -> ()'}}
Expand Down Expand Up @@ -125,8 +137,22 @@ func testFunctionIsolationExprTuple(
return (fn1?.isolation, fn2?.isolation)
}

func nonSendableIsolatedAny(
func nonSendableIsolatedAnySyncToSendableSync(
_ fn: @escaping @isolated(any) () -> Void // expected-note {{parameter 'fn' is implicitly non-sendable}}
) {
let _: @Sendable () -> Void = fn // expected-warning {{using non-sendable parameter 'fn' in a context expecting a '@Sendable' closure}}
// expected-warning @-1 {{converting @isolated(any) function of type '@isolated(any) () -> Void' to synchronous function type '@Sendable () -> Void' is not allowed; this will be an error in a future Swift language mode}}
}

func nonSendableIsolatedAnyAsyncToSendableSync(
_ fn: @escaping @isolated(any) () async -> Void // expected-note {{parameter 'fn' is implicitly non-sendable}}
) {
let _: @Sendable () -> Void = fn // expected-warning {{using non-sendable parameter 'fn' in a context expecting a '@Sendable' closure}}
// expected-error @-1 {{invalid conversion from 'async' function of type '@isolated(any) () async -> Void' to synchronous function type '@Sendable () -> Void'}}
}

func nonSendableIsolatedAnyAsyncToSendableAsync(
_ fn: @escaping @isolated(any) () async -> Void // expected-note {{parameter 'fn' is implicitly non-sendable}}
) {
let _: @Sendable () async -> Void = fn // expected-warning {{using non-sendable parameter 'fn' in a context expecting a '@Sendable' closure}}
}