-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Description
The compiler isn't updating function declarations after incremental rebuilds leading to programs compiling when they shouldn't, ultimately resulting in linking failures.
First reported on the CMake bug tracker: https://gitlab.kitware.com/cmake/cmake/-/issues/27416
Reproducer
There are two libraries, A and B. A defines a function that returns an integer, which is called by B. A compiles and B compiles. Then we update A so that the function by the same name returns a float without updating B. This should fail to compile, but it doesn't.
Library A consists of one file, a.swift:
let number: Int = 32
public func callA() -> Int { number }Library B consists of one file, b.swift:
import A
public func callB() -> Int { callA() + 1 }I then rebuild Library A where I replace callA with a function that doesn't return.
public func callA() -> Void {}Rebuilding library B against this new interface should fail because callB expects callA to return an integer, but it doesn't, instead leading to a link-time failure because A.callA() -> Swift.Int doesn't exist.
This seems to fail consistently in the Swift 6.2.1 aarch64 Ubuntu Noble image.
$ docker run --rm -v <path to unpacked tar>:/repro -it swift:6.2-noble
# apt update && apt install zsh
# cd /repro
# ./driver.shLooking at the emitted textual interface, the function callA is seen to update from returning an Int to not returning anything:
+./driver.sh:5> SWIFTC=swiftc
+./driver.sh:7> ROOT=/repro
+./driver.sh:8> SOURCEROOT=/repro
+./driver.sh:9> OBJROOT=build
+./driver.sh:11> rm -fr build
+./driver.sh:12> mkdir -p build build/A.dir build/B.dir
+./driver.sh:16> perl -pe 's/\${PWD}/$ENV{PWD}/g' A-output-file-map.in.json
+./driver.sh:18> perl -pe 's/\${PWD}/$ENV{PWD}/g' B-output-file-map.in.json
+./driver.sh:21> pushd build/A.dir
+./driver.sh:22> swiftc -j 10 -num-threads 10 -c -module-cache-path /repro/build/module-cache -parse-as-library -static -emit-module -emit-module-path A.swiftmodule -emit-module-interface -module-name A -module-link-name A -incremental -output-file-map ofm.json /repro/a.swift
<unknown>:0: warning: module interfaces are only supported with -enable-library-evolution
<unknown>:0: warning: module interfaces are only supported with -enable-library-evolution
+./driver.sh:23> cat A.swiftinterface
// swift-interface-format-version: 1.0
// swift-compiler-version: Swift version 6.2.1 (swift-6.2.1-RELEASE)
// swift-module-flags: -target aarch64-unknown-linux-gnu -disable-objc-interop -module-link-name A -static -module-name A
// swift-module-flags-ignorable: -formal-cxx-interoperability-mode=off -interface-compiler-version 6.2.1
import Swift
import _Concurrency
import _StringProcessing
import _SwiftConcurrencyShims
public func callA() -> Swift.Int
+./driver.sh:24> popd
+./driver.sh:26> sha256sum build/A.dir/A.swiftmodule build/A.dir/a.swift.o
b55c8dc5cf1591a174e2da2718822d25f967d521f8178deb826c1c7f084d9a1f build/A.dir/A.swiftmodule
90e62312781fa0a78a484de3eb53509f6c6c6a7f70fb91c921ab7be471eabd98 build/A.dir/a.swift.o
+./driver.sh:29> pushd build/B.dir
+./driver.sh:30> swiftc -j 10 -num-threads 10 -c -module-cache-path /repro/build/module-cache -parse-as-library -static -emit-module -emit-module-path B.swiftmodule -module-name B -module-link-name B -incremental -output-file-map ofm.json /repro/b.swift -I /repro/build/A.dir
+./driver.sh:31> popd
+./driver.sh:33> sha256sum build/B.dir/B.swiftmodule build/B.dir/b.swift.o
287b153c93fb2a3f18f2e59ca0ea7d55eec8e0ea353d425d4f86e09ece5fdca5 build/B.dir/B.swiftmodule
9fa495a83f7361404ad57dc248e60732f978857b1f91e2a26c8fa0a0c2db7907 build/B.dir/b.swift.o
+./driver.sh:37> perl -pe 's/\${PWD}/$ENV{PWD}/g' A-new-output-file-map.in.json
+./driver.sh:39> pushd build/A.dir
+./driver.sh:40> swiftc -j 10 -num-threads 10 -c -module-cache-path /repro/build/module-cache -parse-as-library -static -emit-module -emit-module-path A.swiftmodule -emit-module-interface -module-name A -module-link-name A -incremental -output-file-map ofm.json /repro/a-new.swift
<unknown>:0: warning: module interfaces are only supported with -enable-library-evolution
<unknown>:0: warning: module interfaces are only supported with -enable-library-evolution
+./driver.sh:41> cat A.swiftinterface
// swift-interface-format-version: 1.0
// swift-compiler-version: Swift version 6.2.1 (swift-6.2.1-RELEASE)
// swift-module-flags: -target aarch64-unknown-linux-gnu -disable-objc-interop -module-link-name A -static -module-name A
// swift-module-flags-ignorable: -formal-cxx-interoperability-mode=off -interface-compiler-version 6.2.1
import Swift
import _Concurrency
import _StringProcessing
import _SwiftConcurrencyShims
public func callA()
+./driver.sh:42> popd
+./driver.sh:44> sha256sum build/A.dir/A.swiftmodule build/A.dir/a.swift.o
d9b1001782a21b042b9ecd1a1440458018bb592b88a33cdc384f5f72f6ea0042 build/A.dir/A.swiftmodule
c01e875369087a7b59b92d2101a765cd7edea7454bda47fcd55bc663c654414d build/A.dir/a.swift.o
+./driver.sh:47> pushd build/B.dir
+./driver.sh:48> swiftc -j 10 -num-threads 10 -c -module-cache-path /repro/build/module-cache -parse-as-library -static -emit-module -emit-module-path B.swiftmodule -module-name B -module-link-name B -incremental -output-file-map ofm.json /repro/b.swift -I /repro/build/A.dir
+./driver.sh:49> r=0
+./driver.sh:50> popd
+./driver.sh:53> [[ 0 -ne 0 ]]
+./driver.sh:57> sha256sum build/B.dir/B.swiftmodule build/B.dir/b.swift.o
287b153c93fb2a3f18f2e59ca0ea7d55eec8e0ea353d425d4f86e09ece5fdca5 build/B.dir/B.swiftmodule
9fa495a83f7361404ad57dc248e60732f978857b1f91e2a26c8fa0a0c2db7907 build/B.dir/b.swift.o
+./driver.sh:59> exit 1
I'm seeing the compiler work correctly and report the expected build failure 5% of the time (with swiftinterfaces enabled):
/repro/b.swift:2:30: error: cannot convert value of type 'Void' to expected argument type 'Int'
1 | import A
2 | public func callB() -> Int { callA() + 1 }
| `- error: cannot convert value of type 'Void' to expected argument type 'Int'
3 |
The expected behavior occurs less than that when only emitting binary swiftmodule files. Deleting the module-cache between each swiftc invocation appears to clear things up, so this seems like a module caching bug.
I've attached the reproducer files here: repro.tar.gz