Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,16 @@ jobs:
uses: actions/checkout@v4

- name: Run Swift Macro Compatibility Check
uses: Matejkob/swift-macro-compatibility-check@v1
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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value: LosslessStringCodable>: LosslessDecodingStrategy {
public static var losslessDecodableTypes: [(Decoder) -> LosslessStringCodable?] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ public enum PolymorphicCodableError: LocalizedError {
}
}
}

// Immutable error value; the `decoded` payload is only read for diagnostics.
extension PolymorphicCodableError: @unchecked Sendable {}
5 changes: 5 additions & 0 deletions Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions Sources/KarrotCodableKit/Resilient/ErrorReporting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down