Skip to content

Swift 6 data race false negative diagnostic #85330

@nikitabobko

Description

@nikitabobko

Description

Hi, together with @InversionSpaces we were stress testing the compiler and found the following data race that wasn't caught by the Swift compiler.

The following program has a data race. The counter is incremented two times concurrently, and thus the program is expected to run indefinitely printing 2, but at some point the program prints 1

Reproduction

class NonSendable {
    var value: Int = 0

    @discardableResult
    func inc() -> Int {
        value += 1
        return value
    }
}

actor MyActor {
    var field = NonSendable()
    func useValue(_ value: sending NonSendable) -> Task<Int, Never> {
        Task { value.inc() }
    }
}

func closureThatCapturesActorIsolatedStateTransfersExample(y: isolated MyActor, x: sending NonSendable) async -> Int {
    let closure = {
        y.field.inc()
        return x.inc()
    }
    let task = await MyActor().useValue(x) // <-- We expect the compiler to prevent us passing `x` to another isolation
    let b = closure()
    let a = await task.result.get()
    return max(a, b)
}

while true {
    let x = NonSendable()
    let result = await closureThatCapturesActorIsolatedStateTransfersExample(y: MyActor(), x: x)
    print(result)
    if result != 2 {
        break
    }
}

Expected behavior

Compilation error on await MyActor().useValue(x) line

Environment

Apple Swift version 6.2.1 (swift-6.2.1-RELEASE)
Target: arm64-apple-macosx15.0
Build config: +assertions

Additional information

The example we found comes from us not understanding the third example in the evolution proposal, non-Sendable Closures section. https://github.com/swiftlang/swift-evolution/blob/c30efdf5145b95fb3b2d6a45d9d9f69d7e0dcaad/proposals/0414-region-based-isolation.md#captures

The proposal says that the example is unsafe while we believe that it is safe since everything is isolated to the same instance of MyActor. Our example is a slightly modified version where we extracted closureThatCapturesActorIsolatedStateTransfersExample function and used isolated parameter syntax instead of member function isolation syntax which leads the compiler to another extreme, and makes it think that everything is safe.

While we are still unsure about the original example from the evolution proposal (thus not raising any ticket yet), we are pretty confident that the problem we report in this GitHub issue is a bug.

Metadata

Metadata

Assignees

Labels

bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.triage neededThis issue needs more specific labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions