Skip to content

Commit 9a8ee60

Browse files
committed
RequirementMachine: Fix silly oversight when resolving concrete types and superclass bounds
The property map stores the concrete type or superclass bound for all terms whose suffix is equal to some key, so eg if you have protocol P { associatedtype T where T == U? associatedtype U } Then you have this rewrite system [P].T => [P:T] [P].U => [P:U] [P:T].[concrete: Optional<τ_0_0> with <[P:U]>] => [P:T] Which creates this property map [P:T] => { [concrete: Optional<τ_0_0> with <[P:U]>] } Now if I start with a generic signature like <T, U where U : P> This produces the following rewrite system: τ_0_1.[U] => τ_0_1 τ_0_1.T => τ_0_1.[P:T] τ_0_1.U => τ_0_1.[P:U] Consider the computation of the canonical type of τ_0_1.T. This term reduces to τ_0_1.[P:T]. The suffix [P:T] has a concrete type symbol in the property map, [concrete: Optional<τ_0_0> with <[P:U]>]. However it would not be correct to canonicalize τ_0_1.[P:T] to Optional<τ_0_0>.subst { τ_0_0 => getTypeForTerm([P:T]) }; this produces the type Optional<τ_0_0.T>, and not Optional<τ_0_1.T> as expected. The reason is that the substitution τ_0_0 => getTypeForTerm([P:T]) is "relative" to the protocol Self type of P, since domain([P:T]) = {P}. Indeed, the right solution here is to note that τ_0_1.[P:T] has a suffix equal to the key of the property map entry, [P:T]. If we strip off the suffix, we get τ_0_1. If we then prepend τ_0_1 to the substitution term, we get the term τ_0_1.[P:U], whose canonical type is τ_0_1.U. Now, applying the substitution τ_0_0 => τ_0_1.U to Optional<τ_0_0> produces the desired result, Optional<τ_0_1.U>. Note that this is the same "prepend a prefix to each substitution of a concrete type symbol" operation that is performed in checkForOverlap() and PropertyBag::copyPropertiesFrom(), however we can simplify things slightly by open-coding it instead of calling the utility method prependPrefixToConcreteSubstitutions(), since the latter creates a new symbol, which we don't actually need.
1 parent 3376372 commit 9a8ee60

File tree

4 files changed

+73
-15
lines changed

4 files changed

+73
-15
lines changed

