swiftlang / swift

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

Use of TaskLocal seems to defeat sending #75524

Open mattmassicotte opened 3 months ago

mattmassicotte commented 3 months ago

Description

Using TaskLocal.withValue defeats my use of sending here. I'm having a little trouble determing if this a problem with the withValue definition, or another kind of issue.

Adding the constraint that T be Sendable fixes things, but it doesn't seem like that should be necessary here.

test.swift:15:10: error: returning 'isolation'-isolated 'value' as a 'sending' result risks causing data races

Reproduction

public final class MyClass {
    @TaskLocal private static var local = false

    public init() {
    }

    public func withBlock<T>(
        isolation: isolated (any Actor)? = #isolation,
        _ block: () async throws -> sending T
    ) async rethrows -> sending T {
        let value = try await Self.$local.withValue(true) {
            try await block()
        }

        return value
    }
}

Expected behavior

I would expect this to compile diagnostic-free.

Environment

Apple Swift version 6.0-dev (LLVM 961ea4ef750ef11, Swift dc553031a742cd6) Target: arm64-apple-macosx14.0

Additional information

No response

gottesmm commented 2 months ago

So what is actually happening here is that withBlock is actor isolated to its isolated parameter. So that makes it so that block is also actor isolated to isolation. Then you are capturing block in the closure passed to withValue. That causes withValue to also be actor isolated to isolation. So value (which is a T) is thus actor isolated since its region is actor isolated since withValue takes actor isolated state.

sending requires that the value not be actor isolated, so this is a correct error.

mattmassicotte commented 2 months ago

Huh, so it is possible to make a function that both uses an isolated paramter and a block that returns a sending type?