Merge pull request #11315 from itaiferber/fix-sr-5206-hack
Remove previous hack for SR-5206
diff --git a/stdlib/public/SDK/Foundation/Data.swift b/stdlib/public/SDK/Foundation/Data.swift
index a6b0f7b..8b3e54f 100644
--- a/stdlib/public/SDK/Foundation/Data.swift
+++ b/stdlib/public/SDK/Foundation/Data.swift
@@ -1827,27 +1827,11 @@
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 @@
self[i] = byte
}
} else {
- self = Data()
+ self.init()
}
while !container.isAtEnd {
@@ -1866,21 +1850,6 @@
}
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 05dd9d9..c179f30 100644
--- a/stdlib/public/SDK/Foundation/Date.swift
+++ b/stdlib/public/SDK/Foundation/Date.swift
@@ -287,40 +287,13 @@
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 f4a2a61..d2523b1 100644
--- a/stdlib/public/SDK/Foundation/Decimal.swift
+++ b/stdlib/public/SDK/Foundation/Decimal.swift
@@ -478,17 +478,6 @@
}
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 @@
}
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 04a121b..4393b39 100644
--- a/stdlib/public/SDK/Foundation/JSONEncoder.swift
+++ b/stdlib/public/SDK/Foundation/JSONEncoder.swift
@@ -99,7 +99,7 @@
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 @@
// 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 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 @@
// 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 262efa7..3799dba 100644
--- a/stdlib/public/SDK/Foundation/URL.swift
+++ b/stdlib/public/SDK/Foundation/URL.swift
@@ -1214,17 +1214,6 @@
}
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 @@
}
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 cc23d9f..7af278a 100644
--- a/stdlib/public/core/Codable.swift
+++ b/stdlib/public/core/Codable.swift
@@ -3987,6 +3987,34 @@
}
}
+// 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<Key>(to container: inout KeyedEncodingContainer<Key>, forKey key: Key) throws { try container.encode(self, forKey: key) }
+}
+
+// FIXME: Remove when conditional conformance is available.
+extension Decodable {
+ // Since we cannot call these __init, we'll give the parameter a '__'.
+ 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<Key>(__from container: KeyedDecodingContainer<Key>, 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 +4023,7 @@
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 +4037,7 @@
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 +4050,7 @@
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 +4064,7 @@
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 +4076,7 @@
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 +4090,7 @@
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 +4122,22 @@
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 +4155,7 @@
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 +4174,7 @@
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 +4192,14 @@
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)
}
}