Skip to content

Commit 576845d

Browse files
committed
RequirementMachine: Tighten up type witness recursion hack
When a type parameter is subject to a conformance requirement and a concrete type requirement, the concrete type unification pass recursively walks each nested type introduced by the conformance requirement, and fixes it to the concrete type witness in the concrete type conformance. In general, this can produce an infinite sequence of concrete type requirements. There are a couple of heuristics to "tie off" the recursion. One heuristic is that if a nested type of a concrete parent type is exactly equal to the parent type, a same-type requirement is introduced between the child and the parent, preventing concrete type unification from recursing further. This used to check for exact equality of types, but it is possible for the type witness to be equal under canonical type equivalence, but use a different spelling via same-type requirements for some type parameter appearing in structural position. Instead, canonicalize terms here, allowing recursion where the type witness names a different spelling of the same type. Fixes <rdar://problem/83894546>.
1 parent 4d5e641 commit 576845d

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,19 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent(
910910

911911
MutableTerm constraintType;
912912

913-
if (concreteType == typeWitness &&
913+
auto simplify = [&](CanType t) -> CanType {
914+
return CanType(t.transformRec([&](Type t) -> Optional<Type> {
915+
if (!t->isTypeParameter())
916+
return None;
917+
918+
auto term = getRelativeTermForType(t->getCanonicalType(),
919+
substitutions, Context);
920+
System.simplify(term);
921+
return Context.getTypeForTerm(term, { }, Protos);
922+
}));
923+
};
924+
925+
if (simplify(concreteType) == simplify(typeWitness) &&
914926
requirementKind == RequirementKind::SameType) {
915927
// FIXME: ConcreteTypeInDomainMap should support substitutions so
916928
// that we can remove this.

test/Generics/rdar83894546.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
public protocol P1 {
4+
associatedtype A: P1 where A.A == A
5+
}
6+
7+
public protocol P2 {
8+
associatedtype B: P1
9+
associatedtype C: P2
10+
}
11+
12+
public struct G<B : P1> : P2 {
13+
public typealias C = G<B.A>
14+
}
15+
16+
// C == G<B> together with the definition of G<B>.C implies an infinite series
17+
// of rules:
18+
// - C.C == G<B.A>
19+
// - C.C.C == G<B.A.A>
20+
// - C.C.C.C == G<B.A.A.A>
21+
// - ...
22+
//
23+
// This would normally prevent the completion procedure from terminating,
24+
// however A.A == A in protocol P1 means that the right hand sides simplify
25+
// as follows:
26+
//
27+
// - C.C == G<B.A>
28+
// - C.C.C == B<G.A>
29+
// - C.C.C.C == G<B.A>
30+
// - ...
31+
//
32+
// Therefore, the single rule C.C == C.C.C suffices to "tie off" the recursion.
33+
public protocol P3: P2 where C == G<B> {}

0 commit comments

Comments
 (0)