swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.32k stars 10.34k forks source link

Swift 6: incorrect compiler error when passing a `sending` closure from a global-actor-isolated function #74382

Closed groue closed 3 months ago

groue commented 3 months ago

Description

The Swift 6 compiler that ships with Xcode 16.0 beta (16A5171c) produces an invalid compiler error when the following conditions are met:

Reproduction

Package.swift

// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "SwiftIssue",
    platforms: [
        .macOS(.v10_15),
    ],
    products: [
        .library(name: "SwiftIssue", targets: ["SwiftIssue"]),
    ],
    targets: [
        .target(name: "SwiftIssue"),
    ]
)

SwiftIssue.swift

@globalActor actor MyGlobalActor: GlobalActor {
    static let shared = MyGlobalActor()
}

func acceptSendingClosure(completion: sending @escaping () -> Void) { }
func acceptMainActorClosure(completion: @escaping @MainActor () -> Void) { }
func acceptMyGlobalActorClosure(completion: @escaping @MyGlobalActor () -> Void) { }

@MainActor func mainActorIsolatedFunction() {
    // error: main actor-isolated value of type '() -> ()' passed as a
    // strongly transferred parameter; later accesses could race
    acceptSendingClosure { }

    acceptMainActorClosure { }     // OK
    acceptMyGlobalActorClosure { } // OK
}

@MyGlobalActor func myGlobalActorIsolatedFunction() {
    // error: global actor 'MyGlobalActor'-isolated value of type '() -> ()'
    // passed as a strongly transferred parameter; later accesses could race
    acceptSendingClosure { }

    acceptMainActorClosure { }     // OK
    acceptMyGlobalActorClosure { } // OK
}

actor MyActor {
    func actorIsolatedFunction() {
        acceptSendingClosure { }       // OK
        acceptMainActorClosure { }     // OK
        acceptMyGlobalActorClosure { } // OK
    }
}

Build errors:

% swift package clean && swift build
Building for debugging...
/Users/groue/Desktop/SwiftIssue/Sources/SwiftIssue/SwiftIssue.swift:12:5: error: main actor-isolated value of type '() -> ()' passed as a strongly transferred parameter; later accesses could race
10 |     // error: main actor-isolated value of type '() -> ()' passed as a
11 |     // strongly transferred parameter; later accesses could race
12 |     acceptSendingClosure { }
   |     `- error: main actor-isolated value of type '() -> ()' passed as a strongly transferred parameter; later accesses could race
13 |     
14 |     acceptMainActorClosure { }     // OK

/Users/groue/Desktop/SwiftIssue/Sources/SwiftIssue/SwiftIssue.swift:21:5: error: global actor 'MyGlobalActor'-isolated value of type '() -> ()' passed as a strongly transferred parameter; later accesses could race
19 |     // error: global actor 'MyGlobalActor'-isolated value of type '() -> ()'
20 |     // passed as a strongly transferred parameter; later accesses could race
21 |     acceptSendingClosure { }
   |     `- error: global actor 'MyGlobalActor'-isolated value of type '() -> ()' passed as a strongly transferred parameter; later accesses could race
22 |     
23 |     acceptMainActorClosure { }     // OK

In the Swift 5 language mode, the errors are emitted as warnings:

Variant of Package.swift for reproduction in Swift 5 language mode ```swift // swift-tools-version: 6.0 import PackageDescription let package = Package( name: "SwiftIssue", platforms: [ .macOS(.v10_15), ], products: [ .library(name: "SwiftIssue", targets: ["SwiftIssue"]), ], targets: [ .target( name: "SwiftIssue", swiftSettings: [ .enableUpcomingFeature("StrictConcurrency"), .enableExperimentalFeature("RegionBasedIsolation"), .enableExperimentalFeature("TransferringArgsAndResults"), ] ) ], swiftLanguageVersions: [.v5] ) ```

Expected behavior

No compiler error in the Swift 6 language mode.

No compiler warning in the Swift 5 language mode.

Environment

swift-driver version: 1.109.2 Apple Swift version 6.0 (swiftlang-6.0.0.3.300 clang-1600.0.20.10) Target: arm64-apple-macosx14.0

Additional information

This compiler error is pretty concerning, because it makes it impossible to call a function that accepts a completion block from a global-actor-isolated function. Functions with completion blocks still exist, and global-actor-isolated functions are pervasive in user code.

Please see this forum post for the initial report, and more information.

groue commented 3 months ago

Please find another reproducer below. In this variant the global-actor isolation comes from the enclosing type (also a very frequent pattern in user code):

@MainActor
struct MainActorIsolatedStruct {
    func mainActorIsolatedFunction() {
        // error: main actor-isolated value of type '() -> ()' passed as a
        // strongly transferred parameter; later accesses could race
        acceptSendingClosure { }

        acceptMainActorClosure { }     // OK
        acceptMyGlobalActorClosure { } // OK
    }
}
hborla commented 3 months ago

In case it wasn't obvious from the link, this is addressed in the second commit of https://github.com/apple/swift/pull/74239

groue commented 3 months ago

@hborla, the PR targets master, not release/6.0. It modifies the Task initializer, so that's a pretty big change. I'm wondering if those changes are planned for Swift 6.1, not Swift 6.0.

Could you please tell if the second commit has good chances to ship in the official release of Swift 6.0 (and the final Xcode 16)?

hborla commented 3 months ago

Both changes will be cherry picked to release/6.0.

groue commented 2 months ago

The issue is still present in Xcode 16 beta 2 (same sample code, same output). @hborla, @gottesmm, this is concerning because it lies at the intersection of two important use cases: the global-actor-isolated functions of user code, and the usage of sending in libraries. This bug creates confusion and makes libraries look bad. If I were to ship a GRDB beta right now, people be disappointed and would open issues in the GRDB repo.

mattmassicotte commented 2 months ago

@groue just wanted to let you know that your example code compiles cleanly using swift-6.0-DEVELOPMENT-SNAPSHOT-2024-06-22

groue commented 2 months ago

The issue is still present in Xcode 16.0 beta 3 (16A5202i) 😢

hborla commented 2 months ago

This fix is not included in Xcode 16.0 Beta 3.

groue commented 1 month ago

It is in beta 4 🥳