swiftlang / swift

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

[SR-8339] Members declared in a conditional protocol extension are available on types not meeting constraints #50867

Open swift-ci opened 6 years ago

swift-ci commented 6 years ago
Previous ID SR-8339
Radar None
Original Reporter Anandabits (JIRA User)
Type Bug
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: c94304280ea88de749652ea896c67cf7

Issue Description:

I have several examples of this that are likely caused by the same (or very similar) problems (using the 7/17 nightly):

protocol P1 {
    associatedtype B: P2
}
protocol P2: P1 where B == Self {
    // isn't this redunant with the B == Self constraint?
    // if it is removed the code won't compile but it seems like it should be unnecessary
    associatedtype B = Self
}
protocol P3 {
    associatedtype T: P1
    associatedtype U
    var s: S<T, U> { get }
}
struct S<T: P1, U>: P1, P3 {
    typealias B = T.B
    var s: S<T, U> { return self }
    func f<F: P3>(_ type: F.Type) -> F where F.T == T, F.U == U {
        // the body doesn't matter
        fatalError()
    }
}
struct AP1: P1 { typealias B = AP2 }
struct AP2: P2 { typealias B = AP2 }
struct AP3: P3 {
    typealias T = AP1
    typealias U = String
    var s: S<T, U> { return S() }
}
extension P3 where T == T.B {
    func foo() -> String { return "foo" }
}
let ap3 = AP3()
print(ap3.foo()) // prints foo
print(AP3.T.self) // prints AP1
print(AP3.T.B.self) // prints AP2

In the above example, clearly `AP3.T != AP3.T.B` but the extension on `P3` that declares `foo` clearly requires them to be equal. `foo` should not be available on `AP3`. The same incorrect behavior is exhibited when the constraint is `T: P2` instead of `T == T.B`.

protocol P1 {
    associatedtype B: P2
}
protocol P2: P1 where B == Self {
    // isn't this redunant with the B == Self constraint?
    // if it is removed the code won't compile but it seems like it should be unnecessary
    associatedtype B = Self
}
protocol P3 {
    associatedtype T: P1
    associatedtype U
    var s: S<T, U> { get }
}
protocol P4: P3 where T: P3, T.B == T.T.B {
    typealias T2 = T.T
    typealias U2 = T.U
}
struct S<T: P1, U>: P1, P3 {
    typealias B = T.B
    var s: S<T, U> { return self }
    func f<F: P3>(_ type: F.Type) -> F where F.T == T, F.U == U {
        // the body doesn't matter
        fatalError()
    }
}
struct AP1: P1 { typealias B = AP2 }
struct AP2: P2 { typealias B = AP2 }
struct AP4: P4 {
    typealias T = S<S<AP2, String>, String>
    typealias U = String
    var s: S<T, U> { return S() }
}

extension P4 where T: P3, T2 == T.B {
    func foo() -> String { return "foo" }
}
let ap4 = AP4()
print(ap4.foo()) // prints foo
print(AP4.T.self) // prints S<S<AP2, String>, String>
print(AP4.T.T.self) // prints S<AP2, String>
print(AP4.T2.self) // prints S<AP2, String>
print(AP4.T.B.self) // prints AP2

In the above example, clearly `AP4.T.T ![](= AP4.T.BandAP4.T2 )= AP4.T.B` but the extension on `P4` that declares `foo` clearly requires them to be equal. `foo` should not be available on `AP4`. The same incorrect behavior is exhibited when the constraint uses `T.T == T.B` or `T.T: P2` instead of `T2 == T.B`. Interestingly however, placing the extension on `P3` instead of `P4` produces the correct behavior of `foo` not being available on `AP4` and thus a no member error for the expression `ap4.foo()`.

belkadan commented 6 years ago

cc @huonw again :-)