Inherited protocol does not automatically conditionally conform nor gets satisfied

I'm really confused by this behavior. I assume that this code should compile, but the compiler won't let me. It feels like a bug because the inherited protocol does not gets satisfied nor it's automatically conditionally conformed.

public struct Generic<Constraint> {}

public protocol ConversionValueLookup {
  associatedtype ConversionValue
}

public protocol SomeProto {}

public protocol ConvertibleA : ConversionValueLookup where ConversionValue : SomeProto {}
public protocol ConvertibleB : ConversionValueLookup where ConversionValue : SomeProto {}

/*
error: type 'Generic<Constraint>' does not conform to protocol 'ConversionValueLookup'
extension Generic : ConvertibleA where Constraint : ConvertibleB {
^

note: type 'Generic<Constraint>' does not conform to inherited protocol 'ConversionValueLookup'
public protocol ConvertibleA : ConversionValueLookup where ConversionValue : SomeProto {}
*/
extension Generic : ConvertibleA where Constraint : ConvertibleB {
  public typealias ConversionValue = Constraint.ConversionValue
}

// Uncomment the next line to fix the error
// extension Generic : ConversionValueLookup where Constraint : ConvertibleB {}

Is it a bug, is it a rule I don't understand or something else?


The types do not have a lot of semantics in the example, because the example contains the bare bones to reproduce the issue. In the real codebase protocol that is named ConvertibleA has more additional constraints and semantics and it's applied to a set of *Container types, while ConvertibleB is similar constrained, yet slightly different is applied on *Constraint types. I use this tactics to extract and forward types stored in the generic parameter lists of nested generic types to enclosing parent generic types. This allows me to build a strongly constrained and statically safe API.

I assume https://forums.swift.org/t/amending-se-0143-conditional-conformances-dont-imply-other-conformances/11017 is the reason.

By the way, while playing with your code I discovered this crashes in Xcode 10 and on master..

struct Foo<T> {}

protocol P1 {associatedtype A}
extension Foo: P1 where A : P1 {}

UDP SR-8033

2 Likes

Thx, I didn't know. I probably had to re-read the updated proposal :face_with_monocle: Finally I can stop fighting the compiler and fix my library.

The explicit fix for me is this:

extension Generic : ConversionValueLookup, ConvertibleA where Constraint : ConvertibleB {

And the official answer to my question is, no it's not a bug but intended behavior (PR from the thread of your link was accepted and merged).


Regarding your example. It did not crash my playgrounds (Xcode 9.4). The compiler shouldn't crash, but should result in an error since the where clause makes no sense.

The master (and Xcode 10 beta) compiler handles this better than the ones in previous Xcode releases. The older versions had various bugs that accidentally prevented the implication, while the new ones explicitly/purposefully prevent it, and detect it to provide a much more explanatory error:

...:14:1: error: conditional conformance of type 'Generic<Constraint>' to protocol 'ConvertibleA' does not imply conformance to inherited protocol 'ConversionValueLookup'
extension Generic : ConvertibleA where Constraint : ConvertibleB {
^

...:14:1: note: did you mean to explicitly state the conformance like 'extension Generic: ConversionValueLookup where ...'?
extension Generic : ConvertibleA where Constraint : ConvertibleB {
^

4 Likes