@@ -144,47 +144,41 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
144
144
def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean = {
145
145
implicit val ctx : Context = this .ctx
146
146
tp2.info match {
147
- case info2 : TypeAlias if tp2.prefix.isStable =>
148
- // If prefix is not stable (i.e. is not a path), then we have a true
149
- // projection `T # A` which is treated as the existential type
150
- // `ex(x: T)x.A`. We need to deal with the existential first before
151
- // following the alias. If we did follow the alias we could be
152
- // unsound as well as incomplete. An example of this was discovered in Iter2.scala.
153
- // It failed to validate the subtype test
154
- //
155
- // (([+X] -> Seq[X]) & C)[SA] <: C[SA]
156
- //
157
- // Both sides are projections of $Apply. The left $Apply does have an
158
- // aliased info, namely, Seq[SA]. But that is not a subtype of C[SA].
159
- // The problem is that, with the prefix not being a path, an aliased info
160
- // does not necessarily give all of the information of the original projection.
161
- // So we can't follow the alias without a backup strategy. If the alias
162
- // would appear on the right then I believe this can be turned into a case
163
- // of unsoundness.
164
- isSubType(tp1, info2.alias)
147
+ case info2 : TypeAlias => isSubType(tp1, info2.alias)
165
148
case _ => tp1 match {
166
149
case tp1 : NamedType =>
167
150
tp1.info match {
168
- case info1 : TypeAlias if tp1.prefix.isStable =>
169
- isSubType(info1.alias, tp2)
151
+ case info1 : TypeAlias =>
152
+ if (isSubType(info1.alias, tp2)) return true
153
+ if (tp1.prefix.isStable) return false
154
+ // If tp1.prefix is stable, the alias does contain all information about the original ref, so
155
+ // there's no need to try something else. (This is important for performance).
156
+ // To see why we cannot in general stop here, consider:
157
+ //
158
+ // trait C { type A }
159
+ // trait D { type A = String }
160
+ // (C & D)#A <: C#A
161
+ //
162
+ // Following the alias leads to the judgment `String <: C#A` which is false.
163
+ // However the original judgment should be true.
170
164
case _ =>
171
- val sym1 = tp1.symbol
172
- if ((sym1 ne NoSymbol ) && (sym1 eq tp2.symbol))
173
- ctx.erasedTypes ||
174
- sym1.isStaticOwner ||
175
- isSubType(tp1.prefix, tp2.prefix) ||
176
- thirdTryNamed(tp1, tp2)
177
- else
178
- ( (tp1.name eq tp2.name)
179
- && isSubType(tp1.prefix, tp2.prefix)
180
- && tp1.signature == tp2.signature
181
- && ! tp1.isInstanceOf [WithFixedSym ]
182
- && ! tp2.isInstanceOf [WithFixedSym ]
183
- ) ||
184
- compareHK(tp1, tp2, inOrder = true ) ||
185
- compareHK(tp2, tp1, inOrder = false ) ||
186
- thirdTryNamed(tp1, tp2)
187
165
}
166
+ val sym1 = tp1.symbol
167
+ if ((sym1 ne NoSymbol ) && (sym1 eq tp2.symbol))
168
+ ctx.erasedTypes ||
169
+ sym1.isStaticOwner ||
170
+ isSubType(tp1.prefix, tp2.prefix) ||
171
+ thirdTryNamed(tp1, tp2)
172
+ else
173
+ ( (tp1.name eq tp2.name)
174
+ && isSubType(tp1.prefix, tp2.prefix)
175
+ && tp1.signature == tp2.signature
176
+ && ! tp1.isInstanceOf [WithFixedSym ]
177
+ && ! tp2.isInstanceOf [WithFixedSym ]
178
+ ) ||
179
+ compareHK(tp1, tp2, inOrder = true ) ||
180
+ compareHK(tp2, tp1, inOrder = false ) ||
181
+ thirdTryNamed(tp1, tp2)
188
182
case _ =>
189
183
compareHK(tp2, tp1, inOrder = false ) ||
190
184
secondTry(tp1, tp2)
@@ -258,11 +252,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
258
252
private def secondTry (tp1 : Type , tp2 : Type ): Boolean = tp1 match {
259
253
case tp1 : NamedType =>
260
254
tp1.info match {
261
- case info1 : TypeAlias => isSubType(info1.alias, tp2)
255
+ case info1 : TypeAlias =>
256
+ if (isSubType(info1.alias, tp2)) return true
257
+ if (tp1.prefix.isStable) return false
262
258
case _ =>
263
- compareHK(tp1, tp2, inOrder = true ) ||
264
- thirdTry(tp1, tp2)
265
259
}
260
+ compareHK(tp1, tp2, inOrder = true ) ||
261
+ thirdTry(tp1, tp2)
266
262
case tp1 : PolyParam =>
267
263
def flagNothingBound = {
268
264
if (! frozenConstraint && tp2.isRef(defn.NothingClass ) && state.isGlobalCommittable) {
@@ -1106,7 +1102,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1106
1102
NoType
1107
1103
}
1108
1104
1109
- /** Try to distribute `|` inside type, detect and handle conflicts.
1105
+ /** Try to distribute `|` inside type, detect and handle conflicts
1110
1106
* Note that, unlike for `&`, a disjunction cannot be pushed into
1111
1107
* a refined or applied type. Example:
1112
1108
*
0 commit comments