Typealias in constrained extension, should this compile?

The folllowing program compiles (in Swift 5.2 / Xcode 11.4 and earlier), but should it?

protocol P {
    associatedtype A
    typealias B = Int
}
extension P where A == Bool {
    typealias B = Float // I mean, isn't this an invalid redeclaration of B?
}

Related old open bug.


If it is meant to compile, then why does this program print Int rather than Bool?

protocol P {
    associatedtype A
    typealias B = Int
}
extension P where A == Bool {
    typealias B = Float
    static func printB() { print(B.self) }
}
struct S<A> : P {}
S<Bool>.printB() // Prints "Int"

And how can the B that is Float be accessed?

1 Like

good question,waiting for swifter to answer!

cc @Slava_Pestov (fixed SR-5440 and SR-10466 which I think might be related).

cc @anthonylatsis (assignee of SR-7516 which might also be related).

This one is different. Should have been a redeclaration (we are not supposed to support this kind of conditional type member overloading, not that it's completely senseless to do so).

struct Foo<T> {
  typealias B = Int
}
extension Foo where T == Bool {
  typealias B = Float // Invalid redeclaration of 'B'
}
1 Like

OK, thanks. Then, by similar reasoning, is the following also a bug?

struct S<A> {
    static var foo: Int { 123 }
    // static var foo: Bool { true } // Does not compile, invalid redeclaration (as expected).
}
extension S where A == UInt16 {
    static var foo: Bool { true } // Compiles, but should it?
}
extension S where A == UInt32 {
    static var foo: String { "abc" } // Compiles, but should it?
}
extension S where A: UnsignedInteger {
    static var foo: Double { 1.23 } // Compiles, but should it?
}
extension S where A: FixedWidthInteger {
    static var foo: Double { 3.21 } // Compiles, but should it?
}

func bar1<T: UnsignedInteger>(_: T.Type) -> Double { return S<T>.foo }
func bar2<T: FixedWidthInteger>(_: T.Type) -> Double { return S<T>.foo }

func test() {
    print(S<UInt16>.foo as Int)    // 123
    print(S<UInt16>.foo as Bool)   // true
    print(S<UInt32>.foo as String) // abc
    print(bar1(UInt16.self))       // 1.23
    print(bar2(UInt16.self))       // 3.21
}
test()

Interesting. I think the behavior is expected, especially since you can use coercion to disambiguate (there is nothing we could do to similarly disambiguate type members in the general case, because a type member is simply a name). @xedin Am I right here?

On the other hand, the amount of bugs in this area shows that an agreement on how all these edge-case ambiguities with conditional conformances and extensions should work is yet to be settled (and implemented).

If that was true, then why this error:

?

Yeah, good question. Swift 5.1:

struct S<A> {
  static var foo: Int { 123 }
}
extension S {
  static var foo: Bool { true } // Redeclaration of 'foo' is deprecated and will be an error in Swift 5
}

... in Swift 5.2?

I only see the error in 5.3 (master) ...which brings some doubt to what I said earlier about a similar example being expected behavior. But I do not see a reason to ban this either.

This is Xcode 11.4, Swift 5.2:

struct S<A> {
    static var foo: Int { 123 }
}
extension S {
    static var foo: Bool { true } // ERROR: Invalid redeclaration of 'foo'
}

But that's different from my original example:

struct S<A> {
    static var foo: Int { 123 }
}
extension S where A == UInt16 {
    static var foo: Bool { true } // Compiles (in Swift 5.2), but should it?
}

Well, if both the above should be valid, then so should this I guess:

struct S {
    static var foo: Int { 123 }
    static var foo: Bool { true } // ERROR (in Swift 5.2): Invalid redeclaration of 'foo'
}

And I agree that it would make sense to make that valid, since these are currently all valid:

struct S {
    static func foo() -> Int { 123 }
    static func foo() -> Bool { true }
}

struct S {
    func foo() -> Int { 123 }
    func foo() -> Bool { true }
}

func foo() -> Int { 123 }
func foo() -> Bool { true }

I've asked about this in a separate thread since it's not the main question of the OP.

2 Likes