Skip to content

Commit 2703543

Browse files
committed
In isSubType, follow aliases only if prefix is a path.
Comment explains why following aliases in general is incomplete and potentially unsound. This makes Iter2 compile, but causes cyclic reference errors for pos/sets.scala.
1 parent b350d20 commit 2703543

File tree

1 file changed

+20
-2
lines changed

1 file changed

+20
-2
lines changed

src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,29 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
144144
def compareNamed(tp1: Type, tp2: NamedType): Boolean = {
145145
implicit val ctx: Context = this.ctx
146146
tp2.info match {
147-
case info2: TypeAlias => firstTry(tp1, info2.alias)
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)
148165
case _ => tp1 match {
149166
case tp1: NamedType =>
150167
tp1.info match {
151-
case info1: TypeAlias => compareNamed(info1.alias, tp2)
168+
case info1: TypeAlias if tp1.prefix.isStable =>
169+
isSubType(info1.alias, tp2)
152170
case _ =>
153171
val sym1 = tp1.symbol
154172
if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol))

0 commit comments

Comments
 (0)