Associated type is not inferred

// Code from https://github.com/cx-org/CombineX/blob/CXNamespace/Sources/CXNamespace/CXNamespace.swift

public protocol CXWrapper {
    
    associatedtype Base
    
    var base: Base { get }
    
    init(_ base: Base)
}

public protocol CXWrappable {
    
    associatedtype CX: CXWrapper where CX.Base == Self
    
    var cx: CX { get }
}

public extension CXWrappable {
    
    var cx: CX {
        return CX(self)
    }
}

// Note CX == Self is inferred here. Adding this requirement produce a warning.
public protocol CXSelfWrapping: CXWrappable, CXWrapper where Base == Self /* CX == Self */ {}

public extension CXSelfWrapping {
    
    var base: Base {
        return self
    }
    
    init(_ base: Base) {
        self = base
    }
}

AFAICS CXSelfWrapping don't have any associated type, and all requirements have default implementation. Any type can conforms to CXSelfWrapping for free. However no type can conform to it.

// Type 'A' does not conform to protocol 'CXSelfWrapping'
// Do you want to add protocol stubs?
class A: CXSelfWrapping {}

// Type 'Optional<Wrapped>' does not conform to protocol 'CXSelfWrapping'
// Do you want to add protocol stubs?
extension Optional: CXSelfWrapping {}

// Type 'Optional<Wrapped>' does not conform to protocol 'CXWrappable'
// Type 'Optional<Wrapped>' does not conform to protocol 'CXWrapper'
extension Optional: CXSelfWrapping {
    public typealias CX = Optional<Wrapped>
}

Is this a bug or intended behavior, Am I doing wrong?

Let us remove all the noise and boil this down a bit first:

protocol CXWrapper {
    associatedtype Base
}

protocol CXWrappable {
    associatedtype CX: CXWrapper where CX.Base == Self
}

protocol CXSelfWrapping: CXWrappable, CXWrapper where Base == Self {}

Hopefully it becomes easier to notice that you cannot conform to CXSelfWrapping for free: Base is constrained and determined but there's nothing stopping CX from being an arbitrary type like Foo:

class Foo: CXWrapper {
  typealias Base = A
}

// Self == A
class A: CXSelfWrapping { // OK
  typealias CX = Foo // : CXWrapper where CX.Base == Self
  
  typealias Base = A
}

That warning you mentioned is actually a bug, filed it under SR-11670.

Thank you. So CX == Self is necessary but the compiler ignored it and produce a warning?

In short, the CX == Self constraint is totally legal.

What happens is CX == Self internally yields a derived and redundant constraint Self: XSWrapper (because CX: CXWrapper), and the compiler erroneously emits a warning telling you the derived constraint is redundant, when only those explicitly written by the user should be allowed to be diagnosed as redundant.


P.S. Note that CX == Self will still not make the conformance "free", seems like we don't handle the == Self case yet :-(
^
SR-11671

2 Likes