RequirementMachine: Concrete contraction needs to substitute the parent type of a subject type sometimes

If you have generic parameters <T, U> and requirements of the form:

- T : P
- T == ConcreteType<U>
- T.[P]U : SomeClass
- T.[P]U : SomeProto

And furthermore SomeClass does not conform to SomeProto, we can't leave
`T.[P]U : SomeClass` unsubstituted; we still have to replace `T` with
`ConcreteType<U>` to transform the latter two requirements into:

- U : SomeClass
- U : SomeProto

"Concrete contraction" is easily the hackiest part of the Requirement
Machine; I need to come up with a more principled solution for the
problem that it solves sooner or later.

Fixes rdar://problem/94150249.
This commit is contained in:
Slava Pestov
2022-06-08 00:37:53 -04:00
parent ea980d6fbd
commit 9695b1957a
2 changed files with 15 additions and 10 deletions

View File

@@ -227,8 +227,8 @@ public:
Optional<Type> ConcreteContraction::substTypeParameterRec(
Type type, Position position) const {
// If the requirement is of the form 'T == C' or 'T : C', don't
// substitute T, since then we end up with 'C == C' or 'C : C',
// If we have a superclass (T : C) or same-type requirement (T == C),
// don't substitute T, since then we end up with 'C == C' or 'C : C',
// losing the requirement.
if (position == Position::BaseType ||
position == Position::ConformanceRequirement) {
@@ -399,9 +399,10 @@ ConcreteContraction::substRequirement(const Requirement &req) const {
!module->lookupConformance(substFirstType, proto,
allowMissing, allowUnavailable)) {
// Handle the case of <T where T : P, T : C> where C is a class and
// C does not conform to P by leaving the conformance requirement
// unsubstituted.
return req;
// C does not conform to P and only substitute the parent type of T
// by pretending we have a same-type requirement here.
substFirstType = substTypeParameter(
firstType, Position::SameTypeRequirement);
}
// Otherwise, replace the generic parameter in the conformance
@@ -418,9 +419,11 @@ ConcreteContraction::substRequirement(const Requirement &req) const {
if (!substFirstType->isTypeParameter() &&
!substFirstType->satisfiesClassConstraint() &&
req.getLayoutConstraint()->isClass()) {
// If the concrete type doesn't satisfy the layout constraint,
// leave it unsubstituted so that we produce a better diagnostic.
return req;
// If the concrete type doesn't satisfy the layout constraint, produce
// a better diagnostic and only substitute the parent type by pretending
// we have a same-type requirement here.
substFirstType = substTypeParameter(
firstType, Position::SameTypeRequirement);
}
return Requirement(req.getKind(),

View File

@@ -12,14 +12,16 @@ class G<Value> : P2 {}
protocol P3 {}
class C {}
extension P1 where Value: P2 {
typealias Element = Value.Value
// Make sure we can resolve 'Element' to 'V' on the left hand side of 'Element: P3'.
// CHECK-LABEL: .P1 extension.set()@
// CHECK-NEXT: Generic signature: <Self, V where Self : P1, V : P3, Self.[P1]Value == G<V>>
func set<V>() where Element: P3, Value == G<V> {
// CHECK-NEXT: Generic signature: <Self, V where Self : P1, V : C, V : P3, Self.[P1]Value == G<V>>
func set<V>() where Element: P3 & C, Value == G<V> {
takeP3(V.self)
}
}