Skip to content

Commit dcf5f9d

Browse files
authored
Better comparisons for type projections (#17092)
We already implemented in essence the rule suggested in lampepfl/dotty-feature-requests#14: ``` Γ ⊨ p : T ------------------------ (Sel-<:-Proj) Γ ⊨ p.A <: T#A ``` This rule is implemented in `isSubPrefix`. But we did not get to check that since we concluded prematurely that an alias type would never match. The alias type in question in i17064.scala was ```scala Outer#Inner ``` Since `Outer` is not a path, the asSeenFrom to recover the info of `Outer#Inner` got approximated with a `Nothing` argument and therefore the alias failed. It is important in this case that we could still succeed with a `isSubPrefix` test, which comes later. So we should not return `false` when the prefix is not a singleton. Fixes #17064
2 parents ee2bfdb + d17da7a commit dcf5f9d

File tree

3 files changed

+25
-4
lines changed

3 files changed

+25
-4
lines changed

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,17 +283,28 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
283283
val ctx = comparerContext
284284
given Context = ctx // optimization for performance
285285
val info2 = tp2.info
286+
287+
/** Does `tp2` have a stable prefix?
288+
* If that's not the case, following an alias via asSeenFrom could be lossy
289+
* so we should not conclude `false` if comparing aliases fails.
290+
* See pos/i17064.scala for a test case
291+
*/
292+
def hasStablePrefix(tp: NamedType) =
293+
tp.prefix.isStable
294+
286295
info2 match
287296
case info2: TypeAlias =>
288297
if recur(tp1, info2.alias) then return true
289-
if tp2.asInstanceOf[TypeRef].canDropAlias then return false
298+
if tp2.asInstanceOf[TypeRef].canDropAlias && hasStablePrefix(tp2) then
299+
return false
290300
case _ =>
291301
tp1 match
292302
case tp1: NamedType =>
293303
tp1.info match {
294304
case info1: TypeAlias =>
295305
if recur(info1.alias, tp2) then return true
296-
if tp1.asInstanceOf[TypeRef].canDropAlias then return false
306+
if tp1.asInstanceOf[TypeRef].canDropAlias && hasStablePrefix(tp2) then
307+
return false
297308
case _ =>
298309
}
299310
val sym2 = tp2.symbol
@@ -302,7 +313,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
302313
// For convenience we want X$ <:< X.type
303314
// This is safe because X$ self-type is X.type
304315
sym1 = sym1.companionModule
305-
if ((sym1 ne NoSymbol) && (sym1 eq sym2))
316+
if (sym1 ne NoSymbol) && (sym1 eq sym2) then
306317
ctx.erasedTypes ||
307318
sym1.isStaticOwner ||
308319
isSubPrefix(tp1.prefix, tp2.prefix) ||

tests/neg/i15525.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def element22(
3737
transmittable20.Type / transmittable21.Type
3838
} = ???
3939

40-
def test22 = // error
40+
def test22 =
4141
Resolution(
4242
element22(
4343
Resolution(element0), Resolution(element0), // error // error

tests/pos/i17064.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class HiddenInner[+O<:Outer](val outer:O){
2+
}
3+
4+
class Outer{
5+
type Inner = HiddenInner[this.type]
6+
}
7+
8+
val o : Outer = new Outer
9+
def a : o.Inner = new o.Inner(o)
10+
val b : Outer#Inner = a // DOES NOT COMPILE

0 commit comments

Comments
 (0)