@@ -600,37 +600,70 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
600
600
*
601
601
*/
602
602
def instantiate (tp1 : Type , tp2 : Type )(implicit ctx : Context ): Type = {
603
- // map `ThisType` of `tp1` to a type variable
604
- // precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant
605
- val thisTypeMap = new TypeMap {
606
- def apply (t : Type ): Type = t match {
607
- case tp @ ThisType (tref) if ! tref.symbol.isStaticOwner =>
608
- if (tref.symbol.is(Module )) mapOver(tref)
609
- else newTypeVar(TypeBounds .upper(tp.underlying))
610
- case _ =>
611
- mapOver(t)
603
+ // expose abstract type references to their bounds or tvars according to variance
604
+ abstract class AbstractTypeMap (maximize : Boolean )(implicit ctx : Context ) extends TypeMap {
605
+ def expose (tp : TypeRef ): Type = {
606
+ val lo = this (tp.info.loBound)
607
+ val hi = this (tp.info.hiBound)
608
+ val exposed =
609
+ if (variance == 0 )
610
+ newTypeVar(TypeBounds (lo, hi))
611
+ else if (variance == 1 )
612
+ if (maximize) hi else lo
613
+ else
614
+ if (maximize) lo else hi
615
+
616
+ debug.println(s " $tp exposed to =====> $exposed" )
617
+ exposed
612
618
}
613
- }
614
619
615
- // replace type parameter references with bounds
616
- val typeParamMap = new TypeMap {
617
- def apply (t : Type ): Type = t match {
618
- case tp : TypeRef if tp.symbol.is(TypeParam ) && tp.underlying.isInstanceOf [TypeBounds ] =>
620
+ override def mapOver (tp : Type ): Type = tp match {
621
+ case tp : TypeRef if tp.underlying.isInstanceOf [TypeBounds ] =>
619
622
// See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
623
+ expose(tp)
624
+
625
+ case AppliedType (tycon : TypeRef , args) if tycon.underlying.isInstanceOf [TypeBounds ] =>
626
+ val args2 = args.map(this )
627
+ val lo = this (tycon.info.loBound).applyIfParameterized(args2)
628
+ val hi = this (tycon.info.hiBound).applyIfParameterized(args2)
620
629
val exposed =
621
- if (variance == 0 ) newTypeVar(tp.underlying.bounds)
622
- else if (variance == 1 ) mapOver(tp.underlying.hiBound)
623
- else mapOver(tp.underlying.loBound)
630
+ if (variance == 0 )
631
+ newTypeVar(TypeBounds (lo, hi))
632
+ else if (variance == 1 )
633
+ if (maximize) hi else lo
634
+ else
635
+ if (maximize) lo else hi
624
636
625
637
debug.println(s " $tp exposed to =====> $exposed" )
626
638
exposed
639
+
627
640
case _ =>
628
- mapOver(t )
641
+ super . mapOver(tp )
629
642
}
630
643
}
631
644
645
+ // We are checking the possibility of `tp1 <:< tp2`, thus we should
646
+ // minimize `tp1` while maximizing `tp2`. See tests/patmat/3645b.scala
647
+ def childTypeMap (implicit ctx : Context ) = new AbstractTypeMap (maximize = false ) {
648
+ def apply (t : Type ): Type = t.dealias match {
649
+ // map `ThisType` of `tp1` to a type variable
650
+ // precondition: `tp1` should have the same shape as `path.Child`, thus `ThisType` is always covariant
651
+ case tp @ ThisType (tref) if ! tref.symbol.isStaticOwner =>
652
+ if (tref.symbol.is(Module )) this (tref)
653
+ else newTypeVar(TypeBounds .upper(tp.underlying))
654
+
655
+ case tp =>
656
+ mapOver(tp)
657
+ }
658
+ }
659
+
660
+ // replace type parameter references with bounds
661
+ def parentTypeMap (implicit ctx : Context ) = new AbstractTypeMap (maximize = true ) {
662
+ def apply (tp : Type ): Type = mapOver(tp.dealias)
663
+ }
664
+
632
665
// replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala
633
- val instUndetMap = new TypeMap {
666
+ def instUndetMap ( implicit ctx : Context ) = new TypeMap {
634
667
def apply (t : Type ): Type = t match {
635
668
case tvar : TypeVar if ! tvar.isInstantiated => WildcardType (tvar.origin.underlying.bounds)
636
669
case _ => mapOver(t)
@@ -643,17 +676,27 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
643
676
)
644
677
645
678
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
646
- val protoTp1 = thisTypeMap(tp1.appliedTo(tvars))
679
+ val protoTp1 = childTypeMap.apply(tp1.appliedTo(tvars))
680
+
681
+ // If parent contains a reference to an abstract type, then we should
682
+ // refine subtype checking to eliminate abstract types according to
683
+ // variance. As this logic is only needed in exhaustivity check,
684
+ // we manually patch subtyping check instead of changing TypeComparer.
685
+ // See tests/patmat/3645b.scala
686
+ def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent =>
687
+ implicit val ictx = ctx.fresh.setNewTyperState()
688
+ parent.argInfos.nonEmpty && childTypeMap.apply(parent) <:< parentTypeMap.apply(tp2)
689
+ }
647
690
648
691
if (protoTp1 <:< tp2) {
649
692
if (isFullyDefined(protoTp1, force)) protoTp1
650
- else instUndetMap(protoTp1)
693
+ else instUndetMap.apply (protoTp1)
651
694
}
652
695
else {
653
- val protoTp2 = typeParamMap (tp2)
654
- if (protoTp1 <:< protoTp2) {
696
+ val protoTp2 = parentTypeMap.apply (tp2)
697
+ if (protoTp1 <:< protoTp2 || parentQualify ) {
655
698
if (isFullyDefined(AndType (protoTp1, protoTp2), force)) protoTp1
656
- else instUndetMap(protoTp1)
699
+ else instUndetMap.apply (protoTp1)
657
700
}
658
701
else {
659
702
debug.println(s " $protoTp1 <:< $protoTp2 = false " )
0 commit comments