SE-0302 defines a few compiler checking rules, first 2 being:
[....] . Any captures must also conform to Sendable .
Closures that have @Sendable function type can only use by-value captures. Captures of immutable values introduced by let are implicitly by-value
In result the code below is allowed
class C {
var i = 0
}
let c = C()
var i = 0
for _ in 1...1000 {
// Task's operation is defined as @Sendable @escaping () async -> Success
Task {
//i = i + 1 //ERROR: referencing and mutating not allowed
c.i = c.i + 1 // OK!
}
}
which is quite basic example of race condition.
So it seems like it's not allowed to capture reference to variable i (that's OK), but it's perfectly allowed to capture constant value c, being reference to not Sendable class C (the fact, compiler is aware of). It seems quite strange to me.
Shouldn't such potential problems be captured by compiler as errors (or at least warnings)?
I'm not freqent reader of this forum, so sorry if I'm repeaiting topic similiar to other(s) already discussed which I couldn't find. And yes, I know that using actor with incrementer instead of class would solve the problem, but the threat of using class may not be always as visible as in this example.
Full Sendable checking is not enabled in the Swift 5.5 compiler. If you want better diagnostics, trying the new Xcode 13.3 beta with Swift 5.6, or one of the 5.6 or main toolchains, which have much more checking enabled by default.
I'll try it out, thx. I'm still not sure what to expect - is it an error at all?. Reading SE-0302 literally it seems like let constant should be accepted by @Sendable clousure ( values introduced by let are implicitly by-value).
In Swift 6 this is likely to be an error, in Swift 5.7 and a recent Xcode 14 beta build this should emit a soft-error aka. warning. Generally speaking you cannot capture a non-sendable instance into a sendable context (this also applies to an init on an actor type).
When I modify your code to embed inside a function f1 then I can see the warnings
func f1() {
let c = C()
var i = 0
for _ in 1...1000 {
// Task's operation is defined as @Sendable @escaping () async -> Success
Task {
i = i + 1 //Warning: Reference to captured var 'i' in concurrently-executing code; this is an error in Swift 6
c.i = c.i + 1 //Warning: Capture of 'c' with non-sendable type 'C' in a `@Sendable` closure
}
}
}
Environment:
Xcode: Version 14.0 beta 3 (14A5270f)
macOS: 13.0 Beta (22A5295i)
Swift: version 5.7 (swiftlang-5.7.0.120.1 clang-1400.0.28.1)