diff --git a/stdlib/public/SDK/Foundation/Data.swift b/stdlib/public/SDK/Foundation/Data.swift index a6b0f7bf107..8b3e54f44e5 100644 --- a/stdlib/public/SDK/Foundation/Data.swift +++ b/stdlib/public/SDK/Foundation/Data.swift @@ -1827,27 +1827,11 @@ extension NSData : _HasCustomAnyHashableRepresentation { extension Data : Codable { public init(from decoder: Decoder) throws { - // FIXME: This is a hook for bypassing a conditional conformance implementation to apply a strategy (see SR-5206). Remove this once conditional conformance is available. - do { - let singleValueContainer = try decoder.singleValueContainer() - if let decoder = singleValueContainer as? _JSONDecoder { - switch decoder.options.dataDecodingStrategy { - case .deferredToData: - break /* fall back to default implementation below; this would recurse */ - - default: - // _JSONDecoder has a hook for Datas; this won't recurse since we're not going to defer back to Data in _JSONDecoder. - self = try singleValueContainer.decode(Data.self) - return - } - } - } catch { /* fall back to default implementation below */ } - var container = try decoder.unkeyedContainer() // It's more efficient to pre-allocate the buffer if we can. if let count = container.count { - self = Data(count: count) + self.init(count: count) // Loop only until count, not while !container.isAtEnd, in case count is underestimated (this is misbehavior) and we haven't allocated enough space. // We don't want to write past the end of what we allocated. @@ -1856,7 +1840,7 @@ extension Data : Codable { self[i] = byte } } else { - self = Data() + self.init() } while !container.isAtEnd { @@ -1866,21 +1850,6 @@ extension Data : Codable { } public func encode(to encoder: Encoder) throws { - // FIXME: This is a hook for bypassing a conditional conformance implementation to apply a strategy (see SR-5206). Remove this once conditional conformance is available. - // We are allowed to request this container as long as we don't encode anything through it when we need the unkeyed container below. - var singleValueContainer = encoder.singleValueContainer() - if let encoder = singleValueContainer as? _JSONEncoder { - switch encoder.options.dataEncodingStrategy { - case .deferredToData: - break /* fall back to default implementation below; this would recurse */ - - default: - // _JSONEncoder has a hook for Datas; this won't recurse since we're not going to defer back to Data in _JSONEncoder. - try singleValueContainer.encode(self) - return - } - } - var container = encoder.unkeyedContainer() // Since enumerateBytes does not rethrow, we need to catch the error, stow it away, and rethrow if we stopped. diff --git a/stdlib/public/SDK/Foundation/Date.swift b/stdlib/public/SDK/Foundation/Date.swift index 05dd9d92ff8..c179f3026dd 100644 --- a/stdlib/public/SDK/Foundation/Date.swift +++ b/stdlib/public/SDK/Foundation/Date.swift @@ -287,40 +287,13 @@ extension Date : CustomPlaygroundQuickLookable { extension Date : Codable { public init(from decoder: Decoder) throws { - // FIXME: This is a hook for bypassing a conditional conformance implementation to apply a strategy (see SR-5206). Remove this once conditional conformance is available. let container = try decoder.singleValueContainer() - if let decoder = container as? _JSONDecoder { - switch decoder.options.dateDecodingStrategy { - case .deferredToDate: - break /* fall back to default implementation below; this would recurse */ - - default: - // _JSONDecoder has a hook for Dates; this won't recurse since we're not going to defer back to Date in _JSONDecoder. - self = try container.decode(Date.self) - return - } - } - let timestamp = try container.decode(Double.self) - self = Date(timeIntervalSinceReferenceDate: timestamp) + self.init(timeIntervalSinceReferenceDate: timestamp) } public func encode(to encoder: Encoder) throws { - // FIXME: This is a hook for bypassing a conditional conformance implementation to apply a strategy (see SR-5206). Remove this once conditional conformance is available. - // We are allowed to request this container as long as we don't encode anything through it when we need the keyed container below. var container = encoder.singleValueContainer() - if let encoder = container as? _JSONEncoder { - switch encoder.options.dateEncodingStrategy { - case .deferredToDate: - break /* fall back to default implementation below; this would recurse */ - - default: - // _JSONEncoder has a hook for Dates; this won't recurse since we're not going to defer back to Date in _JSONEncoder. - try container.encode(self) - return - } - } - try container.encode(self.timeIntervalSinceReferenceDate) } } diff --git a/stdlib/public/SDK/Foundation/Decimal.swift b/stdlib/public/SDK/Foundation/Decimal.swift index f4a2a61a4d0..d2523b1ad35 100644 --- a/stdlib/public/SDK/Foundation/Decimal.swift +++ b/stdlib/public/SDK/Foundation/Decimal.swift @@ -478,17 +478,6 @@ extension Decimal : Codable { } public init(from decoder: Decoder) throws { - // FIXME: This is a hook for bypassing a conditional conformance implementation to apply a strategy (see SR-5206). Remove this once conditional conformance is available. - do { - // We are allowed to request this container as long as we don't decode anything through it when we need the keyed container below. - let singleValueContainer = try decoder.singleValueContainer() - if singleValueContainer is _JSONDecoder { - // _JSONDecoder has a hook for Decimals; this won't recurse since we're not going to defer to Decimal in _JSONDecoder. - self = try singleValueContainer.decode(Decimal.self) - return - } - } catch { /* Fall back to default implementation below. */ } - let container = try decoder.container(keyedBy: CodingKeys.self) let exponent = try container.decode(CInt.self, forKey: .exponent) let length = try container.decode(CUnsignedInt.self, forKey: .length) @@ -516,15 +505,6 @@ extension Decimal : Codable { } public func encode(to encoder: Encoder) throws { - // FIXME: This is a hook for bypassing a conditional conformance implementation to apply a strategy (see SR-5206). Remove this once conditional conformance is available. - // We are allowed to request this container as long as we don't encode anything through it when we need the keyed container below. - var singleValueContainer = encoder.singleValueContainer() - if singleValueContainer is _JSONEncoder { - // _JSONEncoder has a hook for Decimals; this won't recurse since we're not going to defer to Decimal in _JSONEncoder. - try singleValueContainer.encode(self) - return - } - var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(_exponent, forKey: .exponent) try container.encode(_length, forKey: .length) diff --git a/stdlib/public/SDK/Foundation/JSONEncoder.swift b/stdlib/public/SDK/Foundation/JSONEncoder.swift index 04a121b97ba..4393b394953 100644 --- a/stdlib/public/SDK/Foundation/JSONEncoder.swift +++ b/stdlib/public/SDK/Foundation/JSONEncoder.swift @@ -99,7 +99,7 @@ open class JSONEncoder { open var userInfo: [CodingUserInfoKey : Any] = [:] /// Options set on the top-level encoder to pass down the encoding hierarchy. - internal struct _Options { + fileprivate struct _Options { let dateEncodingStrategy: DateEncodingStrategy let dataEncodingStrategy: DataEncodingStrategy let nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy @@ -155,14 +155,14 @@ open class JSONEncoder { // MARK: - _JSONEncoder -internal class _JSONEncoder : Encoder { +fileprivate class _JSONEncoder : Encoder { // MARK: Properties /// The encoder's storage. fileprivate var storage: _JSONEncodingStorage /// Options set on the top-level encoder. - internal let options: JSONEncoder._Options + fileprivate let options: JSONEncoder._Options /// The path to the current point in encoding. public var codingPath: [CodingKey] @@ -827,7 +827,7 @@ open class JSONDecoder { open var userInfo: [CodingUserInfoKey : Any] = [:] /// Options set on the top-level encoder to pass down the decoding hierarchy. - internal struct _Options { + fileprivate struct _Options { let dateDecodingStrategy: DateDecodingStrategy let dataDecodingStrategy: DataDecodingStrategy let nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy @@ -871,14 +871,14 @@ open class JSONDecoder { // MARK: - _JSONDecoder -internal class _JSONDecoder : Decoder { +fileprivate class _JSONDecoder : Decoder { // MARK: Properties /// The decoder's storage. fileprivate var storage: _JSONDecodingStorage /// Options set on the top-level decoder. - internal let options: JSONDecoder._Options + fileprivate let options: JSONDecoder._Options /// The path to the current point in encoding. fileprivate(set) public var codingPath: [CodingKey] diff --git a/stdlib/public/SDK/Foundation/URL.swift b/stdlib/public/SDK/Foundation/URL.swift index 262efa70ac1..3799dbaa1c7 100644 --- a/stdlib/public/SDK/Foundation/URL.swift +++ b/stdlib/public/SDK/Foundation/URL.swift @@ -1214,17 +1214,6 @@ extension URL : Codable { } public init(from decoder: Decoder) throws { - // FIXME: This is a hook for bypassing a conditional conformance implementation to apply a strategy (see SR-5206). Remove this once conditional conformance is available. - do { - // We are allowed to request this container as long as we don't decode anything through it when we need the keyed container below. - let singleValueContainer = try decoder.singleValueContainer() - if singleValueContainer is _JSONDecoder { - // _JSONDecoder has a hook for URLs; this won't recurse since we're not going to defer back to URL in _JSONDecoder. - self = try singleValueContainer.decode(URL.self) - return - } - } catch { /* Fall back to default implementation below. */ } - let container = try decoder.container(keyedBy: CodingKeys.self) let relative = try container.decode(String.self, forKey: .relative) let base = try container.decodeIfPresent(URL.self, forKey: .base) @@ -1238,15 +1227,6 @@ extension URL : Codable { } public func encode(to encoder: Encoder) throws { - // FIXME: This is a hook for bypassing a conditional conformance implementation to apply a strategy (see SR-5206). Remove this once conditional conformance is available. - // We are allowed to request this container as long as we don't encode anything through it when we need the keyed container below. - var singleValueContainer = encoder.singleValueContainer() - if singleValueContainer is _JSONEncoder { - // _JSONEncoder has a hook for URLs; this won't recurse since we're not going to defer back to URL in _JSONEncoder. - try singleValueContainer.encode(self) - return - } - var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.relativeString, forKey: .relative) if let base = self.baseURL { diff --git a/stdlib/public/core/Codable.swift b/stdlib/public/core/Codable.swift index cc23d9f094f..d168a7d0ebe 100644 --- a/stdlib/public/core/Codable.swift +++ b/stdlib/public/core/Codable.swift @@ -3987,6 +3987,33 @@ fileprivate func assertTypeIsDecodable(_ type: T.Type, in wrappingType: Any.T } } +// Temporary resolution for SR-5206. +// +// The following two extension on Encodable and Decodable are used below to provide static type information where we don't have any yet. +// The wrapped contents of the below generic types have to expose their Encodable/Decodable conformance via an existential cast/their metatype. +// Since those are dynamic types without static type guarantees, we cannot call generic methods taking those arguments, e.g. +// +// try container.encode((someElement as! Encodable)) +// +// One way around this is to get elements to encode into `superEncoder`s and decode from `superDecoder`s because those interfaces are available via the existentials/metatypes. +// However, this direct encoding/decoding never gives containers a chance to intercept and do something custom on types. +// +// If we instead expose this custom private functionality of writing to/reading from containers directly, the containers do get this chance. + +// FIXME: Remove when conditional conformance is available. +extension Encodable { + fileprivate func encode(to container: inout SingleValueEncodingContainer) throws { try container.encode(self) } + fileprivate func encode(to container: inout UnkeyedEncodingContainer) throws { try container.encode(self) } + fileprivate func encode(to container: inout KeyedEncodingContainer, forKey key: Key) throws { try container.encode(self, forKey: key) } +} + +// FIXME: Remove when conditional conformance is available. +extension Decodable { + fileprivate init(from container: SingleValueDecodingContainer) throws { self = try container.decode(Self.self) } + fileprivate init(from container: inout UnkeyedDecodingContainer) throws { self = try container.decode(Self.self) } + fileprivate init(from container: KeyedDecodingContainer, forKey key: Key) throws { self = try container.decode(Self.self, forKey: key) } +} + // FIXME: Uncomment when conditional conformance is available. extension Optional : Encodable /* where Wrapped : Encodable */ { public func encode(to encoder: Encoder) throws { @@ -3995,7 +4022,7 @@ extension Optional : Encodable /* where Wrapped : Encodable */ { var container = encoder.singleValueContainer() switch self { case .none: try container.encodeNil() - case .some(let wrapped): try (wrapped as! Encodable).encode(to: encoder) + case .some(let wrapped): try (wrapped as! Encodable).encode(to: &container) } } } @@ -4009,7 +4036,7 @@ extension Optional : Decodable /* where Wrapped : Decodable */ { let container = try decoder.singleValueContainer() if !container.decodeNil() { let metaType = (Wrapped.self as! Decodable.Type) - let element = try metaType.init(from: decoder) + let element = try metaType.init(from: container) self = .some(element as! Wrapped) } } @@ -4022,10 +4049,7 @@ extension Array : Encodable /* where Element : Encodable */ { var container = encoder.unkeyedContainer() for element in self { - // superEncoder appends an empty element and wraps an Encoder around it. - // This is normally appropriate for encoding super, but this is really what we want to do. - let subencoder = container.superEncoder() - try (element as! Encodable).encode(to: subencoder) + try (element as! Encodable).encode(to: &container) } } } @@ -4039,10 +4063,7 @@ extension Array : Decodable /* where Element : Decodable */ { let metaType = (Element.self as! Decodable.Type) var container = try decoder.unkeyedContainer() while !container.isAtEnd { - // superDecoder fetches the next element as a container and wraps a Decoder around it. - // This is normally appropriate for decoding super, but this is really what we want to do. - let subdecoder = try container.superDecoder() - let element = try metaType.init(from: subdecoder) + let element = try metaType.init(from: &container) self.append(element as! Element) } } @@ -4054,10 +4075,7 @@ extension Set : Encodable /* where Element : Encodable */ { var container = encoder.unkeyedContainer() for element in self { - // superEncoder appends an empty element and wraps an Encoder around it. - // This is normally appropriate for encoding super, but this is really what we want to do. - let subencoder = container.superEncoder() - try (element as! Encodable).encode(to: subencoder) + try (element as! Encodable).encode(to: &container) } } } @@ -4071,10 +4089,7 @@ extension Set : Decodable /* where Element : Decodable */ { let metaType = (Element.self as! Decodable.Type) var container = try decoder.unkeyedContainer() while !container.isAtEnd { - // superDecoder fetches the next element as a container and wraps a Decoder around it. - // This is normally appropriate for decoding super, but this is really what we want to do. - let subdecoder = try container.superDecoder() - let element = try metaType.init(from: subdecoder) + let element = try metaType.init(from: &container) self.insert(element as! Element) } } @@ -4106,29 +4121,22 @@ extension Dictionary : Encodable /* where Key : Encodable, Value : Encodable */ var container = encoder.container(keyedBy: _DictionaryCodingKey.self) for (key, value) in self { let codingKey = _DictionaryCodingKey(stringValue: key as! String)! - let valueEncoder = container.superEncoder(forKey: codingKey) - try (value as! Encodable).encode(to: valueEncoder) + try (value as! Encodable).encode(to: &container, forKey: codingKey) } } else if Key.self == Int.self { // Since the keys are already Ints, we can use them as keys directly. var container = encoder.container(keyedBy: _DictionaryCodingKey.self) for (key, value) in self { let codingKey = _DictionaryCodingKey(intValue: key as! Int)! - let valueEncoder = container.superEncoder(forKey: codingKey) - try (value as! Encodable).encode(to: valueEncoder) + try (value as! Encodable).encode(to: &container, forKey: codingKey) } } else { // Keys are Encodable but not Strings or Ints, so we cannot arbitrarily convert to keys. // We can encode as an array of alternating key-value pairs, though. var container = encoder.unkeyedContainer() for (key, value) in self { - // superEncoder appends an empty element and wraps an Encoder around it. - // This is normally appropriate for encoding super, but this is really what we want to do. - let keyEncoder = container.superEncoder() - try (key as! Encodable).encode(to: keyEncoder) - - let valueEncoder = container.superEncoder() - try (value as! Encodable).encode(to: valueEncoder) + try (key as! Encodable).encode(to: &container) + try (value as! Encodable).encode(to: &container) } } } @@ -4146,8 +4154,7 @@ extension Dictionary : Decodable /* where Key : Decodable, Value : Decodable */ let container = try decoder.container(keyedBy: _DictionaryCodingKey.self) let valueMetaType = Value.self as! Decodable.Type for key in container.allKeys { - let valueDecoder = try container.superDecoder(forKey: key) - let value = try valueMetaType.init(from: valueDecoder) + let value = try valueMetaType.init(from: container, forKey: key) self[key.stringValue as! Key] = (value as! Value) } } else if Key.self == Int.self { @@ -4166,8 +4173,7 @@ extension Dictionary : Decodable /* where Key : Decodable, Value : Decodable */ debugDescription: "Expected Int key but found String key instead.")) } - let valueDecoder = try container.superDecoder(forKey: key) - let value = try valueMetaType.init(from: valueDecoder) + let value = try valueMetaType.init(from: container, forKey: key) self[key.intValue! as! Key] = (value as! Value) } } else { @@ -4185,19 +4191,14 @@ extension Dictionary : Decodable /* where Key : Decodable, Value : Decodable */ let keyMetaType = (Key.self as! Decodable.Type) let valueMetaType = (Value.self as! Decodable.Type) while !container.isAtEnd { - // superDecoder fetches the next element as a container and wraps a Decoder around it. - // This is normally appropriate for decoding super, but this is really what we want to do. - let keyDecoder = try container.superDecoder() - let key = try keyMetaType.init(from: keyDecoder) + let key = try keyMetaType.init(from: &container) guard !container.isAtEnd else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unkeyed container reached end before value in key-value pair.")) } - let valueDecoder = try container.superDecoder() - let value = try valueMetaType.init(from: valueDecoder) - + let value = try valueMetaType.init(from: &container) self[key as! Key] = (value as! Value) } }