Why does this code pass concurrency checking

Maybe this is a silly question, but why does this code compiles?

// main.swift
class A {
  var val = 0
  func increase() async {
    for _ in 0..<10 { 
      val += 1
    }
  }
}

await Task.detached {
  let a = A()

  // start 2 parallel tasks
  async let val1: () = Task.detached {
    await a.increase()     // <- my question on this line
  }.value
  async let val2: () = Task.detached {
    await a.increase()    // <- and on this line
  }.value

  await val
  await val2
}.value

In my understanding, Task.detached requires an @Sendable block, which can only capture Sendable objects. And A is clearly not Sendable.

The above code compiles with no warnings with the latest Xcode 15.3 (Swift 5.10), with "Strict Concurrency Checking" set to "Completed".
However, when run with thread sanitizer enabled, an (almost guaranteed) issue will arise.

Can someone enlighten me?

2 Likes

this is perhaps not a satisfying answer, but i think this is likely just a bug in the diagnostics for that compiler version that results in a false negative. additionally, i think it's probably somewhat related to the relevant logic being present in 'top-level' code (there are numerous known issues with the behavioral differences in top-level code). if you run a similar example with one of the recent nightly toolchains (like this) or wrap the example in a function (like this), then you get the expected warnings along the lines of:

warning: capture of 'a' with non-sendable type 'A' in 'async let' binding; this is an error in the Swift 6 language mode

1 Like

Really helpful, thanks.

Guess I would stay with nightly builds when I want to test features in the future.