diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 181ab37..8ca86b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,4 +39,16 @@ jobs: uses: actions/checkout@v4 - name: Run Swift Macro Compatibility Check - uses: Matejkob/swift-macro-compatibility-check@v1 \ No newline at end of file + uses: Matejkob/swift-macro-compatibility-check@v1 + + swift6-mode: + name: Swift 6 Language Mode + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + + - name: Select Xcode 16.4 + run: sudo xcode-select -s /Applications/Xcode_16.4.app + + - name: Build in Swift 6 language mode + run: swift build -Xswiftc -swift-version -Xswiftc 6 \ No newline at end of file diff --git a/Sources/KarrotCodableKit/BetterCodable/DateValue/Strategy/ISO8601WithFractionalSecondsStrategy.swift b/Sources/KarrotCodableKit/BetterCodable/DateValue/Strategy/ISO8601WithFractionalSecondsStrategy.swift index d243220..127788f 100644 --- a/Sources/KarrotCodableKit/BetterCodable/DateValue/Strategy/ISO8601WithFractionalSecondsStrategy.swift +++ b/Sources/KarrotCodableKit/BetterCodable/DateValue/Strategy/ISO8601WithFractionalSecondsStrategy.swift @@ -16,7 +16,9 @@ import Foundation /// representing 39 minutes and 57 seconds after the 16th hour of December 19th, 1996 with an offset of -08:00 from UTC /// (Pacific Standard Time). public struct ISO8601WithFractionalSecondsStrategy: DateValueCodableStrategy { - private static let formatter: ISO8601DateFormatter = { + // `ISO8601DateFormatter` is configured once and never mutated, so concurrent reads are safe. + // `nonisolated(unsafe)` acknowledges this until the SDK annotates the type `Sendable`. + private nonisolated(unsafe) static let formatter: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] return formatter diff --git a/Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift b/Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift index 893917f..97187e3 100644 --- a/Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift +++ b/Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift @@ -101,7 +101,8 @@ extension LosslessValueCodable: Hashable where Strategy.Value: Hashable { } } -extension LosslessValueCodable: Sendable where Strategy.Value: Sendable {} +// `type` is an immutable metatype (no shared mutable state); the where-clause keeps `wrappedValue` safe. +extension LosslessValueCodable: @unchecked Sendable where Strategy.Value: Sendable {} public struct LosslessDefaultStrategy: LosslessDecodingStrategy { public static var losslessDecodableTypes: [(Decoder) -> LosslessStringCodable?] { diff --git a/Sources/KarrotCodableKit/BetterCodable/LosslessValue/OptionalLosslessValue.swift b/Sources/KarrotCodableKit/BetterCodable/LosslessValue/OptionalLosslessValue.swift index 4bbff1f..6527515 100644 --- a/Sources/KarrotCodableKit/BetterCodable/LosslessValue/OptionalLosslessValue.swift +++ b/Sources/KarrotCodableKit/BetterCodable/LosslessValue/OptionalLosslessValue.swift @@ -120,7 +120,8 @@ extension OptionalLosslessValueCodable: Hashable where Strategy.Value: Hashable } } -extension OptionalLosslessValueCodable: Sendable where Strategy.Value: Sendable {} +// `type` is an immutable metatype (no shared mutable state); the where-clause keeps `wrappedValue` safe. +extension OptionalLosslessValueCodable: @unchecked Sendable where Strategy.Value: Sendable {} /// Decodes Optional Codable values into their respective preferred types. /// diff --git a/Sources/KarrotCodableKit/PolymorphicCodable/Error/PolymorphicCodableError.swift b/Sources/KarrotCodableKit/PolymorphicCodable/Error/PolymorphicCodableError.swift index e93dfca..d65b77f 100644 --- a/Sources/KarrotCodableKit/PolymorphicCodable/Error/PolymorphicCodableError.swift +++ b/Sources/KarrotCodableKit/PolymorphicCodable/Error/PolymorphicCodableError.swift @@ -34,3 +34,6 @@ public enum PolymorphicCodableError: LocalizedError { } } } + +// Immutable error value; the `decoded` payload is only read for diagnostics. +extension PolymorphicCodableError: @unchecked Sendable {} diff --git a/Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift b/Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift index 214cba0..96803fe 100644 --- a/Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift +++ b/Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift @@ -40,3 +40,8 @@ extension ResilientDecodingOutcome { } } #endif + +#if DEBUG +// DEBUG-only immutable diagnostic holder; `results` is read-only after init. +extension ResilientDecodingOutcome.ArrayDecodingError: @unchecked Sendable {} +#endif diff --git a/Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift b/Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift index 3b9928f..48ef4ab 100644 --- a/Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift +++ b/Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift @@ -38,3 +38,8 @@ extension ResilientDecodingOutcome { } } #endif + +#if DEBUG +// DEBUG-only immutable diagnostic holder; `results` is read-only after init. +extension ResilientDecodingOutcome.DictionaryDecodingError: @unchecked Sendable {} +#endif diff --git a/Sources/KarrotCodableKit/Resilient/ErrorReporting.swift b/Sources/KarrotCodableKit/Resilient/ErrorReporting.swift index 580decb..eb34ee7 100644 --- a/Sources/KarrotCodableKit/Resilient/ErrorReporting.swift +++ b/Sources/KarrotCodableKit/Resilient/ErrorReporting.swift @@ -236,3 +236,6 @@ public struct UnknownNovelValueError: Error { self.novelValue = novelValue } } + +// Immutable; `novelValue` is captured once and only read for diagnostics. +extension UnknownNovelValueError: @unchecked Sendable {} diff --git a/Sources/KarrotCodableKitMacros/UnnestedPolymorphicCodableMacro/UnnestedPolymorphicCodableMacro.swift b/Sources/KarrotCodableKitMacros/UnnestedPolymorphicCodableMacro/UnnestedPolymorphicCodableMacro.swift index e05f2d3..3efa1f4 100644 --- a/Sources/KarrotCodableKitMacros/UnnestedPolymorphicCodableMacro/UnnestedPolymorphicCodableMacro.swift +++ b/Sources/KarrotCodableKitMacros/UnnestedPolymorphicCodableMacro/UnnestedPolymorphicCodableMacro.swift @@ -12,8 +12,8 @@ import SwiftSyntaxBuilder import SwiftSyntaxMacros public enum UnnestedPolymorphicCodableMacro: MemberMacro, UnnestedPolymorphicMacroType { - public static let protocolType = PolymorphicExtensionFactory.PolymorphicProtocolType.codable - public static let macroType = UnnestedPolymorphicCodeGenerator.MacroType.codable + public nonisolated(unsafe) static let protocolType = PolymorphicExtensionFactory.PolymorphicProtocolType.codable + public nonisolated(unsafe) static let macroType = UnnestedPolymorphicCodeGenerator.MacroType.codable public static let macroName = "UnnestedPolymorphicCodable" public static func expansion( diff --git a/Sources/KarrotCodableKitMacros/UnnestedPolymorphicCodableMacro/UnnestedPolymorphicDecodableMacro.swift b/Sources/KarrotCodableKitMacros/UnnestedPolymorphicCodableMacro/UnnestedPolymorphicDecodableMacro.swift index edd0655..6df11a4 100644 --- a/Sources/KarrotCodableKitMacros/UnnestedPolymorphicCodableMacro/UnnestedPolymorphicDecodableMacro.swift +++ b/Sources/KarrotCodableKitMacros/UnnestedPolymorphicCodableMacro/UnnestedPolymorphicDecodableMacro.swift @@ -12,8 +12,8 @@ import SwiftSyntaxBuilder import SwiftSyntaxMacros public enum UnnestedPolymorphicDecodableMacro: MemberMacro, UnnestedPolymorphicMacroType { - public static let protocolType = PolymorphicExtensionFactory.PolymorphicProtocolType.decodable - public static let macroType = UnnestedPolymorphicCodeGenerator.MacroType.decodable + public nonisolated(unsafe) static let protocolType = PolymorphicExtensionFactory.PolymorphicProtocolType.decodable + public nonisolated(unsafe) static let macroType = UnnestedPolymorphicCodeGenerator.MacroType.decodable public static let macroName = "UnnestedPolymorphicDecodable" public static func expansion(