Skip to content

[SR-2620] Conditional Downcast from AnyHashable to [file]private Protocol Fails Unless Going Through Intermediate Internal/Public Protocol #45225

@itaiferber

Description

@itaiferber
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![](", then "D)"):

// 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itselfruntimeThe Swift Runtime

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions