Skip to content

Commit 3376372

Browse files
committed
RequirementMachine: Fix getCanonicalTypeInContext() to handle member types of superclass bounds
If you have protocol P { associatedtype T } class C<T> : P {} Then substituting the type parameter T.T from the generic signature <T where T : P> into the generic signature <T, U where T : C<U>> is the identity operation, and also returns T.T, because subst() isn't smart enough to replace T.T with U. So getCanonicalTypeInContext() has to do the concrete conformance lookup here, just like it does for the analogous situation where you have a concrete type requirement. This could be solved in a more principled way by better book-keeping elsewhere, but the GSB supported the old behavior, so we can simulate it easily enough in the RequirementMachine also.
1 parent 319b3e6 commit 3376372

File tree

2 files changed

+49
-8
lines changed

2 files changed

+49
-8
lines changed

lib/AST/RequirementMachine/GenericSignatureQueries.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -346,14 +346,31 @@ Type RequirementMachine::getCanonicalTypeInContext(
346346
verify(prefix);
347347

348348
auto *props = Map.lookUpProperties(prefix);
349-
if (props && props->isConcreteType()) {
350-
auto concreteType = props->getConcreteType(genericParams,
351-
protos, Context);
352-
if (!concreteType->hasTypeParameter())
353-
return concreteType;
354-
355-
// FIXME: Recursion guard is needed here
356-
return getCanonicalTypeInContext(concreteType, genericParams);
349+
if (props) {
350+
if (props->isConcreteType()) {
351+
auto concreteType = props->getConcreteType(genericParams,
352+
protos, Context);
353+
if (!concreteType->hasTypeParameter())
354+
return concreteType;
355+
356+
// FIXME: Recursion guard is needed here
357+
return getCanonicalTypeInContext(concreteType, genericParams);
358+
}
359+
360+
// Skip this part if the entire input term is valid, because in that
361+
// case we don't want to replace the term with its superclass bound;
362+
// unlike a fixed concrete type, the superclass bound only comes into
363+
// play when looking up a member type.
364+
if (props->hasSuperclassBound() &&
365+
prefix.size() != term.size()) {
366+
auto superclass = props->getSuperclassBound(genericParams,
367+
protos, Context);
368+
if (!superclass->hasTypeParameter())
369+
return superclass;
370+
371+
// FIXME: Recursion guard is needed here
372+
return getCanonicalTypeInContext(superclass, genericParams);
373+
}
357374
}
358375

359376
return Context.getTypeForTerm(prefix, genericParams, protos);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-emit-silgen %s -requirement-machine=verify
2+
3+
// The substituted type of SS.x.x is computed by taking the type of S.x,
4+
// which is T.T in the generic signature <T where T : P>, and then
5+
// canonicalizing it in the generic signature <T : C<U>, U>.
6+
//
7+
// The latter generic signature does not have a conformance requirement T : P,
8+
// but the superclass bound C<U> of T conforms to P concretely; make sure that
9+
// the requirement machine's getCanonicalTypeInContext() can handle this.
10+
public protocol P {
11+
associatedtype T
12+
}
13+
14+
public class C<T> : P {}
15+
16+
public struct S<T : P> {
17+
public var x: T.T
18+
}
19+
20+
public struct SS<T : C<U>, U> {
21+
public var x: S<T>
22+
}
23+
24+

0 commit comments

Comments
 (0)