There are three kinds of invariant 'Self' uses here:
- 'Self' appears as the left hand side of a same-type requirement between type parameters
- 'Self' appears as a structural component of the right hand side of a concrete type requirement
- 'Self' appears as a structural component of the right hand side of a superclass requirement
My previous fix only handled the first case. Generalize it to handle all three.
Fixes rdar://problem/74944514.
See the comment at the top of ConcreteContraction.cpp for a detailed explanation.
This can be turned off with the -disable-requirement-machine-concrete-contraction
pass, mostly meant for testing. A few tests now run with this pass both enabled
and disabled, to exercise code paths which are otherwise trivially avoided by
concrete contraction.
Fixes rdar://problem/88135912.
If you have something like this:
protocol P {
associatedtype A : Q where Self == Self.A.B
}
protocol Q {
associatedtype B
}
class C : P {
typealias A = D
}
class D : P {
typealias B = C
}
The GSB would minimize the generic signature <T where T : P, T : C>
to <T where T == C>, because of the same-type requirement in
protocol P.
However in reality, the conformance 'C : P' is unsound, because it
is no longer covariant. I added a warning in commit d831eff74879cb.
Because the upcoming 'concrete contraction' pass eliminates 'T : P'
before homotopy reduction gets a chance to run, I'm changing the
Requirement Machine to produce a different minimization from the
GSB. This is technically an ABI break, but it should not impact any
real code in practice. If it does, I'll need to come up with a
different workaround in concrete contraction.
This ensures we produce the same minimal signature as the GSB
in a couple of really silly examples where a superclass requirement
and a concrete type requirement imply each other.
After we drop redundant conformance requirements, the left hand side
of a concrete same-type requirement might become unresolvable:
protocol P {
associatedtype T where T == Self
}
struct S : P {}
extension P where T == S {}
Here, we begin with <Self where Self : P, Self.T == S>, and then we
drop (Self : P). However, <Self where Self.T == S> is no longer a
valid generic signature.
We can canonicalize Self.T down to Self before we build the new
signature, but we must only do this for concrete same-type requirements,
since canonicalizing the subject type of an abstract same-type
requirement might lose information produce a trivial requirement of
the form 'T == T'.
This is really unprincipled, and no doubt other counter-examples
exist. The entire procedure for rebuilding a generic signature needs
to be re-designed from first principles.
Fixes rdar://problem/80503090.
A protocol conformance requirement together with a superclass requirement
can collapse down to a same-type requirement if the protocol itself has
a 'where' clause:
protocol P {
associatedtype T where T == Self
}
class C : P {}
extension P where T : C {}
(Self : P) and (Self.T : C) imply that (Self == C), because protocol P
says that Self.T == Self, and the witness of P.T in C is C, so when we
substitute that in we get Self == C.
Part of rdar://problem/80503090.
Generally we say that a conformance requirement in a generic signature
is redundant if there is some other way to derive this conformance by
starting from another conformance requirement in the same signature,
and possibly following one or more conformance requirements on nested
types defined in protocols.
The notion of a 'valid derivation path' comes into play when you have
something like this:
protocol P {
associatedtype A
}
protocol Q {
associatedtype B : P
}
<T where T : P, T.A : P, T.A.B == T>
Here, we don't want to conclude that (T : P) can be derived from
(T.A : P)(Self.B : P), because if we drop (T : P) from the signature,
we end up with
<T where T.A : P, T.A.B == T>
Now in order to recover the witness table for T : P, we need to start
from T.A : P, which requires the type metadata for T.A, which can
only be recovered from the witness table for T : P, etc. We're stuck.
What we want to do is say that T : P is not redundant, because we
cannot derive it from T.A : P, because T.A depends on T : P.
However, this check was also too strict. Consider this example:
protocol P {
associatedtype T where T == Self
}
protocol Q : P {}
<T where T : P, T.T : Q>
The naive algorithm would conclude that T : P is redundant because
it can be derived as (T.T : Q)(Self.T == Self). However, the valid
derivation path check would fail since T.T is derived from (T : P).
The problem is that since T.T is equivalent to T via (Self.T == T),
we would end up with this minimized signature:
<T where T : P, T : Q>
The derivation path check should canonicalize the type first.
I'm still not 100% convinced this logic is correct, but at least,
we have another test case and maybe it's _more_ correct now.
Fixes part of rdar://problem/80503090.