ITADN

Swift 6.4 emits an unreadable module interface: module selector on a cross-module associated type

#91540Openjqsilver 创建于 8 天前
J
jqsilvercommented
### Description In Swift 6.4, the module interface printer qualifies an associated type with a **module selector naming the conforming type's module** rather than the module that declares the associated type. The resulting `.swiftinterface` cannot be compiled back by the same compiler that produced it. The printer gets the protocol right (`Proto::HasElement`) and the associated type wrong (`Client::Element`) on the very same declaration: ```swift public struct Bag<Element> : Proto::HasElement { public init() public func firstAssoc() -> Client::Bag<Element>.Client::Element? } ``` `Element` is declared in `Proto`, not `Client`, so reading the interface back fails: ``` Client.swiftinterface:12:60: error: 'Element' is not imported through module 'Client' Client.swiftinterface:1:1: error: failed to verify module interface of 'Client' due to the errors above; the textual interface may be broken by project issues or a compiler bug ``` This is a regression. Swift 6.3.2 prints `Client.Bag<Element>.Element?`, which reads back fine. ### Reproduction Two modules, eight lines, no dependencies, no package manifest. ```swift // Proto.swift — declares the associated type public protocol HasElement { associatedtype Element } ``` ```swift // Client.swift — conforms, and writes the return type as a member type path import Proto public struct Bag<Element>: HasElement { public init() {} public func firstAssoc() -> Self.Element? { nil } } ``` ```bash swiftc -swift-version 5 -enable-library-evolution -module-name Proto \ -emit-module -emit-module-path Proto.swiftmodule \ -emit-module-interface-path Proto.swiftinterface Proto.swift swiftc -swift-version 5 -enable-library-evolution -module-name Client -I . \ -emit-module -emit-module-path Client.swiftmodule \ -emit-module-interface-path Client.swiftinterface Client.swift ``` The second command fails in `verify-emitted-module-interface`. (On macOS, prefix both with `xcrun` so the host SDK is found.) **Both source conditions are required:** - The type must be written as a **member type path** (`Self.Element`). Writing the generic parameter directly (`-> Element?`) prints `Element?` and is fine. - The associated type must be declared in a **different module**. Moving `HasElement` into `Client` makes the printer emit `Client::Bag<Element>.Client::Element?`, which is then correct and reads back fine. Reproduces identically under `-swift-version 5` and `-swift-version 6`. ### `-enable-library-evolution` hides the bug rather than causing it Dropping `-enable-library-evolution` makes the build **succeed**, but the emitted interface still contains the bad selector — verification is simply not run. That interface is still unreadable: ```console $ grep firstAssoc Client.swiftinterface public func firstAssoc() -> Client::Bag<Element>.Client::Element? $ swift-frontend -typecheck-module-from-interface Client.swiftinterface -module-name Client -I . Client.swiftinterface:12:60: error: 'Element' is not imported through module 'Client' ``` So the printer defect is unconditional; library evolution only turns on the check that catches it. Anyone emitting interfaces without verification ships a `.swiftinterface` that consumers cannot compile against. ### Expected behavior The emitted interface should be readable by the compiler that produced it — either by attributing the associated type to its declaring module, or by omitting the selector in member type positions. ### Actual behavior The interface fails to typecheck with `'Element' is not imported through module 'Client'`, both during `verify-emitted-module-interface` and on any later read-back. ### Real-world impact This breaks library-evolution builds of [apple/swift-collections](https://github.com/apple/swift-collections). `OrderedSet.remove(at:)` is declared `-> Self.Element` (`OrderedSet+Partial RangeReplaceableCollection.swift:50`) and its `Element` comes from `Collection`/`SetAlgebra` in the `Swift` module, so it hits both conditions: ```swift @inlinable public mutating func remove(at index: Swift::Int) -> OrderedCollections::OrderedSet<Element>.OrderedCollections::Element { ``` Building any library with `BUILD_LIBRARY_FOR_DISTRIBUTION=YES` and `SWIFT_EMIT_MODULE_INTERFACE=YES` that depends on swift-collections `1.1.4` fails, with a cascade of overload-resolution errors in the `@inlinable` bodies the verifier re-parses (`extraneous argument label 'at:'`, `cannot convert value of type 'Int' to expected argument type 'Key'`). Deleting only that one qualifier from the emitted interface — `.OrderedCollections::Element` to `.Element`, leaving the other 384 module selectors in the file untouched, including `Swift::Int` on the same line — makes the whole file verify clean, and the cascading body errors disappear with it. Note the module itself compiles without error; only the interface is bad. That may explain how this escaped pre-release testing against swift-collections, since an ordinary build or test run never reads the interface back. ### Workaround `-disable-module-selectors-in-module-interface` restores the 6.3.2 spelling. It is accepted by 6.3.2 and 6.4 alike, so it needs no version gating. ### Environment | Toolchain | Printed type | Read-back | | --- | --- | --- | | Apple Swift 6.3.2 (Xcode 26.5) | `Client.Bag<Element>.Element?` | passes | | Apple Swift 6.4 (`swiftlang-6.4.0.30.4`) | `Client::Bag<Element>.Client::Element?` | **fails** | | Apple Swift 6.4 + `-disable-module-selectors-in-module-interface` | `Client.Bag<Element>.Element?` | passes | Host: macOS 26, arm64. Only Apple toolchains were available to test; I have not checked an open-source swift.org toolchain or Linux, so I cannot say whether this also affects those builds.
1 条评论