@@ -24,6 +24,7 @@ import typer.Applications.productSelectorTypes
24
24
import reporting .trace
25
25
import annotation .constructorOnly
26
26
import cc .{CapturingType , derivedCapturingType , CaptureSet , stripCapturing , isBoxedCapturing , boxed , boxedUnlessFun , boxedIfTypeParam , isAlwaysPure }
27
+ import NameKinds .WildcardParamName
27
28
28
29
/** Provides methods to compare types.
29
30
*/
@@ -865,10 +866,36 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
865
866
fourthTry
866
867
}
867
868
869
+ /** Can we widen an abstract type when comparing with `tp`?
870
+ * This is the case with the following cases:
871
+ * - if `canWidenAbstract` is true.
872
+ *
873
+ * Secondly, if `tp` is a type parameter, we can widen if:
874
+ * - if `tp` is not a type parameter of the matched-against case lambda
875
+ * - if `tp` is an invariant or wildcard type parameter
876
+ * - finally, allow widening, but record the type parameter in `poisoned`,
877
+ * so that can be accounted for during the reduction step
878
+ */
879
+ def widenAbstractOKFor (tp : Type ): Boolean =
880
+ val acc = new TypeAccumulator [Boolean ]:
881
+ override def apply (x : Boolean , t : Type ) =
882
+ x && t.match
883
+ case t : TypeParamRef =>
884
+ variance == 0
885
+ || (t.binder ne caseLambda)
886
+ || t.paramName.is(WildcardParamName )
887
+ || { poisoned += t; true }
888
+ case _ =>
889
+ foldOver(x, t)
890
+
891
+ canWidenAbstract && acc(true , tp)
892
+
868
893
def tryBaseType (cls2 : Symbol ) = {
869
894
val base = nonExprBaseType(tp1, cls2).boxedIfTypeParam(tp1.typeSymbol)
870
895
if base.exists && (base ne tp1)
871
- && (! caseLambda.exists || canWidenAbstract || tp1.widen.underlyingClassRef(refinementOK = true ).exists)
896
+ && (! caseLambda.exists
897
+ || widenAbstractOKFor(tp2)
898
+ || tp1.widen.underlyingClassRef(refinementOK = true ).exists)
872
899
then
873
900
isSubType(base, tp2, if (tp1.isRef(cls2)) approx else approx.addLow)
874
901
&& recordGadtUsageIf { MatchType .thatReducesUsingGadt(tp1) }
@@ -889,8 +916,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
889
916
|| narrowGADTBounds(tp1, tp2, approx, isUpper = true ))
890
917
&& (tp2.isAny || GADTusage (tp1.symbol))
891
918
892
- (! caseLambda.exists || canWidenAbstract )
893
- && isSubType(hi1.boxedIfTypeParam(tp1.symbol), tp2, approx.addLow) && (trustBounds || isSubType(lo1, tp2, approx.addLow))
919
+ (! caseLambda.exists || widenAbstractOKFor(tp2) )
920
+ && isSubType(hi1.boxedIfTypeParam(tp1.symbol), tp2, approx.addLow) && (trustBounds || isSubType(lo1, tp2, approx.addLow))
894
921
|| compareGADT
895
922
|| tryLiftedToThis1
896
923
case _ =>
@@ -984,7 +1011,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
984
1011
tp1.cases.corresponds(tp2.cases)(isSubType)
985
1012
case _ => false
986
1013
}
987
- recur(tp1.underlying, tp2) || compareMatch
1014
+ ( ! caseLambda.exists || canWidenAbstract) && recur(tp1.underlying, tp2) || compareMatch
988
1015
case tp1 : AnnotatedType if tp1.isRefining =>
989
1016
isNewSubType(tp1.parent)
990
1017
case JavaArrayType (elem1) =>
@@ -3091,6 +3118,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
3091
3118
}
3092
3119
3093
3120
def matchCases (scrut : Type , cases : List [Type ])(using Context ): Type = {
3121
+ // a reference for the type parameters poisoned during matching
3122
+ // for use during the reduction step
3123
+ var poisoned : Set [TypeParamRef ] = Set .empty
3094
3124
3095
3125
def paramInstances (canApprox : Boolean ) = new TypeAccumulator [Array [Type ]]:
3096
3126
def apply (insts : Array [Type ], t : Type ) = t match
@@ -3102,16 +3132,24 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
3102
3132
case entry : TypeBounds =>
3103
3133
val lo = fullLowerBound(param)
3104
3134
val hi = fullUpperBound(param)
3105
- if isSubType(hi, lo) then lo.simplified else Range (lo, hi)
3135
+ if ! poisoned(param) && isSubType(hi, lo) then lo.simplified else Range (lo, hi)
3106
3136
case inst =>
3107
3137
assert(inst.exists, i " param = $param\n constraint = $constraint" )
3108
- inst.simplified
3138
+ if ! poisoned(param) then inst.simplified else Range (inst, inst)
3109
3139
insts
3110
3140
case _ =>
3111
3141
foldOver(insts, t)
3112
3142
3113
3143
def instantiateParams (insts : Array [Type ]) = new ApproximatingTypeMap {
3114
3144
variance = 0
3145
+
3146
+ override def range (lo : Type , hi : Type ): Type =
3147
+ if variance == 0 && (lo eq hi) then
3148
+ // override the default `lo eq hi` test, which removes the Range
3149
+ // which leads to a Reduced result, instead of NoInstance
3150
+ Range (lower(lo), upper(hi))
3151
+ else super .range(lo, hi)
3152
+
3115
3153
def apply (t : Type ) = t match {
3116
3154
case t @ TypeParamRef (b, n) if b `eq` caseLambda => insts(n)
3117
3155
case t : LazyRef => apply(t.ref)
@@ -3133,9 +3171,14 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
3133
3171
3134
3172
def matches (canWidenAbstract : Boolean ): Boolean =
3135
3173
val saved = this .canWidenAbstract
3174
+ val savedPoisoned = this .poisoned
3136
3175
this .canWidenAbstract = canWidenAbstract
3176
+ this .poisoned = Set .empty
3137
3177
try necessarySubType(scrut, pat)
3138
- finally this .canWidenAbstract = saved
3178
+ finally
3179
+ poisoned = this .poisoned
3180
+ this .poisoned = savedPoisoned
3181
+ this .canWidenAbstract = saved
3139
3182
3140
3183
def redux (canApprox : Boolean ): MatchResult =
3141
3184
caseLambda match
0 commit comments