Uploaded image for project: 'Swift'
  1. Swift
  2. SR-7031

Conditional Conformance of Codable on Collections in Swift 4.1 is source backwards breaking

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Priority: Medium
    • Resolution: Unresolved
    • Component/s: Compiler, Standard Library
    • Labels:
    • Environment:

      swift-4.1-DEVELOPMENT-SNAPSHOT-2018-02-01-a

      Description

      In Swift 4.0, using a Dictionary of type [String: Any] was allowed to be encoded since Dictionary and other collections conformed to Codable. assertTypeIsEncodable and assertTypeIsDecodable were used at runtime to generate a trap if the keys and values were indeed not conforming. Swift 4.1 removed these checks by utilizing Conditional Conformance (https://github.com/apple/swift/pull/13178/files#diff-b586e10e3c5fa7f2feb8cee6482b6e95L4324). While the correct move to make, this introduces a source backwards breaking change to code such as that included below.

      Reproduction Source:

      func regression<T>(val: T) where T: Encodable {
          print(type(of: val))
      }
      let dict: [String: Any] = ["hello": "world"]
      regression(val: dict)
      

      Swift 4.0 Output

      Dictionary<String, Any>
      

      Swift 4.1 Output

      error: MyPlayground.playground:41:1: error: type 'Any' does not conform to protocol 'Encodable'
      regression(val: dict)
      ^
      
      error: MyPlayground.playground:41:1: error: '<T where T : Encodable> (val: T) -> ()' requires that 'Any' conform to 'Encodable'
      regression(val: dict)
      

      This isn't strictly a bug per se. Any is truly not a Codable but I believe there is probably a considerable amount of 4.0 code in the wild exploiting the ability to use this and sometimes hit the runtime trap when the instance of Any is not a Codable. The blog post at https://swift.org/blog/conditional-conformance/ explicitly points this out "This approach also works for Codable. If you try and encode an array of non-codable types, you'll now get a compile-time error instead of the runtime trap you used to get."

      Given that is is a source backwards breaking change, was it intentional to make this a part of 4.1 and not 5.0 where source compatibility is allowed to break (assuming semantic versioning is followed in some sense)?

      In any case, should the Swift documentation for Codable be updated to point out that types like [Any] or [String: Any] will not work as Any does not conform to Encodable & Decodable? In addition to that, there could be some guidance about how to handle "generic" collections with Codable considering the above cannot be used as well as [Codable] or [String: Codable] since the use of a protocol as a concrete type is not allowed due to the protocol conforming to itself restriction.

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              edaniels Eric Daniels
            • Votes:
              2 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

              • Created:
                Updated: