Skip to content

Commit 3266477

Browse files
authored
Merge pull request swiftlang#10470 from itaiferber/codable-improved-runtime-diagnostics
Improve runtime errors for conditionally-Codable types
2 parents 53f68d7 + 41f93ca commit 3266477

File tree

1 file changed

+34
-44
lines changed

1 file changed

+34
-44
lines changed

stdlib/public/core/Codable.swift

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3233,15 +3233,33 @@ public extension RawRepresentable where RawValue == String, Self : Decodable {
32333233
}
32343234

32353235
//===----------------------------------------------------------------------===//
3236-
// Optional Conformance
3236+
// Optional/Collection Type Conformances
32373237
//===----------------------------------------------------------------------===//
32383238

3239+
fileprivate func assertTypeIsEncodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
3240+
guard T.self is Encodable.Type else {
3241+
if T.self == Encodable.self || T.self == Codable.self {
3242+
preconditionFailure("\(wrappingType) does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.")
3243+
} else {
3244+
preconditionFailure("\(wrappingType) does not conform to Encodable because \(T.self) does not conform to Encodable.")
3245+
}
3246+
}
3247+
}
3248+
3249+
fileprivate func assertTypeIsDecodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
3250+
guard T.self is Decodable.Type else {
3251+
if T.self == Decodable.self || T.self == Codable.self {
3252+
preconditionFailure("\(wrappingType) does not conform to Decodable because Decodable does not conform to itself. You must use a concrete type to encode or decode.")
3253+
} else {
3254+
preconditionFailure("\(wrappingType) does not conform to Decodable because \(T.self) does not conform to Decodable.")
3255+
}
3256+
}
3257+
}
3258+
32393259
// FIXME: Uncomment when conditional conformance is available.
32403260
extension Optional : Encodable /* where Wrapped : Encodable */ {
32413261
public func encode(to encoder: Encoder) throws {
3242-
guard Wrapped.self is Encodable.Type else {
3243-
preconditionFailure("\(type(of: self)) does not conform to Encodable because \(Wrapped.self) does not conform to Encodable.")
3244-
}
3262+
assertTypeIsEncodable(Wrapped.self, in: type(of: self))
32453263

32463264
var container = encoder.singleValueContainer()
32473265
switch self {
@@ -3253,12 +3271,9 @@ extension Optional : Encodable /* where Wrapped : Encodable */ {
32533271

32543272
extension Optional : Decodable /* where Wrapped : Decodable */ {
32553273
public init(from decoder: Decoder) throws {
3256-
// Initialize self here so we can print type(of: self).
3274+
// Initialize self here so we can get type(of: self).
32573275
self = .none
3258-
3259-
guard Wrapped.self is Decodable.Type else {
3260-
preconditionFailure("\(type(of: self)) does not conform to Decodable because \(Wrapped.self) does not conform to Decodable.")
3261-
}
3276+
assertTypeIsDecodable(Wrapped.self, in: type(of: self))
32623277

32633278
let container = try decoder.singleValueContainer()
32643279
if !container.decodeNil() {
@@ -3269,16 +3284,10 @@ extension Optional : Decodable /* where Wrapped : Decodable */ {
32693284
}
32703285
}
32713286

3272-
//===----------------------------------------------------------------------===//
3273-
// Collection Conformances
3274-
//===----------------------------------------------------------------------===//
3275-
32763287
// FIXME: Uncomment when conditional conformance is available.
32773288
extension Array : Encodable /* where Element : Encodable */ {
32783289
public func encode(to encoder: Encoder) throws {
3279-
guard Element.self is Encodable.Type else {
3280-
preconditionFailure("\(type(of: self)) does not conform to Encodable because \(Element.self) does not conform to Encodable.")
3281-
}
3290+
assertTypeIsEncodable(Element.self, in: type(of: self))
32823291

32833292
var container = encoder.unkeyedContainer()
32843293
for element in self {
@@ -3292,12 +3301,9 @@ extension Array : Encodable /* where Element : Encodable */ {
32923301

32933302
extension Array : Decodable /* where Element : Decodable */ {
32943303
public init(from decoder: Decoder) throws {
3295-
// Initialize self here so we can print type(of: self).
3304+
// Initialize self here so we can get type(of: self).
32963305
self.init()
3297-
3298-
guard Element.self is Decodable.Type else {
3299-
preconditionFailure("\(type(of: self)) does not conform to Decodable because \(Element.self) does not conform to Decodable.")
3300-
}
3306+
assertTypeIsDecodable(Element.self, in: type(of: self))
33013307

33023308
let metaType = (Element.self as! Decodable.Type)
33033309
var container = try decoder.unkeyedContainer()
@@ -3313,9 +3319,7 @@ extension Array : Decodable /* where Element : Decodable */ {
33133319

33143320
extension Set : Encodable /* where Element : Encodable */ {
33153321
public func encode(to encoder: Encoder) throws {
3316-
guard Element.self is Encodable.Type else {
3317-
preconditionFailure("\(type(of: self)) does not conform to Encodable because \(Element.self) does not conform to Encodable.")
3318-
}
3322+
assertTypeIsEncodable(Element.self, in: type(of: self))
33193323

33203324
var container = encoder.unkeyedContainer()
33213325
for element in self {
@@ -3329,12 +3333,9 @@ extension Set : Encodable /* where Element : Encodable */ {
33293333

33303334
extension Set : Decodable /* where Element : Decodable */ {
33313335
public init(from decoder: Decoder) throws {
3332-
// Initialize self here so we can print type(of: self).
3336+
// Initialize self here so we can get type(of: self).
33333337
self.init()
3334-
3335-
guard Element.self is Decodable.Type else {
3336-
preconditionFailure("\(type(of: self)) does not conform to Decodable because \(Element.self) does not conform to Decodable.")
3337-
}
3338+
assertTypeIsDecodable(Element.self, in: type(of: self))
33383339

33393340
let metaType = (Element.self as! Decodable.Type)
33403341
var container = try decoder.unkeyedContainer()
@@ -3366,13 +3367,8 @@ internal struct _DictionaryCodingKey : CodingKey {
33663367

33673368
extension Dictionary : Encodable /* where Key : Encodable, Value : Encodable */ {
33683369
public func encode(to encoder: Encoder) throws {
3369-
guard Key.self is Encodable.Type else {
3370-
preconditionFailure("\(type(of: self)) does not conform to Encodable because \(Key.self) does not conform to Encodable.")
3371-
}
3372-
3373-
guard Value.self is Encodable.Type else {
3374-
preconditionFailure("\(type(of: self)) does not conform to Encodable because \(Value.self) does not conform to Encodable.")
3375-
}
3370+
assertTypeIsEncodable(Key.self, in: type(of: self))
3371+
assertTypeIsEncodable(Value.self, in: type(of: self))
33763372

33773373
if Key.self == String.self {
33783374
// Since the keys are already Strings, we can use them as keys directly.
@@ -3411,14 +3407,8 @@ extension Dictionary : Decodable /* where Key : Decodable, Value : Decodable */
34113407
public init(from decoder: Decoder) throws {
34123408
// Initialize self here so we can print type(of: self).
34133409
self.init()
3414-
3415-
guard Key.self is Decodable.Type else {
3416-
preconditionFailure("\(type(of: self)) does not conform to Decodable because \(Key.self) does not conform to Decodable.")
3417-
}
3418-
3419-
guard Value.self is Decodable.Type else {
3420-
preconditionFailure("\(type(of: self)) does not conform to Decodable because \(Value.self) does not conform to Decodable.")
3421-
}
3410+
assertTypeIsDecodable(Key.self, in: type(of: self))
3411+
assertTypeIsDecodable(Value.self, in: type(of: self))
34223412

34233413
if Key.self == String.self {
34243414
// The keys are Strings, so we should be able to expect a keyed container.

0 commit comments

Comments
 (0)