@@ -60,6 +60,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
6060 /** Indicates whether the subtype check used GADT bounds */
6161 private var GADTused : Boolean = false
6262
63+ protected var canWidenAbstract : Boolean = true
64+
6365 private var myInstance : TypeComparer = this
6466 def currentInstance : TypeComparer = myInstance
6567
@@ -757,9 +759,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
757759
758760 def tryBaseType (cls2 : Symbol ) = {
759761 val base = nonExprBaseType(tp1, cls2)
760- if (base.exists && (base `ne` tp1))
761- isSubType(base, tp2, if (tp1.isRef(cls2)) approx else approx.addLow) ||
762- base.isInstanceOf [OrType ] && fourthTry
762+ if base.exists && (base ne tp1)
763+ && (! caseLambda.exists || canWidenAbstract || tp1.widen.underlyingClassRef(refinementOK = true ).exists)
764+ then
765+ isSubType(base, tp2, if (tp1.isRef(cls2)) approx else approx.addLow)
766+ || base.isInstanceOf [OrType ] && fourthTry
763767 // if base is a disjunction, this might have come from a tp1 type that
764768 // expands to a match type. In this case, we should try to reduce the type
765769 // and compare the redux. This is done in fourthTry
@@ -776,7 +780,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
776780 || narrowGADTBounds(tp1, tp2, approx, isUpper = true ))
777781 && (tp2.isAny || GADTusage (tp1.symbol))
778782
779- isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
783+ (! caseLambda.exists || canWidenAbstract) && isSubType(hi1, tp2, approx.addLow)
784+ || compareGADT
785+ || tryLiftedToThis1
780786 case _ =>
781787 // `Mode.RelaxedOverriding` is only enabled when checking Java overriding
782788 // in explicit nulls, and `Null` becomes a bottom type, which allows
@@ -2851,7 +2857,16 @@ object TypeComparer {
28512857 comparing(_.tracked(op))
28522858}
28532859
2860+ object TrackingTypeComparer :
2861+ enum MatchResult :
2862+ case Reduced (tp : Type )
2863+ case Disjoint
2864+ case Stuck
2865+ case NoInstance (fails : List [(Name , TypeBounds )])
2866+
28542867class TrackingTypeComparer (initctx : Context ) extends TypeComparer (initctx) {
2868+ import TrackingTypeComparer .*
2869+
28552870 init(initctx)
28562871
28572872 override def trackingTypeComparer = this
@@ -2889,31 +2904,36 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
28892904 }
28902905
28912906 def matchCases (scrut : Type , cases : List [Type ])(using Context ): Type = {
2892- def paramInstances = new TypeAccumulator [Array [Type ]] {
2893- def apply (inst : Array [Type ], t : Type ) = t match {
2894- case t @ TypeParamRef (b, n) if b `eq` caseLambda =>
2895- inst(n) = approximation(t, fromBelow = variance >= 0 ).simplified
2896- inst
2907+
2908+ def paramInstances (canApprox : Boolean ) = new TypeAccumulator [Array [Type ]]:
2909+ def apply (insts : Array [Type ], t : Type ) = t match
2910+ case param @ TypeParamRef (b, n) if b eq caseLambda =>
2911+ insts(n) =
2912+ if canApprox then
2913+ approximation(param, fromBelow = variance >= 0 ).simplified
2914+ else constraint.entry(param) match
2915+ case entry : TypeBounds =>
2916+ val lo = fullLowerBound(param)
2917+ val hi = fullUpperBound(param)
2918+ if isSubType(hi, lo) then lo.simplified else Range (lo, hi)
2919+ case inst =>
2920+ assert(inst.exists, i " param = $param\n constraint = $constraint" )
2921+ inst.simplified
2922+ insts
28972923 case _ =>
2898- foldOver(inst, t)
2899- }
2900- }
2924+ foldOver(insts, t)
29012925
2902- def instantiateParams (inst : Array [Type ]) = new TypeMap {
2926+ def instantiateParams (insts : Array [Type ]) = new ApproximatingTypeMap {
2927+ variance = 0
29032928 def apply (t : Type ) = t match {
2904- case t @ TypeParamRef (b, n) if b `eq` caseLambda => inst (n)
2929+ case t @ TypeParamRef (b, n) if b `eq` caseLambda => insts (n)
29052930 case t : LazyRef => apply(t.ref)
29062931 case _ => mapOver(t)
29072932 }
29082933 }
29092934
2910- /** Match a single case.
2911- * @return Some(tp) if the match succeeds with type `tp`
2912- * Some(NoType) if the match fails, and there is an overlap between pattern and scrutinee
2913- * None if the match fails and we should consider the following cases
2914- * because scrutinee and pattern do not overlap
2915- */
2916- def matchCase (cas : Type ): Option [Type ] = trace(i " match case $cas vs $scrut" , matchTypes) {
2935+ /** Match a single case. */
2936+ def matchCase (cas : Type ): MatchResult = trace(i " match case $cas vs $scrut" , matchTypes) {
29172937 val cas1 = cas match {
29182938 case cas : HKTypeLambda =>
29192939 caseLambda = constrained(cas)
@@ -2924,34 +2944,52 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
29242944
29252945 val defn .MatchCase (pat, body) = cas1 : @ unchecked
29262946
2927- if (isSubType(scrut, pat))
2928- // `scrut` is a subtype of `pat`: *It's a Match!*
2929- Some {
2930- caseLambda match {
2931- case caseLambda : HKTypeLambda =>
2932- val instances = paramInstances(new Array (caseLambda.paramNames.length), pat)
2933- instantiateParams(instances)(body).simplified
2934- case _ =>
2935- body
2936- }
2937- }
2947+ def matches (canWidenAbstract : Boolean ): Boolean =
2948+ val saved = this .canWidenAbstract
2949+ this .canWidenAbstract = canWidenAbstract
2950+ try necessarySubType(scrut, pat)
2951+ finally this .canWidenAbstract = saved
2952+
2953+ def redux (canApprox : Boolean ): MatchResult =
2954+ caseLambda match
2955+ case caseLambda : HKTypeLambda =>
2956+ val instances = paramInstances(canApprox)(new Array (caseLambda.paramNames.length), pat)
2957+ instantiateParams(instances)(body) match
2958+ case Range (lo, hi) =>
2959+ MatchResult .NoInstance {
2960+ caseLambda.paramNames.zip(instances).collect {
2961+ case (name, Range (lo, hi)) => (name, TypeBounds (lo, hi))
2962+ }
2963+ }
2964+ case redux =>
2965+ MatchResult .Reduced (redux.simplified)
2966+ case _ =>
2967+ MatchResult .Reduced (body)
2968+
2969+ if caseLambda.exists && matches(canWidenAbstract = false ) then
2970+ redux(canApprox = true )
2971+ else if matches(canWidenAbstract = true ) then
2972+ redux(canApprox = false )
29382973 else if (provablyDisjoint(scrut, pat))
29392974 // We found a proof that `scrut` and `pat` are incompatible.
29402975 // The search continues.
2941- None
2976+ MatchResult . Disjoint
29422977 else
2943- Some ( NoType )
2978+ MatchResult . Stuck
29442979 }
29452980
29462981 def recur (remaining : List [Type ]): Type = remaining match
29472982 case cas :: remaining1 =>
29482983 matchCase(cas) match
2949- case None =>
2984+ case MatchResult . Disjoint =>
29502985 recur(remaining1)
2951- case Some ( NoType ) =>
2986+ case MatchResult . Stuck =>
29522987 MatchTypeTrace .stuck(scrut, cas, remaining1)
29532988 NoType
2954- case Some (tp) =>
2989+ case MatchResult .NoInstance (fails) =>
2990+ MatchTypeTrace .noInstance(scrut, cas, fails)
2991+ NoType
2992+ case MatchResult .Reduced (tp) =>
29552993 tp
29562994 case Nil =>
29572995 val casesText = MatchTypeTrace .noMatchesText(scrut, cases)
0 commit comments