[Solved] "swift build" reports concurrency error but "swift <file>" doesn't

While trying a simple piece of code to verify my understanding of concurrency in Swift 6, I find weird behaviors:

  • If I put the code in a swift package and run swift build, it reports concurrency error.
  • If I run swift <file> directly, it doesn't output any error.
  • I also paste the code into godblot and it compiles too.

Below are the details. I'd appreciate if anyone can clarify why the difference.

My environment: Ubuntu 22.04, Swift 6.0.3 (the same behavior can be reproduced on my MBP which has Swift 6.0.1)

 # lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 22.04.4 LTS
Release:	22.04
Codename:	jammy

 # swift --version
Swift version 6.0.3 (swift-6.0.3-RELEASE)
Target: x86_64-unknown-linux-gnu

Below I create a new package, put the code in it and run swift build. It reports concurrency error.

 # mkdir demo

 # cd demo

 # swift package init
Creating library package: demo
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/demo/demo.swift
Creating Tests/
Creating Tests/demoTests/
Creating Tests/demoTests/demoTests.swift

 # cat > Sources/demo/demo.swift
class NonSendable {
    var bool: Bool = .random()
}

func makeNonSendable() async -> NonSendable {
    NonSendable()
}

actor A1 {
    var bool: Bool?

    func foo() async {
        let ns = await makeNonSendable()
        bool = ns.bool
    }
}

 # swift build
Building for debugging...
/var/tmp/sandbox/swift/demo/Sources/demo/demo.swift:13:24: error: non-sendable type 'NonSendable' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
 1 | class NonSendable {
   |       `- note: class 'NonSendable' does not conform to the 'Sendable' protocol
 2 |     var bool: Bool = .random()
 3 | }
   :
11 | 
12 |     func foo() async {
13 |         let ns = await makeNonSendable()
   |                        `- error: non-sendable type 'NonSendable' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
14 |         bool = ns.bool
15 |     }

However, if I run swift <file> directly, it doesn't output any error.

 # swift Sources/demo/demo.swift 

 # echo $?
0

It also compiles on godbolt. See here. The compile options are:

-g -o /nosym/tmp/compiler-explorer-compiler202517-9622-j46sru.94gd/output.s -emit-assembly -Xllvm --x86-asm-syntax=intel -S /nosym/tmp/compiler-explorer-compiler202517-9622-j46sru.94gd/example.swift

In case it helps, I collected the commands invoked by swift build and swift <file>.

swift build verbose output:

 # swift build --verbose
