@@ -371,7 +371,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
371
371
case _ =>
372
372
compareRefinedSlow ||
373
373
fourthTry(tp1, tp2) ||
374
- compareHkLambda(tp2, tp1, inOrder = false )
374
+ compareHkLambda(tp2, tp1, inOrder = false ) ||
375
+ compareAliasedRefined(tp2, tp1, inOrder = false )
375
376
}
376
377
else // fast path, in particular for refinements resulting from parameterization.
377
378
isSubType(tp1, skipped2) &&
@@ -491,7 +492,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
491
492
}
492
493
isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths
493
494
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 )
495
498
case AndType (tp11, tp12) =>
496
499
// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
497
500
// and analogously for T11 & (T121 | T122) & T12 <: T2
@@ -614,6 +617,35 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
614
617
false
615
618
}
616
619
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
+
617
649
/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
618
650
* to keep the constraint as wide as possible. Specifically, if
619
651
*
@@ -742,11 +774,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
742
774
/** A type has been covered previously in subtype checking if it
743
775
* is some combination of TypeRefs that point to classes, where the
744
776
* 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.
745
780
*/
746
781
private def isCovered (tp : Type ): Boolean = tp.dealias.stripTypeVar match {
747
782
case tp : TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass
748
783
case tp : ProtoType => false
749
- case tp : RefinedType => isCovered(tp.parent)
784
+ case tp : RefinedType => isCovered(tp.parent) && ! refinedSymbol(tp).is( BaseTypeArg )
750
785
case tp : AnnotatedType => isCovered(tp.underlying)
751
786
case AndType (tp1, tp2) => isCovered(tp1) && isCovered(tp2)
752
787
case _ => false
0 commit comments