@@ -60,6 +60,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
60
60
/** Indicates whether the subtype check used GADT bounds */
61
61
private var GADTused : Boolean = false
62
62
63
+ protected var canWidenAbstract : Boolean = true
64
+
63
65
private var myInstance : TypeComparer = this
64
66
def currentInstance : TypeComparer = myInstance
65
67
@@ -757,9 +759,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
757
759
758
760
def tryBaseType (cls2 : Symbol ) = {
759
761
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
763
767
// if base is a disjunction, this might have come from a tp1 type that
764
768
// expands to a match type. In this case, we should try to reduce the type
765
769
// and compare the redux. This is done in fourthTry
@@ -776,7 +780,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
776
780
|| narrowGADTBounds(tp1, tp2, approx, isUpper = true ))
777
781
&& (tp2.isAny || GADTusage (tp1.symbol))
778
782
779
- isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
783
+ (! caseLambda.exists || canWidenAbstract) && isSubType(hi1, tp2, approx.addLow)
784
+ || compareGADT
785
+ || tryLiftedToThis1
780
786
case _ =>
781
787
// `Mode.RelaxedOverriding` is only enabled when checking Java overriding
782
788
// in explicit nulls, and `Null` becomes a bottom type, which allows
@@ -2851,7 +2857,16 @@ object TypeComparer {
2851
2857
comparing(_.tracked(op))
2852
2858
}
2853
2859
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
+
2854
2867
class TrackingTypeComparer (initctx : Context ) extends TypeComparer (initctx) {
2868
+ import TrackingTypeComparer .*
2869
+
2855
2870
init(initctx)
2856
2871
2857
2872
override def trackingTypeComparer = this
@@ -2889,31 +2904,36 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
2889
2904
}
2890
2905
2891
2906
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
2897
2923
case _ =>
2898
- foldOver(inst, t)
2899
- }
2900
- }
2924
+ foldOver(insts, t)
2901
2925
2902
- def instantiateParams (inst : Array [Type ]) = new TypeMap {
2926
+ def instantiateParams (insts : Array [Type ]) = new ApproximatingTypeMap {
2927
+ variance = 0
2903
2928
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)
2905
2930
case t : LazyRef => apply(t.ref)
2906
2931
case _ => mapOver(t)
2907
2932
}
2908
2933
}
2909
2934
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) {
2917
2937
val cas1 = cas match {
2918
2938
case cas : HKTypeLambda =>
2919
2939
caseLambda = constrained(cas)
@@ -2924,34 +2944,52 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
2924
2944
2925
2945
val defn .MatchCase (pat, body) = cas1 : @ unchecked
2926
2946
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 )
2938
2973
else if (provablyDisjoint(scrut, pat))
2939
2974
// We found a proof that `scrut` and `pat` are incompatible.
2940
2975
// The search continues.
2941
- None
2976
+ MatchResult . Disjoint
2942
2977
else
2943
- Some ( NoType )
2978
+ MatchResult . Stuck
2944
2979
}
2945
2980
2946
2981
def recur (remaining : List [Type ]): Type = remaining match
2947
2982
case cas :: remaining1 =>
2948
2983
matchCase(cas) match
2949
- case None =>
2984
+ case MatchResult . Disjoint =>
2950
2985
recur(remaining1)
2951
- case Some ( NoType ) =>
2986
+ case MatchResult . Stuck =>
2952
2987
MatchTypeTrace .stuck(scrut, cas, remaining1)
2953
2988
NoType
2954
- case Some (tp) =>
2989
+ case MatchResult .NoInstance (fails) =>
2990
+ MatchTypeTrace .noInstance(scrut, cas, fails)
2991
+ NoType
2992
+ case MatchResult .Reduced (tp) =>
2955
2993
tp
2956
2994
case Nil =>
2957
2995
val casesText = MatchTypeTrace .noMatchesText(scrut, cases)
0 commit comments