- 
                Notifications
    You must be signed in to change notification settings 
- Fork 10.6k
Description
| Previous ID | SR-2620 | 
| Radar | rdar://problem/28281488 | 
| Original Reporter | @itaiferber | 
| Type | Bug | 
Environment
Running a recent internal build of macOS Sierra, with a recent internal build of Xcode, with a pre-built toolchain from swift.org (built 2016-09-06).
Additional Detail from JIRA
| Votes | 0 | 
| Component/s | Compiler | 
| Labels | Bug, Runtime | 
| Assignee | None | 
| Priority | Medium | 
md5: cec9ad937be41dd8fe2810f062273465
relates to:
- SR-14064 Accessing Static Member on Downcast Type not Calling Overridden Implementation
Issue Description:
In my current project, I am processing data by mapping keys and values in a dictionary; the keys and values I get are type-erased as AnyHashable and Any, respectively, and I need to check if the types passed in adopt a protocol A, adopt a more specific, fileprivate protocol D (which inherits from A), or neither.
Checking this should be relatively easy — conditionally downcast to D; if that fails, try to downcast to A; if that fails, then the value conforms to neither protocol.
However, downcasting from the AnyHashable key behaves differently than the Any value; even when the key is a D, the downcast to D fails, and the downcast to A succeeds. The value, when a D, always downcasts correctly. However, the key does successfully downcast to a D if first cast to an A.
The code below shows a simplified example of the problem (the code prints "A"):
// Functionality to do A
protocol A {
    func doA() throws
}
// Functionality to do B
protocol B {
    static func doB() throws -> Self?
}
// Functionality to do both A and B
protocol C: A, B {}
// A marker + hidden implementation for specific types.
fileprivate protocol D: C {}
extension D {
    func doA() throws {
        fatalError()
    }
    
    static func doB() throws -> Self? {
        fatalError()
    }
}
// Specific types which support this behavior.
extension String: D {}
// Data is exposed as AnyHashable (in reality, as a key in a dictionary)
let s = "Hello, world!" as AnyHashable
// Direct conditional cast from String → D fails
// Conditional cast from String → A succeeds
if let d = s as? D {
    print("D!")
} else if let a = s as? A {
    print("A!")
}
// Conditional case from String → D through A succeeds
if let d = (s as? A) as? D {
    print("D!")
}
If the declaration of s is changed to
let s = "Hello, world!" as Any
then "D!" is printed twice.
If necessary, I can file as a Radar with additional internal information and attach a URL.