Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-6899] Codable does not derive correctly for enums backed by non basic raw types #49448

Open
swift-ci opened this issue Feb 1, 2018 · 5 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. Codable Area → standard library: `Codable` and co. compiler The Swift compiler in itself

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Feb 1, 2018

Previous ID SR-6899
Radar rdar://problem/37135432
Original Reporter tmpz (JIRA User)
Type Bug
Environment

MacOS Sierra 10.12.6 (16G29)

Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
Target: x86_64-apple-macosx10.9

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, Codable
Assignee @itaiferber
Priority Medium

md5: 7c5d0c095962aac1ffbda000990ce04a

Issue Description:

The following code does not compile

import Foundation

public struct Bar: Codable {
    
    let value: String
    
    public init(bar: Bar) {
        
        self.value = bar.value
    }
}

extension Bar: ExpressibleByStringLiteral {
    
    public typealias StringLiteralType = String
    
    public init(unicodeScalarLiteral value: UnicodeScalar) {
        
        self.value = "\(value)"
    }
    
    public init(extendedGraphemeClusterLiteral value: String) {
        
        self.value = value
    }
    
    public init(stringLiteral value: String) {
        
        self.value = value
    }
}

extension Bar: Equatable { }

public func ==(lhs: Bar, rhs: Bar) -> Bool {
    
    return lhs.value.caseInsensitiveCompare(rhs.value) == ComparisonResult.orderedSame
}

public enum Foo: Bar, Codable {
    
    case foor = "Foo"
}

Generating 2 errors:

error: type 'Foo' does not conform to protocol 'Decodable'
public enum Foo: Bar, Codable {
            ^
error: type 'Foo' does not conform to protocol 'Encodable'
public enum Foo: Bar, Codable {

Change the RawValue of `Foo` to String, and the problem goes away.

Expected Result:
Codable is derived entirely.

Discovered along side with: https://bugs.swift.org/browse/SR-6897

@itaiferber
Copy link
Contributor

Looks like the issue are the following extensions that this is trying to match against:

extension RawRepresentable where RawValue == ${type}, Self : Encodable {
  /// Encodes this value into the given encoder.
  ///
  /// This function throws an error if any values are invalid for the given
  /// encoder's format.
  ///
  /// - Parameter encoder: The encoder to write data to.
  @_inlineable // FIXME(sil-serialize-all)
  public func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    try container.encode(self.rawValue)
  }
}

extension RawRepresentable where RawValue == ${type}, Self : Decodable {
  /// Creates a new instance by decoding from the given decoder.
  ///
  /// This initializer throws an error if reading from the decoder fails, or
  /// if the data read is corrupted or otherwise invalid.
  ///
  /// - Parameter decoder: The decoder to read data from.
  @_inlineable // FIXME(sil-serialize-all)
  public init(from decoder: Decoder) throws {
    let decoded = try decoder.singleValueContainer().decode(RawValue.self)
    guard let value = Self(rawValue: decoded) else {
      throw DecodingError.dataCorrupted(
        DecodingError.Context(
          codingPath: decoder.codingPath,
          debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)"))
    }

    self = value
  }
}

Now that we have conditional conformance, I think we can reasonably change this code to use that.

@itaiferber
Copy link
Contributor

@swift-ci Create

@itaiferber
Copy link
Contributor

Adding the following extension causes it to compile correctly:

extension RawRepresentable where RawValue == S {
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        guard let value = Self.init(rawValue: try container.decode(S.self)) else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unable to decode \(RawValue.self)")
        }
        
        self = value
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(self.rawValue)
    }
}

@swift-ci
Copy link
Collaborator Author

swift-ci commented Feb 2, 2018

Comment by Tommaso Piazza (JIRA)

@itaiferber Thumps up for conditional conformance. I'll add the `RawRepresentable` extension in the mean time.

Edit: You might want to change where RawValue == Bar in the comment but I get the gist of it.

@itaiferber
Copy link
Contributor

@belkadan correctly pointed out in Radar that we can't conform protocols to other protocols with conditional conformance, so we might need to find a way to improve the diagnostics here instead. Not sure if it'll be possible without special-casing this, but this bears investigation nonetheless.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. Codable Area → standard library: `Codable` and co. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

2 participants