Jens
1
I'd like to understand why this program behaves as it does and why there are no concurrency-related warnings or errors. This is using the Swift 6.0 that comes with Xcode 16.0 Beta 4 (16A521). Any insights would be appreciated.
% swift --version
swift-driver version: 1.112.3 Apple Swift version 6.0 (swiftlang-6.0.0.6.8 clang-1600.0.23.1)
Target: arm64-apple-macosx14.0
% cat > test.swift
final class C {
func foo(_ label: String) {
enum S {
static var count = 0
}
print(label, "| count:", S.count)
S.count += 1
}
}
func test() async throws {
let c = C()
Task {
c.foo("A")
}
c.foo("B")
c.foo("C")
try await Task.sleep(for: .seconds(1))
}
for _ in 0..<4 {
try! await test()
print("--")
}
% swiftc test.swift && ./test
A | count: 0
B | count: 0
C | count: 2
--
B | count: 3
C | count: 4
A | count: 5
--
B | count: 6
C | count: 7
A | count: 6
--
B | count: 9
C | count: 10
A | count: 9
--
For reference, here's what happens when changing `class` to `actor`.
% cat > test.swift
final actor C {
func foo(_ label: String) {
enum S {
static var count = 0
}
print(label, "| count:", S.count)
S.count += 1
}
}
func test() async throws {
let c = C()
Task {
await c.foo("A")
}
await c.foo("B")
await c.foo("C")
try await Task.sleep(for: .seconds(1))
}
for _ in 0..<4 {
try! await test()
print("--")
}
% swiftc test.swift && ./test
B | count: 0
A | count: 1
C | count: 2
--
B | count: 3
A | count: 4
C | count: 5
--
B | count: 6
A | count: 7
C | count: 8
--
B | count: 9
A | count: 10
C | count: 11
--
1 Like
vns
2
You compile it in Swift 5 mode, not 6, and with any concurrency checks turned on whatsoever. If you run it with swiftc test.swift -swift-version 6 it won't compile, as it should.
2 Likes
Jens
3
Oh
, I assumed the Swift 6.0 compiler would use -swift-version 6 implicitly, since it's Swift 6.0 ...
1 Like
vns
4
-swift-version is a bit misguiding and there is an ongoing thread about its renaming. It is a mode, that guards source-breaking changes that come with 6th version, so that code can be gradually migrated, and even with latest compiler defaults to pre-6 behaviour when it comes to concurrency checks.
You can use SPM and run your test code as part of a package with swift-tools-version: 6.0 set in Package.swift, then it will compile everything in Swift 6 mode by default.
1 Like
Jens
5
I noticed that Xcode (16.0 Beta 4) sets the Swift Language Version build setting to Swift 5 for a newly created project.
And there's also e.g. these:
Has this always been the case, that you have to opt into breaking changes of Swift version X even when you are using Swift version X?
Will it be the same in Xcode 16.0 GM?
I assumed these flags were something we should use only while still using Swift 5, in order to prepare for the breaking changes in Swift 6.
vns
6
I can’t recall such major breaking changes, so that’s pretty much the first time I think. It's just that adopting new concurrency might be not easy for projects with full checks on. Having defaults to throw potentially hundreds of errors might be off-putting on adoption.
These feature options guard some of concurrency checks, so that project can turn them on gradually, adopting it step-by-step. And Xcode behaviour I'd expect to remain mostly the same as it in betas on that.
Here is a nice wwdc session on the topic of migration: Migrate your app to Swift 6 - WWDC24 - Videos - Apple Developer