@@ -11,7 +11,7 @@ import collection.mutable
11
11
import util .{Stats , NoSourcePosition , EqHashMap }
12
12
import config .Config
13
13
import config .Feature .{betterMatchTypeExtractorsEnabled , migrateTo3 , sourceVersion }
14
- import config .Printers .{subtyping , gadts , matchTypes , noPrinter }
14
+ import config .Printers .{subtyping , gadts , matchTypes , capt , noPrinter }
15
15
import config .SourceVersion
16
16
import TypeErasure .{erasedLub , erasedGlb }
17
17
import TypeApplications .*
@@ -47,7 +47,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
47
47
GADTused = false
48
48
opaquesUsed = false
49
49
openedExistentials = Nil
50
- assocExistentials = Map .empty
50
+ assocExistentials = Nil
51
51
recCount = 0
52
52
needsGc = false
53
53
if Config .checkTypeComparerReset then checkReset()
@@ -76,7 +76,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
76
76
* Each existential gets mapped to the opened existentials to which it
77
77
* may resolve at this point.
78
78
*/
79
- private var assocExistentials : Map [ TermParamRef , List [ TermParamRef ]] = Map .empty
79
+ private var assocExistentials : ExAssoc = Nil
80
80
81
81
private var myInstance : TypeComparer = this
82
82
def currentInstance : TypeComparer = myInstance
@@ -358,7 +358,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
358
358
isMatchedByProto(tp2, tp1)
359
359
case tp2 : BoundType =>
360
360
tp2 == tp1
361
- || existentialVarsConform(tp1, tp2)
362
361
|| secondTry
363
362
case tp2 : TypeVar =>
364
363
recur(tp1, typeVarInstance(tp2))
@@ -2787,6 +2786,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2787
2786
false
2788
2787
}
2789
2788
2789
+ // ----------- Capture checking -----------------------------------------------
2790
+
2791
+ /** A type associating instantiatable existentials on the right of a comparison
2792
+ * with the existentials they can be instantiated with.
2793
+ */
2794
+ type ExAssoc = List [(TermParamRef , List [TermParamRef ])]
2795
+
2790
2796
private def compareExistentialLeft (boundVar : TermParamRef , tp1unpacked : Type , tp2 : Type )(using Context ): Boolean =
2791
2797
val saved = openedExistentials
2792
2798
try
@@ -2798,16 +2804,32 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2798
2804
private def compareExistentialRight (tp1 : Type , boundVar : TermParamRef , tp2unpacked : Type )(using Context ): Boolean =
2799
2805
val saved = assocExistentials
2800
2806
try
2801
- assocExistentials = assocExistentials.updated (boundVar, openedExistentials)
2807
+ assocExistentials = (boundVar, openedExistentials) :: assocExistentials
2802
2808
recur(tp1, tp2unpacked)
2803
2809
finally
2804
2810
assocExistentials = saved
2805
2811
2806
- def canSubsumeExistentially (tp1 : TermParamRef , tp2 : CaptureRef )(using Context ): Boolean =
2807
- Existential .isExistentialVar(tp1)
2808
- && assocExistentials.get(tp1).match
2809
- case Some (xs) => ! Existential .isExistentialVar(tp2) || xs.contains(tp2)
2810
- case None => false
2812
+ /** Is `tp1` an existential var that subsumes `tp2`? This is the case if `tp1` is
2813
+ * instantiatable (i.e. it's a key in `assocExistentials`) and one of the
2814
+ * following is true:
2815
+ * - `tp2` is not an existential var,
2816
+ * - `tp1` is associated via `assocExistentials` with `tp2`,
2817
+ * - `tp2` appears as key in `assocExistentials` further out than `tp1`.
2818
+ * The third condition allows to instantiate c2 to c1 in
2819
+ * EX c1: A -> Ex c2. B
2820
+ */
2821
+ def subsumesExistentially (tp1 : TermParamRef , tp2 : CaptureRef )(using Context ): Boolean =
2822
+ def canInstantiateWith (assoc : ExAssoc ): Boolean = assoc match
2823
+ case (bv, bvs) :: assoc1 =>
2824
+ if bv == tp1 then
2825
+ ! Existential .isExistentialVar(tp2)
2826
+ || bvs.contains(tp2)
2827
+ || assoc1.exists(_._1 == tp2)
2828
+ else
2829
+ canInstantiateWith(assoc1)
2830
+ case Nil =>
2831
+ false
2832
+ Existential .isExistentialVar(tp1) && canInstantiateWith(assocExistentials)
2811
2833
2812
2834
/** Are tp1, tp2 termRefs that can be linked? This should never be called
2813
2835
* normally, since exietential variables appear only in capture sets
@@ -2817,16 +2839,70 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2817
2839
private def existentialVarsConform (tp1 : Type , tp2 : Type ) =
2818
2840
tp2 match
2819
2841
case tp2 : TermParamRef => tp1 match
2820
- case tp1 : CaptureRef => canSubsumeExistentially (tp2, tp1)
2842
+ case tp1 : CaptureRef => subsumesExistentially (tp2, tp1)
2821
2843
case _ => false
2822
2844
case _ => false
2823
2845
2846
+ /** bi-map taking existentials to the left of a comparison to matching
2847
+ * existentials on the right. This is not a bijection. However
2848
+ * we have `forwards(backwards(bv)) == bv` for an existentially bound `bv`.
2849
+ * That's enough to qualify as a BiTypeMap.
2850
+ */
2851
+ private class MapExistentials (assoc : ExAssoc )(using Context ) extends BiTypeMap :
2852
+
2853
+ private def bad (t : Type ) =
2854
+ Existential .badExistential
2855
+ .showing(i " existential match not found for $t in $assoc" , capt)
2856
+
2857
+ def apply (t : Type ) = t match
2858
+ case t : TermParamRef if Existential .isExistentialVar(t) =>
2859
+ // Find outermost existential on the right that can be instantiated to `t`,
2860
+ // or `badExistential` if none exists.
2861
+ def findMapped (assoc : ExAssoc ): CaptureRef = assoc match
2862
+ case (bv, assocBvs) :: assoc1 =>
2863
+ val outer = findMapped(assoc1)
2864
+ if ! Existential .isBadExistential(outer) then outer
2865
+ else if assocBvs.contains(t) then bv
2866
+ else bad(t)
2867
+ case Nil =>
2868
+ bad(t)
2869
+ findMapped(assoc)
2870
+ case _ =>
2871
+ mapOver(t)
2872
+
2873
+ /** The inverse takes existentials on the right to the innermost existential
2874
+ * on the left to which they can be instantiated.
2875
+ */
2876
+ lazy val inverse = new BiTypeMap :
2877
+ def apply (t : Type ) = t match
2878
+ case t : TermParamRef if Existential .isExistentialVar(t) =>
2879
+ assoc.find(_._1 == t) match
2880
+ case Some ((_, bvs)) if bvs.nonEmpty => bvs.head
2881
+ case _ => bad(t)
2882
+ case _ =>
2883
+ mapOver(t)
2884
+
2885
+ def inverse = MapExistentials .this
2886
+ override def toString = " MapExistentials.inverse"
2887
+ end inverse
2888
+ end MapExistentials
2889
+
2824
2890
protected def subCaptures (refs1 : CaptureSet , refs2 : CaptureSet , frozen : Boolean )(using Context ): CaptureSet .CompareResult =
2825
- try refs1.subCaptures(refs2, frozen)
2891
+ try
2892
+ if assocExistentials.isEmpty then
2893
+ refs1.subCaptures(refs2, frozen)
2894
+ else
2895
+ val mapped = refs1.map(MapExistentials (assocExistentials))
2896
+ if mapped.elems.exists(Existential .isBadExistential)
2897
+ then CaptureSet .CompareResult .Fail (refs2 :: Nil )
2898
+ else subCapturesMapped(mapped, refs2, frozen)
2826
2899
catch case ex : AssertionError =>
2827
2900
println(i " fail while subCaptures $refs1 <:< $refs2" )
2828
2901
throw ex
2829
2902
2903
+ protected def subCapturesMapped (refs1 : CaptureSet , refs2 : CaptureSet , frozen : Boolean )(using Context ): CaptureSet .CompareResult =
2904
+ refs1.subCaptures(refs2, frozen)
2905
+
2830
2906
/** Is the boxing status of tp1 and tp2 the same, or alternatively, is
2831
2907
* the capture sets `refs1` of `tp1` a subcapture of the empty set?
2832
2908
* In the latter case, boxing status does not matter.
@@ -3291,9 +3367,6 @@ object TypeComparer {
3291
3367
def lub (tp1 : Type , tp2 : Type , canConstrain : Boolean = false , isSoft : Boolean = true )(using Context ): Type =
3292
3368
comparing(_.lub(tp1, tp2, canConstrain = canConstrain, isSoft = isSoft))
3293
3369
3294
- def canSubsumeExistentially (tp1 : TermParamRef , tp2 : CaptureRef )(using Context ) =
3295
- comparing(_.canSubsumeExistentially(tp1, tp2))
3296
-
3297
3370
/** The least upper bound of a list of types */
3298
3371
final def lub (tps : List [Type ])(using Context ): Type =
3299
3372
tps.foldLeft(defn.NothingType : Type )(lub(_,_))
@@ -3366,6 +3439,9 @@ object TypeComparer {
3366
3439
3367
3440
def subCaptures (refs1 : CaptureSet , refs2 : CaptureSet , frozen : Boolean )(using Context ): CaptureSet .CompareResult =
3368
3441
comparing(_.subCaptures(refs1, refs2, frozen))
3442
+
3443
+ def subsumesExistentially (tp1 : TermParamRef , tp2 : CaptureRef )(using Context ) =
3444
+ comparing(_.subsumesExistentially(tp1, tp2))
3369
3445
}
3370
3446
3371
3447
object MatchReducer :
@@ -3881,5 +3957,10 @@ class ExplainingTypeComparer(initctx: Context, short: Boolean) extends TypeCompa
3881
3957
super .subCaptures(refs1, refs2, frozen)
3882
3958
}
3883
3959
3960
+ override def subCapturesMapped (refs1 : CaptureSet , refs2 : CaptureSet , frozen : Boolean )(using Context ): CaptureSet .CompareResult =
3961
+ traceIndented(i " subcaptures mapped $refs1 <:< $refs2 ${if frozen then " frozen" else " " }" ) {
3962
+ super .subCapturesMapped(refs1, refs2, frozen)
3963
+ }
3964
+
3884
3965
def lastTrace (header : String ): String = header + { try b.toString finally b.clear() }
3885
3966
}
0 commit comments