@@ -600,37 +600,70 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
600600 *
601601 */
602602 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
612618 }
613- }
614619
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 ] =>
619622 // 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)
620629 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
624636
625637 debug.println(s " $tp exposed to =====> $exposed" )
626638 exposed
639+
627640 case _ =>
628- mapOver(t )
641+ super . mapOver(tp )
629642 }
630643 }
631644
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+
632665 // replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala
633- val instUndetMap = new TypeMap {
666+ def instUndetMap ( implicit ctx : Context ) = new TypeMap {
634667 def apply (t : Type ): Type = t match {
635668 case tvar : TypeVar if ! tvar.isInstantiated => WildcardType (tvar.origin.underlying.bounds)
636669 case _ => mapOver(t)
@@ -643,17 +676,27 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
643676 )
644677
645678 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+ }
647690
648691 if (protoTp1 <:< tp2) {
649692 if (isFullyDefined(protoTp1, force)) protoTp1
650- else instUndetMap(protoTp1)
693+ else instUndetMap.apply (protoTp1)
651694 }
652695 else {
653- val protoTp2 = typeParamMap (tp2)
654- if (protoTp1 <:< protoTp2) {
696+ val protoTp2 = parentTypeMap.apply (tp2)
697+ if (protoTp1 <:< protoTp2 || parentQualify ) {
655698 if (isFullyDefined(AndType (protoTp1, protoTp2), force)) protoTp1
656- else instUndetMap(protoTp1)
699+ else instUndetMap.apply (protoTp1)
657700 }
658701 else {
659702 debug.println(s " $protoTp1 <:< $protoTp2 = false " )
0 commit comments