@@ -2800,12 +2800,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2800
2800
* property that in all possible contexts, the same match type expression
2801
2801
* is either stuck or reduces to the same case.
2802
2802
*/
2803
- def provablyDisjoint (tp1 : Type , tp2 : Type )(using Context ): Boolean = trace( i " provable disjoint $tp1 , $tp2 " , matchTypes) {
2804
- // println(s" provablyDisjoint(${ tp1.show}, ${ tp2.show})" )
2803
+ def provablyDisjoint (tp1 : Type , tp2 : Type )(using Context ): Boolean =
2804
+ provablyDisjoint(tp1, tp2, null )
2805
2805
2806
- def isEnumValue ( ref : TermRef ) : Boolean =
2807
- val sym = ref.termSymbol
2808
- sym.isAllOf( EnumCase , butNot = JavaDefined )
2806
+ def provablyDisjoint ( tp1 : Type , tp2 : Type , pending : util. HashSet [( Type , Type )] | Null )(
2807
+ using Context ) : Boolean = trace( i " provable disjoint $tp1 , $tp2 " , matchTypes) {
2808
+ // println(s"provablyDisjoint(${tp1.show}, ${tp2.show})" )
2809
2809
2810
2810
@ scala.annotation.tailrec
2811
2811
def disjointnessBoundary (tp : Type ): Type = tp match
@@ -2823,7 +2823,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2823
2823
case tp @ AppliedType (tycon : TypeRef , _) if tycon.symbol.isClass =>
2824
2824
tp
2825
2825
case tp : TermRef =>
2826
- if isEnumValue(tp) then tp
2826
+ val isEnumValue = tp.termSymbol.isAllOf(EnumCase , butNot = JavaDefined )
2827
+ if isEnumValue then tp
2827
2828
else
2828
2829
val optGadtBounds = gadtBounds(tp.symbol)
2829
2830
if optGadtBounds != null then disjointnessBoundary(optGadtBounds.hi)
@@ -2841,17 +2842,21 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2841
2842
end disjointnessBoundary
2842
2843
2843
2844
(disjointnessBoundary(tp1), disjointnessBoundary(tp2)) match
2845
+ // Infinite recursion detection
2846
+ case pair if pending != null && pending.contains(pair) =>
2847
+ false
2848
+
2844
2849
// Cases where there is an intersection or union on the right
2845
2850
case (tp1, tp2 : OrType ) =>
2846
- provablyDisjoint(tp1, tp2.tp1) && provablyDisjoint(tp1, tp2.tp2)
2851
+ provablyDisjoint(tp1, tp2.tp1, pending ) && provablyDisjoint(tp1, tp2.tp2, pending )
2847
2852
case (tp1, tp2 : AndType ) =>
2848
- provablyDisjoint(tp1, tp2.tp1) || provablyDisjoint(tp1, tp2.tp2)
2853
+ provablyDisjoint(tp1, tp2.tp1, pending ) || provablyDisjoint(tp1, tp2.tp2, pending )
2849
2854
2850
2855
// Cases where there is an intersection or union on the left but not on the right
2851
2856
case (tp1 : OrType , tp2) =>
2852
- provablyDisjoint(tp1.tp1, tp2) && provablyDisjoint(tp1.tp2, tp2)
2857
+ provablyDisjoint(tp1.tp1, tp2, pending ) && provablyDisjoint(tp1.tp2, tp2, pending )
2853
2858
case (tp1 : AndType , tp2) =>
2854
- provablyDisjoint(tp1.tp1, tp2) || provablyDisjoint(tp1.tp2, tp2)
2859
+ provablyDisjoint(tp1.tp1, tp2, pending ) || provablyDisjoint(tp1.tp2, tp2, pending )
2855
2860
2856
2861
/* Cases where both are unique values (enum cases or constant types)
2857
2862
*
@@ -2888,18 +2893,18 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2888
2893
*
2889
2894
* Regardless, we do not look at prefixes.
2890
2895
*/
2891
- case (tp1, tp2) =>
2896
+ case tpPair @ (tp1, tp2) =>
2892
2897
val cls1 = tp1.classSymbol.asClass
2893
2898
val cls2 = tp2.classSymbol.asClass
2894
2899
2895
- def isBaseTypeWithDisjointArguments (baseClass : ClassSymbol ): Boolean =
2900
+ def isBaseTypeWithDisjointArguments (baseClass : ClassSymbol , pending : util. HashSet [( Type , Type )] ): Boolean =
2896
2901
if baseClass.typeParams.isEmpty then
2897
2902
// A common mono base class can never be disjoint thanks to type params
2898
2903
false
2899
2904
else
2900
2905
(tp1.baseType(baseClass), tp2.baseType(baseClass)) match
2901
2906
case (AppliedType (tycon1, args1), AppliedType (tycon2, args2)) =>
2902
- provablyDisjointTypeArgs(baseClass, args1, args2)
2907
+ provablyDisjointTypeArgs(baseClass, args1, args2, pending )
2903
2908
case _ =>
2904
2909
false
2905
2910
end isBaseTypeWithDisjointArguments
@@ -2922,16 +2927,22 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2922
2927
* We exclude any base class that is an ancestor of one of the other base classes:
2923
2928
* they are useless, since anything discovered at their level would also be discovered at
2924
2929
* the level of the descendant common base class.
2925
- * Moreover, we have to do this to prevent infinite recursion with curiously-recursive
2926
- * superclasses: for example `class Seq[A] extends SeqOps[A, Seq[A]]`.
2927
2930
*/
2931
+ val innerPending =
2932
+ if pending != null then pending
2933
+ else util.HashSet [(Type , Type )]()
2934
+ innerPending += tpPair
2935
+
2928
2936
val cls2BaseClassSet = SymDenotations .BaseClassSet (cls2.classDenot.baseClasses)
2929
2937
val commonBaseClasses = cls1.classDenot.baseClasses.filter(cls2BaseClassSet.contains(_))
2930
2938
def isAncestorOfOtherBaseClass (cls : ClassSymbol ): Boolean =
2931
2939
commonBaseClasses.exists(other => (other ne cls) && other.derivesFrom(cls))
2932
- commonBaseClasses.exists { baseClass =>
2933
- ! isAncestorOfOtherBaseClass(baseClass) && isBaseTypeWithDisjointArguments(baseClass)
2940
+ val result = commonBaseClasses.exists { baseClass =>
2941
+ ! isAncestorOfOtherBaseClass(baseClass) && isBaseTypeWithDisjointArguments(baseClass, innerPending )
2934
2942
}
2943
+
2944
+ innerPending -= tpPair
2945
+ result
2935
2946
end existsCommonBaseTypeWithDisjointArguments
2936
2947
2937
2948
provablyDisjointClasses(cls1, cls2)
@@ -2941,7 +2952,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2941
2952
2942
2953
private def provablyDisjointClasses (cls1 : Symbol , cls2 : Symbol )(using Context ): Boolean =
2943
2954
def isDecomposable (cls : Symbol ): Boolean =
2944
- cls.is(Sealed ) && ! cls.hasAnonymousChild
2955
+ cls.is(Sealed ) && ! cls.hasAnonymousChild
2945
2956
2946
2957
def decompose (cls : Symbol ): List [Symbol ] =
2947
2958
cls.children.map { child =>
@@ -2979,7 +2990,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2979
2990
false
2980
2991
end provablyDisjointClasses
2981
2992
2982
- private def provablyDisjointTypeArgs (cls : ClassSymbol , args1 : List [Type ], args2 : List [Type ])(using Context ): Boolean =
2993
+ private def provablyDisjointTypeArgs (cls : ClassSymbol , args1 : List [Type ], args2 : List [Type ], pending : util. HashSet [( Type , Type )] )(using Context ): Boolean =
2983
2994
def fullyInstantiated (tp : Type ): Boolean = new TypeAccumulator [Boolean ] {
2984
2995
override def apply (x : Boolean , t : Type ) =
2985
2996
x && {
@@ -2993,11 +3004,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2993
3004
2994
3005
// It is possible to conclude that two types applied are disjoint by
2995
3006
// looking at covariant type parameters if the said type parameters
2996
- // are disjoin and correspond to fields.
3007
+ // are disjoint and correspond to fields.
2997
3008
// (Type parameter disjointness is not enough by itself as it could
2998
3009
// lead to incorrect conclusions for phantom type parameters).
2999
- def covariantDisjoint (tp1 : Type , tp2 : Type ): Boolean =
3000
- provablyDisjoint(tp1, tp2)
3010
+ def covariantDisjoint (tp1 : Type , tp2 : Type , tparam : TypeParamInfo ): Boolean =
3011
+ provablyDisjoint(tp1, tp2, pending) && typeparamCorrespondsToField(cls.appliedRef, tparam )
3001
3012
3002
3013
// In the invariant case, we also use a stronger notion of disjointness:
3003
3014
// we consider fully instantiated types not equal wrt =:= to be disjoint
@@ -3008,66 +3019,26 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
3008
3019
// Note that this is currently a theoretical concern since Dotty
3009
3020
// doesn't have type tags, meaning that users cannot write patterns
3010
3021
// that do type tests on higher kinded types.
3011
- def invariantDisjoint (tp1 : Type , tp2 : Type ): Boolean =
3012
- provablyDisjoint(tp1, tp2) ||
3022
+ def invariantDisjoint (tp1 : Type , tp2 : Type , tparam : TypeParamInfo ): Boolean =
3023
+ provablyDisjoint(tp1, tp2, pending ) ||
3013
3024
! isSameType(tp1, tp2) &&
3014
3025
fullyInstantiated(tp1) && // We can only trust a "no" from `isSameType` when
3015
3026
fullyInstantiated(tp2) // both `tp1` and `tp2` are fully instantiated.
3016
3027
3017
3028
args1.lazyZip(args2).lazyZip(cls.typeParams).exists {
3018
3029
(arg1, arg2, tparam) =>
3019
- val v = typeParamVarianceStatusForDisjointness(cls, tparam)
3020
- if (v.is(Covariant ))
3021
- covariantDisjoint(arg1, arg2)
3022
- else if (v.is(Contravariant ))
3030
+ val v = tparam.paramVarianceSign
3031
+ if (v > 0 )
3032
+ covariantDisjoint(arg1, arg2, tparam)
3033
+ else if (v < 0 )
3034
+ // Contravariant case: a value where this type parameter is
3035
+ // instantiated to `Any` belongs to both types.
3023
3036
false
3024
3037
else
3025
- invariantDisjoint(arg1, arg2)
3038
+ invariantDisjoint(arg1, arg2, tparam )
3026
3039
}
3027
3040
end provablyDisjointTypeArgs
3028
3041
3029
- /** The "variance status" of the given type parameter for the purpose of deciding disjointness.
3030
- *
3031
- * The variance status of a type parameter is its variance if it is "usable for deciding disjointness",
3032
- * and `Contravariant` otherwise.
3033
- *
3034
- * - `Contravariant` means that it is not usable.
3035
- * - `Covariant` means that it is usable and requires type arguments to be provably disjoint.
3036
- * - `Invariant` means that it is usable and requires type arguments be either a) provably disjoint
3037
- * or b) not equivalent and fully defined.
3038
- *
3039
- * A contravariant type parameter is never usable for deciding disjointness.
3040
- * We can always instantiate them to their upper bound to find a common type.
3041
- *
3042
- * An invariant type parameter is usable if its bounds do not depend on the prefix.
3043
- *
3044
- * A covariant type parameter is usable if its bounds do not depend on the prefix *and*
3045
- * it corresponds to a field.
3046
- * (Type parameter disjointness is not enough by itself as it could lead to
3047
- * incorrect conclusions for phantom type parameters.)
3048
- */
3049
- private def typeParamVarianceStatusForDisjointness (cls : ClassSymbol , tparam : TypeSymbol )(using Context ): Variance =
3050
- // Should this be cached in `tparam` or its denotation?
3051
-
3052
- if tparam.is(Contravariant ) then
3053
- Contravariant
3054
- else
3055
- val boundsDependOnPrefix = new TypeAccumulator [Boolean ] {
3056
- override def apply (x : Boolean , t : Type ): Boolean =
3057
- x || (t match {
3058
- case t : ThisType => true
3059
- case _ => foldOver(false , t)
3060
- })
3061
- }.apply(false , tparam.info)
3062
-
3063
- if boundsDependOnPrefix then
3064
- Contravariant
3065
- else if tparam.is(Covariant ) && ! typeparamCorrespondsToField(cls.appliedRef, tparam) then
3066
- Contravariant
3067
- else
3068
- tparam.variance
3069
- end typeParamVarianceStatusForDisjointness
3070
-
3071
3042
protected def explainingTypeComparer = ExplainingTypeComparer (comparerContext)
3072
3043
protected def trackingTypeComparer = TrackingTypeComparer (comparerContext)
3073
3044
0 commit comments