Mysterious failure of extensions declared on typealiases of protocols. Any good workaround?

If I declare a typealias for a concrete type in a protocol, e.g.

struct A { }

protocol P {
  typealias Element = A
}

and then I write an extension based on that typealias on a conforming type, e.g.:

struct Conforming: P { }

extension Conforming.Element { // This should be identical to `extension A`
  var success: Bool { true }
}

The compiler allows it. But trying to access the extension property will fail:

print(Conforming.Element().success) //Compiler error:  "Value of type 'Conforming.Element' (aka 'A') has no member 'success'"

I declare the extension directly on the type (not through the typealias), e.g.

extension A {
  var success: Bool { true }
}

Then the same print statement (accessing type A through the protocol typealias) will succeed

Declaring the typealias directly on the type allows the extension through the typealias to work:

struct Conforming {
  typealias Element = A
}

extension Conforming.Element { 
  var success: Bool { true }
}

print(Conforming.Element().success) //  "true"

It only seems to fail on the requirement of writing an extension that refers to a typealias declared by a protocol.

Is this a bug? Does anyone have any ideas for workarounds?

Ultimately what I want to accomplish is something like this (just a convenience really)

protocol SettingsProvider { 
  typealias SettingsConfiguration = Configuration<Self>
}

struct Configuration<T> { }

struct ConcreteType: SettingsProvider { }

// I want to be able to do this
extension ConcreteType.Settings {
  var someProperty: String
}

// Instead of this equivalent that seems much less intuitive to me
extension Configuration<ConcreteType> {
  var someProperty: String
}

2 Likes

Yup, seems like a bug. The workaround would be to declare the extension without using the alias, as you're doing.

4 Likes

Thanks Xiaodi!

Would you mind filing a bug over on the Swift GitHub issues tracker?

Done!

1 Like

The bug was that it was not diagnosed. However, the type checker is rejecting the extension because it doesn't fit the implementation model. Extension binding has to happen very early, before other name lookups take place, so it cannot depend on conformance lookup tables having been constructed. For this reason, you cannot extend a protocol type alias accessed as a member of a concrete type.

I already fixed the diagnostic on main a while ago so I'm closing the bug: Sema: Improved check for failure to bind an extension by slavapestov · Pull Request #60364 · apple/swift · GitHub

5 Likes

Thanks so much for your rapid attention and helpful explanation @Slava_Pestov!