@@ -365,7 +365,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
365
365
case _ =>
366
366
compareRefinedSlow ||
367
367
fourthTry(tp1, tp2) ||
368
- compareHkLambda(tp2, tp1, inOrder = false )
368
+ compareHkLambda(tp2, tp1, inOrder = false ) ||
369
+ compareAliasedRefined(tp2, tp1, inOrder = false )
369
370
}
370
371
else // fast path, in particular for refinements resulting from parameterization.
371
372
isSubType(tp1, skipped2) &&
@@ -485,7 +486,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
485
486
}
486
487
isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths
487
488
case tp1 : RefinedType =>
488
- isNewSubType(tp1.parent, tp2) || compareHkLambda(tp1, tp2, inOrder = true )
489
+ isNewSubType(tp1.parent, tp2) ||
490
+ compareHkLambda(tp1, tp2, inOrder = true ) ||
491
+ compareAliasedRefined(tp1, tp2, inOrder = true )
489
492
case AndType (tp11, tp12) =>
490
493
// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
491
494
// and analogously for T11 & (T121 | T122) & T12 <: T2
@@ -608,6 +611,35 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
608
611
false
609
612
}
610
613
614
+ /** Say we are comparing a refined type `P{type M = U}` or `P{type M >: L <: U}`.
615
+ * If P#M refers to a BaseTypeArg aliased to some other typeref P#N,
616
+ * do the same comparison with `P{type N = U}` or `P{type N >: L <: U}`, respectively.
617
+ * This allows to handle situations involving named type params like this one:
618
+ *
619
+ * trait Lambda[type Elem]
620
+ * trait Lst[T] extends Lambda[T]
621
+ *
622
+ * compareAliasedRefined is necessary so we establish that
623
+ *
624
+ * Lst[Int] = Lst[Elem = Int]
625
+ */
626
+ private def compareAliasedRefined (rt : RefinedType , other : Type , inOrder : Boolean ) = {
627
+ val mbr = refinedSymbol(rt)
628
+ mbr.is(BaseTypeArg ) && {
629
+ mbr.info match {
630
+ case TypeAlias (TypeRef (_, aliasName)) =>
631
+ val rt1 = rt.derivedRefinedType(rt.parent, aliasName, rt.refinedInfo)
632
+ subtyping.println(i " rewiring $rt to $rt1 in comparison with $other" )
633
+ if (inOrder) isSubType(rt1, other) else isSubType(other, rt1)
634
+ case _ =>
635
+ false
636
+ }
637
+ }
638
+ }
639
+
640
+ /** The symbol referred to in the refinement of `rt` */
641
+ private def refinedSymbol (rt : RefinedType ) = rt.parent.member(rt.refinedName).symbol
642
+
611
643
/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
612
644
* to keep the constraint as wide as possible. Specifically, if
613
645
*
@@ -736,11 +768,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
736
768
/** A type has been covered previously in subtype checking if it
737
769
* is some combination of TypeRefs that point to classes, where the
738
770
* combiners are RefinedTypes, AndTypes or AnnotatedTypes.
771
+ * One exception: Refinements referring to basetype args are never considered
772
+ * to be already covered. This is necessary because such refined types might
773
+ * still need to be compared with a compareAliasRefined.
739
774
*/
740
775
private def isCovered (tp : Type ): Boolean = tp.dealias.stripTypeVar match {
741
776
case tp : TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass
742
777
case tp : ProtoType => false
743
- case tp : RefinedType => isCovered(tp.parent)
778
+ case tp : RefinedType => isCovered(tp.parent) && ! refinedSymbol(tp).is( BaseTypeArg )
744
779
case tp : AnnotatedType => isCovered(tp.underlying)
745
780
case AndType (tp1, tp2) => isCovered(tp1) && isCovered(tp2)
746
781
case _ => false
0 commit comments