Type X does not inherit from Y

2019-09-07 13:38发布

问题:

The following Swift code yields this error on the last line: Type 'E' does not inherit from 'C<Self>'. Not sure what's going on here. Any clues would be appreciated.

class C<T> {}

protocol P {
  typealias E : C<Self>
}

class A : P {
  typealias E = C<A>
}

class S<U : P> {}
class C2<T> : S<A> {}

Update: I simplified the broken example. The old version (to which milos' answer refers) can be found in this question's edit history.

回答1:

I have renamed your identifiers so I can think about them:

protocol P {
    typealias E : C<Self>
}

class A : P {
    typealias E = C1<Any>
}

class B : P {
    typealias E = C2<Any>
}

class C<T> {}
class C1<T> : C<A> {}

class S<T, U : P> : C<T> {} // <-- mark!
class C2<T> : S<B, A> {}

This should eventually, and very nearly does, work out. In effect, what you want is:

class B : P {
    typealias E = C<B> // which is what P requires (same with class A)
}

However, on the marked line, where you are defining class S you are asking the compiler to check the type of U : P and then pass B as the concrete type to check. Unfortunately, at this point B's conformance to P is still unresolved (i.e. it is itself defined in terms of C2 : S : C, which is where you are going with U : P). Removing : P in U : P removes the error, though this may not be what you want. Then again, depending on what you want, there may be any number of solutions :)

EDIT

The following is in response to @igul222's much simplified code example. I still, think, however, that the compiler is simply returning a less then helpful error message, which is really caused by the recursive type definition. Consider, for example, if you define an enum in terms of itself:

enum E {
    case C(E) // error: recursive value type 'E' is not allowed
}

Now, this is probably also the problem with the following:

class C<T> {}

protocol P {
    typealias E : C<Self>
    var c: E { get }
}

final class A : P {
    typealias E = C<A>
    var c: E { return E() }
}
// --> error: type 'A' does not conform to protocol 'P'
// --> note: protocol requires property 'c' with type 'E'
// --> note: candidate has non-matching type 'E'

... neither does this work (a version of your gist):

class C<T> {}

protocol P {
    typealias E : C<Self>
}

final class A : P {
    typealias E = C<A>
}

class X<U : P> {}

X<A>() // --> error: type 'E' does not inherit from 'C<`Self`>'

... or this:

class C<T> {}

protocol P {
    typealias E : C<Self>
}

final class A : P {
    typealias E = C<A>
}

let a = A()

func f<T: P>(T) {}

f(a) // --> error: 'A' is not identical to '`Self`'

What the compiler seems to be saying is that Self in C<Self> is not yet A, i.e. that A is not yet itSelf since to be A it must conform to P which is in turn pending C<Self> checking out... But the following works because A no longer defines an associated type in terms of itself:

class C<T> {}

protocol P {
    var c: C<Self> { get }
}

final class A : P {
    typealias E = C<A> // just a typealias, no longer an associated type
    var c: E { return E() }
}

Some patterns of functional programming require recursively defined types, so it might be nice to have that in Swift. At present, however, I am not sure one can usefully conform to a protocol with an associated type of the form T<Self>, even though the compiler allows its definition... otherwise, this should all just work at runtime.

EDIT 2

I have just upgraded to Xcode 6.1 GM Seed and things have changed! The following snippet, that would not have compiled before, now compiles and appears to run fine!

protocol A {
    var b: B? { get set }
}

protocol B {
    var a: A? { get set }
}

class Ca: A {
    var b: B?
}

class Cb: B {
    var a: A?
}

let a = Ca()    // --> {nil}
let b = Cb()    // --> {nil}

a.b = b         // --> {{{...}}}
b.a = a         // --> {{{...}}}

This improvement, however, does not extend to recursively defined associated types.