swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.41k stars 10.34k forks source link

[SR-7057] Decoding typedef NSString #49605

Open krzyzanowskim opened 6 years ago

krzyzanowskim commented 6 years ago
Previous ID SR-7057
Radar None
Original Reporter @krzyzanowskim
Type Bug
Environment Xcode 9.3 beta 3 (9Q117m)
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, ClangImporter, Codable | |Assignee | @itaiferber | |Priority | Medium | md5: 9368c6c21550e8abd87dedbbbda09089

Issue Description:

The struct imported from the Objective-C the code:

typedef NSString *Foo NS_TYPED_EXTENSIBLE_ENUM;
Foo const BarKey;

After import to Swift the `Foo` type is not Codable. It wasn't enforced in Swift 4.0, but it is in Swift 4.1

This can't be used with decoder (implementing

    public typealias RawValue = [Foo: Any]

    public init(from decoder: Decoder) throws {
        var unkeyedContainer = try decoder.unkeyedContainer()
        self.init(rawValue: try unkeyedContainer.decode(RawValue.self))!
    }

// error: type 'Foo' does not conform to protocol 'Encodable'

this code worked in Swift 4.0.3. Either it didn't work properly in Swift 4.0, or it isn't possible to import Foo as Codable. I'm feel confused.

natecook1000 commented 6 years ago

I don't think you can redefine the RawValue associated type like that—Foo gets imported as RawRepresentable with String as its RawValue. The following code compiles for me:

extension Foo : Codable {
  public init(from decoder: Decoder) throws {
    let unkeyedContainer = try decoder.singleValueContainer()
    self.init(try unkeyedContainer.decode(RawValue.self))
  }

  public func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    try container.encode(self.rawValue)
  }
}
natecook1000 commented 6 years ago

You know what, I totally misread—you're talking about expecting Foo to be codable so that you can use it as the key in a codable dictionary.

That behavior has changed—in Swift 4.0 all dictionaries were "codable," but would fail at runtime if the keys weren't actually codable. In Swift 4.1 we have conditional conformance, so now only dictionaries with codable keys are themselves codable. It looks like that's what you're running into.

(It would make sense to me that enums imported from Objective-C would automatically be Codable, but I don't know if that's a regression from Swift 4.0 or not.)

krzyzanowskim commented 6 years ago

Yea, totaly. I thing it could be codable for basic types.

also it shouldn't compile to you too as there is Any type in the dictionary.

belkadan commented 6 years ago

Thanks, Marcin. The code you wrote can't have worked in Swift 4.0, because Any can't be decoded (even though that wasn't statically enforced). Can you find an example that actually did decode, so we know what we're looking for?

cc @itaiferber

belkadan commented 6 years ago

@itaiferber: Currently a very limited set of protocols are mirrored over on NS_TYPED_EXTENSIBLE_ENUM wrappers – Equatable, Hashable, and ObjectiveCBridgeable. Should Codable be included as well?

krzyzanowskim commented 6 years ago

@natecook1000 my fault, I extracted it from the code. right, for such type, I can't redefine the RawValue that way.

krzyzanowskim commented 6 years ago

@belkadan I guess it never worked at runtime, just compiles. It'd be nice if it actually works automatically for basic types.

there is another though

```
@protocol Pro \<NSObject>
@end
```

```
extension protocol Pro: Codable {}
```

the code compiles, but I believe it shouldn't unless there is some magic.

itaiferber commented 6 years ago

@belkadan Potentially — I could see this being useful. However, other RawRepresentable types don't automatically get Codable conformance, so that'd be a bit magic. It's also relatively easy to create NS_TYPED_EXTENSIBLE_ENUM values which couldn't be Codable, so we'd need to be wary of that.

itaiferber commented 6 years ago

@krzyzanowskim That code shouldn't compile — protocols can't be extended with conformance to other protocols. (Besides the syntax error of writing extension protocol.) Is that also in Swift 4.0.3? Doesn't work for me (correctly) in Swift 4.1

krzyzanowskim commented 6 years ago

@itaiferber after deleting DerivedData it doesn't compile anymore (ufff) and presents the syntax error. Sorry about that, I was very confused.

itaiferber commented 6 years ago

@krzyzanowskim That's alright — happens all the time. 🙂 In any case, about this issue specifically: I think we need to figure out a better way to make RawRepresentable things Codable by default. We have default implementations for primitive types, but you'd still have to write extension MyImportedEnum : Codable in order to benefit from it.