Fix Database not resolving via import SQLiteData under whole-module builds#471
Fix Database not resolving via import SQLiteData under whole-module builds#471jpsim wants to merge 1 commit into
Conversation
… builds SQLiteData re-exports selected GRDB types via scoped imports in `Internal/Exports.swift` (e.g. `@_exported import class GRDB.Database`). 1.6.3 also added non-exported scoped imports of the same declaration (`public import class GRDB.Database`) in `CloudKit/PrimaryKeyMigration.swift` and `StructuredQueries+GRDB/CustomFunctions.swift`, both of which sort before `Internal/Exports.swift` in compilation order. Under whole-module compilation, swiftc dedupes scoped imports of the same declaration keeping the first occurrence in file order, which silently drops the `@_exported` flag. Downstream modules then fail to resolve `Database` via `import SQLiteData` with `cannot find type 'Database' in scope`. This only reproduces under WMO (`swift build -c release`); debug builds compile incrementally and are unaffected. Mark those scoped imports `@_exported` as well so the dedup keeps an exported entry regardless of file order. This is a no-op for consumers: `GRDB.Database` is already intentionally re-exported by `Internal/Exports.swift`. A whole-module `public import GRDB` is not an option because GRDB's `DatabaseFunction` class would collide with StructuredQueries' `DatabaseFunction` protocol used in `CustomFunctions.swift`.
|
Hey @jpsim, thanks for bringing this up. We didn't see this in our examples apps because they aren't WMO. Would an alternative solution be to rename Exports.swift to _Exports.swift to force it to be considered first? |
Well, we would just need to prefix |
This seems better than renaming the file and hoping it gets considered first. |
What
Two scoped imports of
GRDB.Databaseare written aspublic importinstead of@_exported import. Under whole-module compilation this causesDatabaseto stop resolving viaimport SQLiteDatain downstream modules:Why it broke
Internal/Exports.swiftintentionally re-exports selected GRDB types via scoped imports, e.g.:#467 ("Modernize package.", first released in 1.6.3) adopted
InternalImportsByDefaultand added explicit access-level imports across the package, including two non-exported scoped imports of the same declaration:Sources/SQLiteData/CloudKit/PrimaryKeyMigration.swiftSources/SQLiteData/StructuredQueries+GRDB/CustomFunctions.swiftBoth files sort before
Internal/Exports.swiftin compilation order.Under whole-module compilation, swiftc dedupes scoped imports of the same
(module, access-path)pair, keeping the first occurrence in file order. The non-exported import wins, so the@_exportedflag is silently dropped from the serialized module. Sibling re-exports that are only scoped-imported once (Configuration,DatabasePool,DatabaseQueue, …) keep working.This only reproduces under WMO —
swift build -c release, or any build system that compiles library targets with-wmo. Debug builds compile incrementally (non-WMO), which is presumably why it wasn't caught.The fix
Mark the two colliding scoped imports
@_exportedas well, so dedup keeps an exported entry regardless of file order. This is a no-op for consumers:GRDB.Databaseis already intentionally re-exported byInternal/Exports.swift.A whole-module
public import GRDBis not an option inCustomFunctions.swift: GRDB'sDatabaseFunctionclass then collides with StructuredQueries'DatabaseFunctionprotocol used in that file (value of type 'some DatabaseFunction' has no member 'name').Notes
The underlying swiftc behavior (WMO scoped-import dedup dropping
@_exportedbased on file order) looks like a compiler bug worth a separate issue against swiftlang/swift; this PR works around it at the source level.