How to select different associated type based on type constraints?

There's something important I've overlooked in your example. One of your extensions is unconstrained, meaning a type alias is declared as part of the enclosing type's primary declaration. Naturally, a second declaration of a type alias with an equal identifier is then considered a redeclaration. Note this isn't an overload, as it would be with methods.

I was initially referring to a situation when all relevant type alias declarations occur in constrained extensions


To be honest, the example on Array wasn't very accurate. Element is a generic parameter, not a type alias, so we're dealing with shadowing (similar to your example in question 2) rather than a redeclaration. Be it a type alias, such an extension showcases exactly why it is considered a redeclaration.

Your Array extension doesn't make much sense, since the constraint itself implies that Element is predefined. Consider instead redeclaring SubSequence for a constrained Element. Allowing this would break type safety because any API expecting Slice would have to work with a different type. On a non-retroactive example any API with such a type collision will currently trigger type is ambiguous for type lookup in this context together with the redeclaration error. This mostly answers the second question.

It's long time to requalify this shadowing rule into a redeclaration error. I believe there is no specific use case that could advocate the opposite. It's mentioned in this pitch as part of a broader idea.

Associated types are just another way of spelling generic parameters on protocols. Everything you can do in concrete type extensions you can also do in protocol extensions, but protocols also allow to specify default implementations for requirements and make them depend on Self or associated types through constraints. Type aliases aren't the case, but they could be. However, for both concrete types and protocols, we still have to decide on how to address various ambiguous situations. With methods, this is pretty straightforward – we raise an error at call site. With type aliases, the error would have to be triggered at the implementation site as well.

// Module A
protocol P {
    associatedtype A
    associatedtype B
}

extension P where B: Sequence {
    typealias A = Int
}

protocol P1 {}

class Foo: P, P1 {
    typealias B = String // A inferred as Int
    
    func foo() -> A { return 0 } 
}

// Module B
extension P where Self: P1 {
    typealias A = String
}

// How should this ambiguity be diagnosed if the source of A isn't visible to the user?

We should strive to avoid code that retroactively breaks other code. One course of action is to be silent until the relevant type alias is actually used to then say type is ambiguous for type lookup in this context. The same technique is used when you try to retroactively shadow a generic parameter of an external type (relates to your example in question 2 and should really be a redeclaration error additionally).

extension Array {
  typealias Element = Bool

  func foo(_ arg: Element) {} // 'Element' is ambiguous for type lookup in this context
}