lib/AST/RequirementMachine/GenericSignatureQueries.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ RequirementMachine::getLocalRequirements(
4747
return result;
4848

4949
if (props->isConcreteType()) {
50-
result.concreteType = props->getConcreteType({}, protos, Context);
50+
result.concreteType = props->getConcreteType({}, term, protos, Context);
5151
return result;
5252
}
5353

5454
if (props->hasSuperclassBound()) {
55-
result.superclass = props->getSuperclassBound({}, protos, Context);
55+
result.superclass = props->getSuperclassBound({}, term, protos, Context);
5656
}
5757

5858
for (const auto *proto : props->getConformsToExcludingSuperclassConformances())
@@ -153,7 +153,7 @@ Type RequirementMachine::getSuperclassBound(Type depType) const {
153153
return Type();
154154

155155
auto &protos = System.getProtocols();
156-
return props->getSuperclassBound({ }, protos, Context);
156+
return props->getSuperclassBound({ }, term, protos, Context);
157157
}
158158

159159
bool RequirementMachine::isConcreteType(Type depType) const {
@@ -183,7 +183,7 @@ Type RequirementMachine::getConcreteType(Type depType) const {
183183
return Type();
184184

185185
auto &protos = System.getProtocols();
186-
return props->getConcreteType({ }, protos, Context);
186+
return props->getConcreteType({ }, term, protos, Context);
187187
}
188188

189189
bool RequirementMachine::areSameTypeParameterInContext(Type depType1,
@@ -349,7 +349,8 @@ Type RequirementMachine::getCanonicalTypeInContext(
349349
if (props) {
350350
if (props->isConcreteType()) {
351351
auto concreteType = props->getConcreteType(genericParams,
352-
protos, Context);
352+
prefix, protos,
353+
Context);
353354
if (!concreteType->hasTypeParameter())
354355
return concreteType;
355356

@@ -364,7 +365,8 @@ Type RequirementMachine::getCanonicalTypeInContext(
364365
if (props->hasSuperclassBound() &&
365366
prefix.size() != term.size()) {
366367
auto superclass = props->getSuperclassBound(genericParams,
367-
protos, Context);
368+
prefix, protos,
369+
Context);
368370
if (!superclass->hasTypeParameter())
369371
return superclass;
370372

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ static unsigned getGenericParamIndex(Type type) {
144144
static Type getTypeFromSubstitutionSchema(Type schema,
145145
ArrayRef<Term> substitutions,
146146
TypeArrayView<GenericTypeParamType> genericParams,
147+
const MutableTerm &prefix,
147148
const ProtocolGraph &protos,
148149
RewriteContext &ctx) {
149150
assert(!schema->isTypeParameter() && "Must have a concrete type here");
@@ -155,41 +156,76 @@ static Type getTypeFromSubstitutionSchema(Type schema,
155156
if (t->is<GenericTypeParamType>()) {
156157
auto index = getGenericParamIndex(t);
157158

158-
return ctx.getTypeForTerm(substitutions[index],
159-
genericParams, protos);
159+
// Prepend the prefix of the lookup key to the substitution, skipping
160+
// creation of a new MutableTerm in the case where the prefix is empty.
161+
if (prefix.empty()) {
162+
return ctx.getTypeForTerm(substitutions[index], genericParams, protos);
163+
} else {
164+
MutableTerm substitution(prefix);
165+
substitution.append(substitutions[index]);
166+
return ctx.getTypeForTerm(substitution, genericParams, protos);
167+
}
160168
}
161169

162170
assert(!t->isTypeParameter());
163171
return None;
164172
});
165173
}
166174

167-
/// Get the superclass bound for the term represented by this property bag.
175+
/// Given a term \p lookupTerm whose suffix must equal this property bag's
176+
/// key, return a new term with that suffix stripped off. Will be empty if
177+
/// \p lookupTerm exactly equals the key.
178+
MutableTerm
179+
PropertyBag::getPrefixAfterStrippingKey(const MutableTerm &lookupTerm) const {
180+
assert(lookupTerm.size() >= Key.size());
181+
auto prefixBegin = lookupTerm.begin();
182+
auto prefixEnd = lookupTerm.end() - Key.size();
183+
assert(std::equal(prefixEnd, lookupTerm.end(), Key.begin()) &&
184+
"This is not the bag you're looking for");
185+
return MutableTerm(prefixBegin, prefixEnd);
186+
}
187+
188+
/// Get the superclass bound for \p lookupTerm, whose suffix must be the term
189+
/// represented by this property bag.
190+
///
191+
/// The original \p lookupTerm is important in case the concrete type has
192+
/// substitutions. For example, if \p lookupTerm is [P:A].[U:B], and this
193+
/// property bag records that the suffix [U:B] has a superclass symbol
194+
/// [superclass: Cache<τ_0_0> with <[U:C]>], then we actually need to
195+
/// apply the substitution τ_0_0 := [P:A].[U:C] to the type 'Cache<τ_0_0>'.
168196
///
169197
/// Asserts if this property bag does not have a superclass bound.
170198
Type PropertyBag::getSuperclassBound(
171199
TypeArrayView<GenericTypeParamType> genericParams,
200+
const MutableTerm &lookupTerm,
172201
const ProtocolGraph &protos,
173202
RewriteContext &ctx) const {
203+
MutableTerm prefix = getPrefixAfterStrippingKey(lookupTerm);
174204
return getTypeFromSubstitutionSchema(Superclass->getSuperclass(),
175205
Superclass->getSubstitutions(),
176-
genericParams,
177-
protos,
178-
ctx);
206+
genericParams, prefix,
207+
protos, ctx);
179208
}
180209

181210
/// Get the concrete type of the term represented by this property bag.
182211
///
212+
/// The original \p lookupTerm is important in case the concrete type has
213+
/// substitutions. For example, if \p lookupTerm is [P:A].[U:B], and this
214+
/// property bag records that the suffix [U:B] has a concrete type symbol
215+
/// [concrete: Array<τ_0_0> with <[U:C]>], then we actually need to
216+
/// apply the substitution τ_0_0 := [P:A].[U:C] to the type 'Array<τ_0_0>'.
217+
///
183218
/// Asserts if this property bag is not concrete.
184219
Type PropertyBag::getConcreteType(
185220
TypeArrayView<GenericTypeParamType> genericParams,
221+
const MutableTerm &lookupTerm,
186222
const ProtocolGraph &protos,
187223
RewriteContext &ctx) const {
224+
MutableTerm prefix = getPrefixAfterStrippingKey(lookupTerm);
188225
return getTypeFromSubstitutionSchema(ConcreteType->getConcreteType(),
189226
ConcreteType->getSubstitutions(),
190-
genericParams,
191-
protos,
192-
ctx);
227+
genericParams, prefix,
228+
protos, ctx);
193229
}
194230

195231
/// Computes the term corresponding to a member type access on a substitution.

lib/AST/RequirementMachine/PropertyMap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class PropertyBag {
101101

102102
Type getSuperclassBound(
103103
TypeArrayView<GenericTypeParamType> genericParams,
104+
const MutableTerm &lookupTerm,
104105
const ProtocolGraph &protos,
105106
RewriteContext &ctx) const;
106107

@@ -114,6 +115,7 @@ class PropertyBag {
114115

115116
Type getConcreteType(
116117
TypeArrayView<GenericTypeParamType> genericParams,
118+
const MutableTerm &lookupTerm,
117119
const ProtocolGraph &protos,
118120
RewriteContext &ctx) const;
119121

@@ -127,6 +129,8 @@ class PropertyBag {
127129

128130
llvm::TinyPtrVector<const ProtocolDecl *>
129131
getConformsToExcludingSuperclassConformances() const;
132+
133+
MutableTerm getPrefixAfterStrippingKey(const MutableTerm &lookupTerm) const;
130134
};
131135

132136
/// Stores all rewrite rules of the form T.[p] => T, where [p] is a property
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-typecheck-verify-swift -requirement-machine=verify
2+
3+
protocol P {
4+
associatedtype T where T == U?
5+
associatedtype U
6+
}
7+
8+
func sameType<T>(_: T.Type, _: T.Type) {}
9+
10+
func foo<X : P, Y : P>(_: X, _: Y) {
11+
// X.T is Optional<X.U>.
12+
sameType(X.T.self, X.U?.self)
13+
14+
// Y.T is Optional<Y.U>.
15+
sameType(Y.T.self, Y.U?.self)
16+
}

0 commit comments

Comments
 (0)