warning: 'demo': /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swift-frontend -frontend -c -primary-file /var/tmp/sandbox/swift/demo/Package.swift -target x86_64-unknown-linux-gnu -disable-objc-interop -I /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/pm/ManifestAPI -vfsoverlay /tmp/TemporaryDirectory.0OYNwg/vfs.yaml -swift-version 6 -package-description-version 6.0.0 -empty-abi-descriptor -resource-dir /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift -module-name main -plugin-path /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/host/plugins -plugin-path /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/local/lib/swift/host/plugins -o /tmp/TemporaryDirectory.gFcP9W/Package-1.o
/var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swift-autolink-extract /tmp/TemporaryDirectory.gFcP9W/Package-1.o -o /tmp/TemporaryDirectory.gFcP9W/main-1.autolink
/var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/clang -pie -Xlinker -rpath -Xlinker /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/linux /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/linux/x86_64/swiftrt.o /tmp/TemporaryDirectory.gFcP9W/Package-1.o @/tmp/TemporaryDirectory.gFcP9W/main-1.autolink -L /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/linux -lswiftCore --target=x86_64-unknown-linux-gnu -v -L /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/pm/ManifestAPI -lPackageDescription -Xlinker -rpath -Xlinker /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/pm/ManifestAPI -o /tmp/TemporaryDirectory.PRWBcu/demo-manifest
Swift version 6.0.3 (swift-6.0.3-RELEASE)
Target: x86_64-unknown-linux-gnu
clang version 17.0.0 (https://github.com/swiftlang/llvm-project.git 2e6139970eda445d9c6872c0ca293088b4e63dd2)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/11
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/11
Candidate multilib: .;@m64
Selected multilib: .;@m64
 "/usr/bin/ld.gold" -pie -z relro --hash-style=gnu --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o /tmp/TemporaryDirectory.PRWBcu/demo-manifest /lib/x86_64-linux-gnu/Scrt1.o /lib/x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o -L/var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/linux -L/var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/pm/ManifestAPI -L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib64 -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib64 -L/lib -L/usr/lib -rpath /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/linux /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/linux/x86_64/swiftrt.o /tmp/TemporaryDirectory.gFcP9W/Package-1.o -lswiftSwiftOnoneSupport -lswiftCore -lswift_Concurrency -lswift_StringProcessing -lswift_RegexParser -lswiftCore -lPackageDescription -rpath /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/pm/ManifestAPI -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o /lib/x86_64-linux-gnu/crtn.o
Building for debugging...
Write auxiliary file /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/swift-version-50A5C75A265EED51.txt
Write auxiliary file /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/demo.build/sources
/var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swiftc -module-name demo -emit-dependencies -emit-module -emit-module-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/Modules/demo.swiftmodule -output-file-map /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/demo.build/output-file-map.json -parse-as-library -incremental -c @/var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/demo.build/sources -I /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/Modules -target x86_64-unknown-linux-gnu -v -enable-batch-mode -index-store-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/index/store -Onone -enable-testing -j2 -DSWIFT_PACKAGE -DDEBUG -module-cache-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/ModuleCache -parseable-output -parse-as-library -color-diagnostics -swift-version 6 -g -Xcc -fPIC -Xcc -g -package-name demo -Xcc -fno-omit-frame-pointer
Swift version 6.0.3 (swift-6.0.3-RELEASE)
Target: x86_64-unknown-linux-gnu
/var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swift-frontend -frontend -emit-module -experimental-skip-non-inlinable-function-bodies-without-types /var/tmp/sandbox/swift/demo/Sources/demo/demo.swift -target x86_64-unknown-linux-gnu -disable-objc-interop -I /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/Modules -color-diagnostics -enable-testing -g -debug-info-format=dwarf -dwarf-version=4 -module-cache-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/ModuleCache -swift-version 6 -Onone -D SWIFT_PACKAGE -D DEBUG -empty-abi-descriptor -resource-dir /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift -enable-anonymous-context-mangled-names -file-compilation-dir /var/tmp/sandbox/swift/demo -Xcc -fPIC -Xcc -g -Xcc -fno-omit-frame-pointer -module-name demo -package-name demo -plugin-path /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/host/plugins -plugin-path /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/local/lib/swift/host/plugins -emit-module-doc-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/Modules/demo.swiftdoc -emit-module-source-info-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/Modules/demo.swiftsourceinfo -emit-dependencies-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/demo.build/demo.emit-module.d -parse-as-library -o /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/Modules/demo.swiftmodule
/var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swift-frontend -frontend -c -primary-file /var/tmp/sandbox/swift/demo/Sources/demo/demo.swift -emit-dependencies-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/demo.build/demo.d -emit-reference-dependencies-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/demo.build/demo.swiftdeps -target x86_64-unknown-linux-gnu -disable-objc-interop -I /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/Modules -color-diagnostics -enable-testing -g -debug-info-format=dwarf -dwarf-version=4 -module-cache-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/ModuleCache -swift-version 6 -Onone -D SWIFT_PACKAGE -D DEBUG -empty-abi-descriptor -resource-dir /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift -enable-anonymous-context-mangled-names -file-compilation-dir /var/tmp/sandbox/swift/demo -Xcc -fPIC -Xcc -g -Xcc -fno-omit-frame-pointer -module-name demo -package-name demo -plugin-path /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/host/plugins -plugin-path /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/local/lib/swift/host/plugins -parse-as-library -o /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/demo.build/demo.swift.o -index-store-path /var/tmp/sandbox/swift/demo/.build/x86_64-unknown-linux-gnu/debug/index/store -index-system-modules
/var/tmp/sandbox/swift/demo/Sources/demo/demo.swift:13:24: error: non-sendable type 'NonSendable' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
 1 | class NonSendable {
   |       `- note: class 'NonSendable' does not conform to the 'Sendable' protocol
 2 |     var bool: Bool = .random()
 3 | }
   :
11 | 
12 |     func foo() async {
13 |         let ns = await makeNonSendable()
   |                        `- error: non-sendable type 'NonSendable' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
14 |         bool = ns.bool
15 |     }

Processes invoked by swift Sources/demo/demo.swift (I collected them using a ebpf script):

pid: 8202, comm: bash, args: swift Sources/demo/demo.swift
pid: 8204, comm: swift, args: /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swift-frontend -frontend -print-target-info
pid: 8208, comm: swift, args: /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swift-frontend -frontend -print-target-info
pid: 8211, comm: swift, args: /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swift-frontend -frontend -emit-supported-features /tmp/TemporaryDirectory.uX6Evr/dummyInput-1.swift
pid: 8202, comm: swift, args: /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/bin/swift-frontend -frontend -interpret Sources/demo/demo.swift -disable-objc-interop -color-diagnostics -empty-abi-descriptor -resource-dir /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift -module-name demo -plugin-path /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/lib/swift/host/plugins -plugin-path /var/tmp/swift-6.0.3-RELEASE-ubuntu22.04/usr/local/lib/swift/host/plugins

(BTW, I wonder what does the -interpret flag do?)

There's a difference between compiler version and language version.

Even if you are using a version 6.x compiler, the default language mode is still Swift 5 (for compatibility reasons). You can for example pass -swift-version 6 to enable strict concurrency checking. Try that on godbolt and you can get the same error.

On the other hand, if you are using SwiftPM, the first line in Package.swift matters. For example, if the file generated by swift package init contains // swift-tools-version: 6.x, than the compiler will run under Swift 6 language mode.

3 Likes