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?)