@@ -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
@@ -2849,7 +2855,16 @@ object TypeComparer {
28492855 comparing(_.tracked(op))
28502856}
28512857
2858+ object TrackingTypeComparer :
2859+ enum MatchResult :
2860+ case Reduced (tp : Type )
2861+ case Disjoint
2862+ case Stuck
2863+ case NoInstance (fails : List [(Name , TypeBounds )])
2864+
28522865class TrackingTypeComparer (initctx : Context ) extends TypeComparer (initctx) {
2866+ import TrackingTypeComparer .*
2867+
28532868 init(initctx)
28542869
28552870 override def trackingTypeComparer = this
@@ -2887,31 +2902,36 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
28872902 }
28882903
28892904 def matchCases (scrut : Type , cases : List [Type ])(using Context ): Type = {
2890- def paramInstances = new TypeAccumulator [Array [Type ]] {
2891- def apply (inst : Array [Type ], t : Type ) = t match {
2892- case t @ TypeParamRef (b, n) if b `eq` caseLambda =>
2893- inst(n) = approximation(t, fromBelow = variance >= 0 ).simplified
2894- inst
2905+
2906+ def paramInstances (canApprox : Boolean ) = new TypeAccumulator [Array [Type ]]:
2907+ def apply (insts : Array [Type ], t : Type ) = t match
2908+ case param @ TypeParamRef (b, n) if b eq caseLambda =>
2909+ insts(n) =
2910+ if canApprox then
2911+ approximation(param, fromBelow = variance >= 0 ).simplified
2912+ else constraint.entry(param) match
2913+ case entry : TypeBounds =>
2914+ val lo = fullLowerBound(param)
2915+ val hi = fullUpperBound(param)
2916+ if isSubType(hi, lo) then lo.simplified else Range (lo, hi)
2917+ case inst =>
2918+ assert(inst.exists, i " param = $param\n constraint = $constraint" )
2919+ inst.simplified
2920+ insts
28952921 case _ =>
2896- foldOver(inst, t)
2897- }
2898- }
2922+ foldOver(insts, t)
28992923
2900- def instantiateParams (inst : Array [Type ]) = new TypeMap {
2924+ def instantiateParams (insts : Array [Type ]) = new ApproximatingTypeMap {
2925+ variance = 0
29012926 def apply (t : Type ) = t match {
2902- case t @ TypeParamRef (b, n) if b `eq` caseLambda => inst (n)
2927+ case t @ TypeParamRef (b, n) if b `eq` caseLambda => insts (n)
29032928 case t : LazyRef => apply(t.ref)
29042929 case _ => mapOver(t)
29052930 }
29062931 }
29072932
2908- /** Match a single case.
2909- * @return Some(tp) if the match succeeds with type `tp`
2910- * Some(NoType) if the match fails, and there is an overlap between pattern and scrutinee
2911- * None if the match fails and we should consider the following cases
2912- * because scrutinee and pattern do not overlap
2913- */
2914- def matchCase (cas : Type ): Option [Type ] = trace(i " match case $cas vs $scrut" , matchTypes) {
2933+ /** Match a single case. */
2934+ def matchCase (cas : Type ): MatchResult = trace(i " match case $cas vs $scrut" , matchTypes) {
29152935 val cas1 = cas match {
29162936 case cas : HKTypeLambda =>
29172937 caseLambda = constrained(cas)
@@ -2922,34 +2942,52 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
29222942
29232943 val defn .MatchCase (pat, body) = cas1 : @ unchecked
29242944
2925- if (isSubType(scrut, pat))
2926- // `scrut` is a subtype of `pat`: *It's a Match!*
2927- Some {
2928- caseLambda match {
2929- case caseLambda : HKTypeLambda =>
2930- val instances = paramInstances(new Array (caseLambda.paramNames.length), pat)
2931- instantiateParams(instances)(body).simplified
2932- case _ =>
2933- body
2934- }
2935- }
2945+ def matches (canWidenAbstract : Boolean ): Boolean =
2946+ val saved = this .canWidenAbstract
2947+ this .canWidenAbstract = canWidenAbstract
2948+ try necessarySubType(scrut, pat)
2949+ finally this .canWidenAbstract = saved
2950+
2951+ def redux (canApprox : Boolean ): MatchResult =
2952+ caseLambda match
2953+ case caseLambda : HKTypeLambda =>
2954+ val instances = paramInstances(canApprox)(new Array (caseLambda.paramNames.length), pat)
2955+ instantiateParams(instances)(body) match
2956+ case Range (lo, hi) =>
2957+ MatchResult .NoInstance {
2958+ caseLambda.paramNames.zip(instances).collect {
2959+ case (name, Range (lo, hi)) => (name, TypeBounds (lo, hi))
2960+ }
2961+ }
2962+ case redux =>
2963+ MatchResult .Reduced (redux.simplified)
2964+ case _ =>
2965+ MatchResult .Reduced (body)
2966+
2967+ if caseLambda.exists && matches(canWidenAbstract = false ) then
2968+ redux(canApprox = true )
2969+ else if matches(canWidenAbstract = true ) then
2970+ redux(canApprox = false )
29362971 else if (provablyDisjoint(scrut, pat))
29372972 // We found a proof that `scrut` and `pat` are incompatible.
29382973 // The search continues.
2939- None
2974+ MatchResult . Disjoint
29402975 else
2941- Some ( NoType )
2976+ MatchResult . Stuck
29422977 }
29432978
29442979 def recur (remaining : List [Type ]): Type = remaining match
29452980 case cas :: remaining1 =>
29462981 matchCase(cas) match
2947- case None =>
2982+ case MatchResult . Disjoint =>
29482983 recur(remaining1)
2949- case Some ( NoType ) =>
2984+ case MatchResult . Stuck =>
29502985 MatchTypeTrace .stuck(scrut, cas, remaining1)
29512986 NoType
2952- case Some (tp) =>
2987+ case MatchResult .NoInstance (fails) =>
2988+ MatchTypeTrace .noInstance(scrut, cas, fails)
2989+ NoType
2990+ case MatchResult .Reduced (tp) =>
29532991 tp
29542992 case Nil =>
29552993 val casesText = MatchTypeTrace .noMatchesText(scrut, cases)
0 commit comments