Skip to content

Commit 356e59c

Browse files
committed
Add some flexibility in comparing named and unnamed parameterized types.
1 parent 9dd07aa commit 356e59c

File tree

2 files changed

+96
-3
lines changed

2 files changed

+96
-3
lines changed

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

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
371371
case _ =>
372372
compareRefinedSlow ||
373373
fourthTry(tp1, tp2) ||
374-
compareHkLambda(tp2, tp1, inOrder = false)
374+
compareHkLambda(tp2, tp1, inOrder = false) ||
375+
compareAliasedRefined(tp2, tp1, inOrder = false)
375376
}
376377
else // fast path, in particular for refinements resulting from parameterization.
377378
isSubType(tp1, skipped2) &&
@@ -491,7 +492,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
491492
}
492493
isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths
493494
case tp1: RefinedType =>
494-
isNewSubType(tp1.parent, tp2) || compareHkLambda(tp1, tp2, inOrder = true)
495+
isNewSubType(tp1.parent, tp2) ||
496+
compareHkLambda(tp1, tp2, inOrder = true) ||
497+
compareAliasedRefined(tp1, tp2, inOrder = true)
495498
case AndType(tp11, tp12) =>
496499
// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
497500
// and analogously for T11 & (T121 | T122) & T12 <: T2
@@ -614,6 +617,35 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
614617
false
615618
}
616619

620+
/** Say we are comparing a refined type `P{type M = U}` or `P{type M >: L <: U}`.
621+
* If P#M refers to a BaseTypeArg aliased to some other typeref P#N,
622+
* do the same comparison with `P{type N = U}` or `P{type N >: L <: U}`, respectively.
623+
* This allows to handle situations involving named type params like this one:
624+
*
625+
* trait Lambda[type Elem]
626+
* trait Lst[T] extends Lambda[T]
627+
*
628+
* compareAliasedRefined is necessary so we establish that
629+
*
630+
* Lst[Int] = Lst[Elem = Int]
631+
*/
632+
private def compareAliasedRefined(rt: RefinedType, other: Type, inOrder: Boolean) = {
633+
val mbr = refinedSymbol(rt)
634+
mbr.is(BaseTypeArg) && {
635+
mbr.info match {
636+
case TypeAlias(TypeRef(_, aliasName)) =>
637+
val rt1 = rt.derivedRefinedType(rt.parent, aliasName, rt.refinedInfo)
638+
subtyping.println(i"rewiring $rt to $rt1 in comparison with $other")
639+
if (inOrder) isSubType(rt1, other) else isSubType(other, rt1)
640+
case _ =>
641+
false
642+
}
643+
}
644+
}
645+
646+
/** The symbol referred to in the refinement of `rt` */
647+
private def refinedSymbol(rt: RefinedType) = rt.parent.member(rt.refinedName).symbol
648+
617649
/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
618650
* to keep the constraint as wide as possible. Specifically, if
619651
*
@@ -742,11 +774,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
742774
/** A type has been covered previously in subtype checking if it
743775
* is some combination of TypeRefs that point to classes, where the
744776
* combiners are RefinedTypes, AndTypes or AnnotatedTypes.
777+
* One exception: Refinements referring to basetype args are never considered
778+
* to be already covered. This is necessary because such refined types might
779+
* still need to be compared with a compareAliasRefined.
745780
*/
746781
private def isCovered(tp: Type): Boolean = tp.dealias.stripTypeVar match {
747782
case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass
748783
case tp: ProtoType => false
749-
case tp: RefinedType => isCovered(tp.parent)
784+
case tp: RefinedType => isCovered(tp.parent) && !refinedSymbol(tp).is(BaseTypeArg)
750785
case tp: AnnotatedType => isCovered(tp.underlying)
751786
case AndType(tp1, tp2) => isCovered(tp1) && isCovered(tp2)
752787
case _ => false

tests/pos/hk-named.scala

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import language.higherKinds
2+
3+
object hk0 {
4+
5+
trait Lambda[type Elem]
6+
7+
abstract class Functor[F <: Lambda] {
8+
def map[A, B](f: A => B): F[Elem = A] => F[Elem = B]
9+
}
10+
11+
object test1 {
12+
class ListT[T] extends Lambda[T]
13+
14+
val ml: Functor[ListT] = ???
15+
val mx = ml
16+
var xs: ListT[Int] = ???
17+
var ys: ListT { type Elem = Int } = xs
18+
xs = ys
19+
val mm: (Int => Boolean) => ListT[Int] => ListT[Boolean] = mx.map[Int, Boolean]
20+
val mm2: (Int => Boolean) => ListT[Int] => ListT[Boolean] = mx.map
21+
}
22+
}
23+
24+
25+
object higherKinded {
26+
27+
type Untyped = Null
28+
29+
class Tree[type -Attr >: Untyped] {
30+
type ThisType <: Tree
31+
def withString(s: String): ThisType[Attr = String] = withString(s)
32+
}
33+
/*
34+
class Ident[-Attr >: Untyped] extends Tree[Attr] {
35+
type ThisType = Ident
36+
}
37+
38+
val id = new Ident[Integer]
39+
40+
val y = id.withString("abc")
41+
42+
val z: Ident[String] = y
43+
44+
val zz: tpd.Tree = y
45+
46+
abstract class Instance[T >: Untyped] {g
47+
type Tree = higherKinded.Tree[T]
48+
}
49+
50+
object tpd extends Instance[String]
51+
52+
def transform(tree: Tree[String]) = {
53+
val tree1 = tree.withString("")
54+
tree1: Tree[String]
55+
}
56+
*/
57+
}
58+

0 commit comments

Comments
 (0)