diff --git a/Tests/Swift6Migration/LanguageMode5.xcconfig b/Tests/Swift6Migration/LanguageMode5.xcconfig new file mode 100644 index 0000000000..4556c2d2af --- /dev/null +++ b/Tests/Swift6Migration/LanguageMode5.xcconfig @@ -0,0 +1,5 @@ +// Swift 5 mode +SWIFT_VERSION = 5.0 + +// Recommended settings +SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES diff --git a/Tests/Swift6Migration/LanguageMode5StrictConcurrency.xcconfig b/Tests/Swift6Migration/LanguageMode5StrictConcurrency.xcconfig new file mode 100644 index 0000000000..d74feda27a --- /dev/null +++ b/Tests/Swift6Migration/LanguageMode5StrictConcurrency.xcconfig @@ -0,0 +1,6 @@ +// Swift 5 mode with strict concurrency checkings +SWIFT_VERSION = 5.0 +SWIFT_STRICT_CONCURRENCY = complete + +// Recommended settings +SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES diff --git a/Tests/Swift6Migration/LanguageMode6.xcconfig b/Tests/Swift6Migration/LanguageMode6.xcconfig new file mode 100644 index 0000000000..3ce1f8ea29 --- /dev/null +++ b/Tests/Swift6Migration/LanguageMode6.xcconfig @@ -0,0 +1,5 @@ +// Swift 6 mode +SWIFT_VERSION = 6.0 + +// Recommended settings +// SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES diff --git a/Tests/Swift6Migration/Swift6Migration.xcodeproj/project.pbxproj b/Tests/Swift6Migration/Swift6Migration.xcodeproj/project.pbxproj index 476f1a5449..31c7cd6bc4 100644 --- a/Tests/Swift6Migration/Swift6Migration.xcodeproj/project.pbxproj +++ b/Tests/Swift6Migration/Swift6Migration.xcodeproj/project.pbxproj @@ -8,10 +8,16 @@ /* Begin PBXBuildFile section */ 56CFC7D22CA03647000B5023 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = 56CFC7D12CA03647000B5023 /* GRDB */; }; + 56CFC7ED2CA06ADD000B5023 /* LanguageMode5.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 56CFC7EC2CA06ADD000B5023 /* LanguageMode5.xcconfig */; }; + 56CFC7F02CA06B7E000B5023 /* LanguageMode6.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 56CFC7EF2CA06B7E000B5023 /* LanguageMode6.xcconfig */; }; + 56CFC7F12CA06B7E000B5023 /* LanguageMode5StrictConcurrency.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 56CFC7EE2CA06B7E000B5023 /* LanguageMode5StrictConcurrency.xcconfig */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 56CFC7BF2CA035E7000B5023 /* Swift6Migration.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Swift6Migration.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 56CFC7EC2CA06ADD000B5023 /* LanguageMode5.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = LanguageMode5.xcconfig; sourceTree = ""; }; + 56CFC7EE2CA06B7E000B5023 /* LanguageMode5StrictConcurrency.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = LanguageMode5StrictConcurrency.xcconfig; sourceTree = ""; }; + 56CFC7EF2CA06B7E000B5023 /* LanguageMode6.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = LanguageMode6.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -37,6 +43,9 @@ 56CFC7B62CA035E7000B5023 = { isa = PBXGroup; children = ( + 56CFC7EC2CA06ADD000B5023 /* LanguageMode5.xcconfig */, + 56CFC7EE2CA06B7E000B5023 /* LanguageMode5StrictConcurrency.xcconfig */, + 56CFC7EF2CA06B7E000B5023 /* LanguageMode6.xcconfig */, 56CFC7C12CA035E7000B5023 /* Swift6Migration */, 56CFC7C02CA035E7000B5023 /* Products */, ); @@ -118,6 +127,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 56CFC7ED2CA06ADD000B5023 /* LanguageMode5.xcconfig in Resources */, + 56CFC7F02CA06B7E000B5023 /* LanguageMode6.xcconfig in Resources */, + 56CFC7F12CA06B7E000B5023 /* LanguageMode5StrictConcurrency.xcconfig in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -136,6 +148,7 @@ /* Begin XCBuildConfiguration section */ 56CFC7CB2CA035E8000B5023 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 56CFC7EF2CA06B7E000B5023 /* LanguageMode6.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -194,8 +207,6 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES; }; name = Debug; }; @@ -251,8 +262,6 @@ MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES; VALIDATE_PRODUCT = YES; }; name = Release; @@ -280,7 +289,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.github.groue.Swift6Migration; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -308,7 +316,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.github.groue.Swift6Migration; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; diff --git a/Tests/Swift6Migration/Swift6Migration/AsyncOverload.swift b/Tests/Swift6Migration/Swift6Migration/AsyncOverload.swift new file mode 100644 index 0000000000..5c366d2791 --- /dev/null +++ b/Tests/Swift6Migration/Swift6Migration/AsyncOverload.swift @@ -0,0 +1,30 @@ +import GRDB + +private struct Player: Codable, FetchableRecord, PersistableRecord { } +let writer: any DatabaseWriter = { fatalError() }() + +private func fetchPlayers() async throws -> [Player] { + try await writer.read(Player.fetchAll) +} + +private func foo() { + Task { + let players = try writer.read(Player.fetchAll) + } +} + +private struct PlayerRepository { + var writer: any DatabaseWriter + + func fetchPlayers() throws -> [Player] { + try writer.read(Player.fetchAll) + } +} + + +private func bar() { + let repository = try! PlayerRepository(writer: DatabaseQueue()) + Task { + let players = try repository.fetchPlayers() + } +} diff --git a/Tests/Swift6Migration/Swift6Migration/NonSendableConfiguration.swift b/Tests/Swift6Migration/Swift6Migration/NonSendableConfiguration.swift new file mode 100644 index 0000000000..668682b93e --- /dev/null +++ b/Tests/Swift6Migration/Swift6Migration/NonSendableConfiguration.swift @@ -0,0 +1,21 @@ +import GRDB + +private struct Player1: Codable { } +private struct Player2: Codable { } + +#if swift(<6) +extension Player1: FetchableRecord, MutablePersistableRecord { + // Static property 'databaseSelection' is not concurrency-safe + // because non-'Sendable' type '[any SQLSelectable]' + // may have shared mutable state + static let databaseSelection: [any SQLSelectable] = [ + Column("id"), Column("name"), Column("score") + ] +} +#endif + +extension Player2: FetchableRecord, MutablePersistableRecord { + static var databaseSelection: [any SQLSelectable] { + [Column("id"), Column("name"), Column("score")] + } +} diff --git a/Tests/Swift6Migration/Swift6Migration/NonSendableRecord.swift b/Tests/Swift6Migration/Swift6Migration/NonSendableRecord.swift new file mode 100644 index 0000000000..42ab943378 --- /dev/null +++ b/Tests/Swift6Migration/Swift6Migration/NonSendableRecord.swift @@ -0,0 +1,43 @@ +import GRDB + +private final class Player: Codable, Identifiable { + var id: Int64 + var name: String + var score: Int + + init(id: Int64, name: String, score: Int) { + self.id = id + self.name = name + self.score = score + } +} + +extension Player: FetchableRecord, PersistableRecord { } + +#if swift(<6) +private struct PlayerRepository { + var writer: any DatabaseWriter + + func fetch() async throws -> Player? { + // Type 'Player' does not conform to the 'Sendable' protocol + try await writer.read { db in + try Player.fetchOne(db, id: 42) + } + } + + func insert(_ player: Player) async throws { + // Capture of 'player' with non-sendable type 'Player' in a `@Sendable` closure + try await writer.read { db in + try player.insert(db) + } + } + + func observe() { + // Type 'Player' does not conform to the 'Sendable' protocol + let observation = ValueObservation.tracking { db in + try Player.fetchAll(db) + } + _ = observation + } +} +#endif