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-7359] Generic Subscript Parameter Requires Conformance to Hashable, But Then Says It's Redundant #49907

Closed
swift-ci opened this issue Apr 5, 2018 · 7 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Apr 5, 2018

Previous ID SR-7359
Radar rdar://problem/39208960
Original Reporter benstockdesign (JIRA User)
Type Bug
Status Resolved
Resolution Done
Environment
  • OS: macOS 10.12.6 (16G1036)

  • Language: Swift 4.0

  • Editor: Xcode 9.2 (9C40b)

  • Base SDK: macOS 10.13

  • Swift Compiler Optimization Level: None

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: adce73713697b986a00300014df32a22

Issue Description:

When implementing a subscript with generics like so:

subscript <K, V>(key: Key<[K: V]>, nestedKey: K) -> V?

The following error occurs:

Type 'K' does not conform to protocol 'Hashable'

If one then tries to add Hashable conformance in the generic parameter list itself:

subscript <K : Hashable, V>(key: Key<[K: V]>, nestedKey: K) -> V?

or in a where clause:

subscript <K, V>(key: Key<[K: V]>, nestedKey: K) -> V? where K : Hashable

A warning appears:

Redundant conformance constraint 'K': 'Hashable'

In other words, if you add conformance, you get a redundancy warning, and if you don't add conformance, you get an error.

@belkadan
Copy link
Contributor

belkadan commented Apr 5, 2018

Oops, we fixed this for functions, but I guess we missed subscripts. Thanks, Ben! cc @DougGregor, @huonw

@swift-ci create

@swift-ci
Copy link
Collaborator Author

swift-ci commented Apr 5, 2018

Comment by Ben Stock (JIRA)

@belkadan No problem! I have ADD, so seeing that little yellow warning indicator in Xcode’s activity area ❓ has been driving me a little nuts. 😃

@huonw
Copy link
Mannequin

huonw mannequin commented Apr 5, 2018

I tried with

struct Key<T> {}

struct Thing1 {
    subscript<K, V>(_: Key<[K: V]>, _: K) -> V? { return nil }
}
struct Thing2 {
    subscript<K: Hashable, V>(_: Key<[K: V]>, _: K) -> V? { return nil }
}

And it seems to compile without a warning or error with a recent development compiler and with Xcode 9.3. Could you provide a little more context for the code?

@swift-ci
Copy link
Collaborator Author

swift-ci commented Apr 6, 2018

Comment by Ben Stock (JIRA)

@huonw

Update
I just tried your examples, and they both worked. I then tried to add the key and nestedKey parameter names, and I was back in my original pickle. 🙁

So, it looks like the parameter names or argument labels are causing some discrepancy. Weird. I’m not the most knowledgable about the inner workings of the compiler (or any compiler, for that matter), so I think I’ll let you professionals take a stab at it.


I’m basically creating a lightweight key-value store. The subscript causing the problems is defined in this store. Here is an overview of the types I’m working with:

  1. struct Cache
    The key-value store described above. The type simply wraps a dictionary of Any values, keyed by AnyCacheKey values. That brings us to:

  2. class AnyCacheKey
    This class is abstract in nature and has no type information associated with it. It simply serves as a base implementation of the following more “concrete” key type.

  3. class Cache.Key<T>
    As mentioned above, the Cache.Key class inherits from AnyCacheKey and serves as the primary means of accessing values in a Cache instance.

If none of that makes sense, here’s a slightly condensed version of the code:

public class AnyCacheKey : RawRepresentable, Hashable, Comparable {
    public let rawValue: String
    // ... 
    // Hashable and Comparable implementations 
    // ...
}

public struct Cache {

    // This is the concrete key class with the additional static type information.
    final public class Key<T> : AnyCacheKey {}

    // The underlying dictionary containing the cached values.
    private var storage: [AnyCacheKey: Any]
  
    // ...
    // Some other code
    // ...
  
    // Base getter and setter
    public subscript <T>(key: Key<T>) -> T? {
        get { return storage[key] as? T }
        set { storage[key] = newValue }
    }

    // The problem child
    public subscript <K, V>(key: Key<[K: V]>, _ nestedKey: K) -> V? where K : Hashable {
        get {
            return self[key]?[nestedKey]
        }
        set {
            var values = storage[key] as? [K: V] ?? [:]
            values[nestedKey] = newValue
            self[key] = values
        }
    }
}

@huonw
Copy link
Mannequin

huonw mannequin commented Apr 6, 2018

Thanks for the additional info! It's looking like this might have been fixed between Xcode 9.2 and 9.3, which is always nice: theoretically this issue should be fixed with just an update.

I tried both my example with argument names and your example after adding the required methods for it to compile (for future reference, it's really helpful if any examples can just be copied-pasted into a file to demonstrate your problem, to make sure we're both looking at exactly the same thing 🙂 ) and there wasn't an error, with or without the where K: Hashable:

struct Key<T> {}

struct Thing1 {
    subscript<K, V>(key: Key<[K: V]>, nestedKey: K) -> V? { return nil }
}
struct Thing2 {
    subscript<K: Hashable, V>(key: Key<[K: V]>, nestedKey: K) -> V? { return nil }
}

and also after updating your example to compile:

public class AnyCacheKey: RawRepresentable, Hashable, Comparable {
    public let rawValue: String = ""

    // added:
    public required init?(rawValue: String) {}
    public var hashValue: Int { return 0 }
    public static func ==(lhs: AnyCacheKey, rhs: AnyCacheKey) -> Bool {
        return false
    }
    public static func <(lhs: AnyCacheKey, rhs: AnyCacheKey) -> Bool {
        return false
    }
}

public struct Cache {

    // This is the concrete key class with the additional static type information.
    final public class Key<T> : AnyCacheKey {}

    // The underlying dictionary containing the cached values.
    private var storage: [AnyCacheKey: Any]
    
    // ...
    // Some other code
    // ...
    
    // Base getter and setter
    public subscript <T>(key: Key<T>) -> T? {
        get { return storage[key] as? T }
        set { storage[key] = newValue }
    }

    // The problem child
    public subscript <K, V>(key: Key<[K: V]>, _ nestedKey: K) -> V? where K : Hashable {
        get {
            return self[key]?[nestedKey]
        }
        set {
            var values = storage[key] as? [K: V] ?? [:]
            values[nestedKey] = newValue
            self[key] = values
        }
    }
}

@belkadan
Copy link
Contributor

That might just be Doug scoping down the "redundant" warning. We should add these as test cases in case we ever decide to ramp it back up.

@huonw
Copy link
Mannequin

huonw mannequin commented Jul 26, 2018

I believe this was fixed by 98e88bc

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
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. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

2 participants