@@ -130,9 +130,14 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
130
130
}
131
131
}
132
132
133
+ /** The current approximation state. See `ApproxState`. */
133
134
private [this ] var approx : ApproxState = FreshApprox
134
135
protected def approxState : ApproxState = approx
135
136
137
+ /** The original left-hand type of the comparison. Gets reset
138
+ * everytime we compare components of the previous pair of types.
139
+ * This type is used for capture conversion in `isSubArgs`.
140
+ */
136
141
private [this ] var leftRoot : Type = null
137
142
138
143
protected def isSubType (tp1 : Type , tp2 : Type , a : ApproxState ): Boolean = {
@@ -155,6 +160,16 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
155
160
156
161
def isSubType (tp1 : Type , tp2 : Type )(implicit nc : AbsentContext ): Boolean = isSubType(tp1, tp2, FreshApprox )
157
162
163
+ /** The inner loop of the isSubType comparison.
164
+ * Recursive calls from recur should go to recur directly if the two types
165
+ * compared in the callee are essentially the same as the types compared in the
166
+ * caller. "The same" means: represent essentially the same sets of values.
167
+ * `recur` should not be used to compare components of types. In this case
168
+ * one should use `isSubType(_, _)`.
169
+ * `recur` should also not be used to compare approximated versions of the original
170
+ * types (as when we go from an abstract type to one of its bounds). In that case
171
+ * one should use `isSubType(_, _, a)` where `a` defines the kind of approximation
172
+ */
158
173
protected def recur (tp1 : Type , tp2 : Type ): Boolean = trace(s " isSubType ${traceInfo(tp1, tp2)} $approx" , subtyping) {
159
174
160
175
def monitoredIsSubType = {
@@ -1017,15 +1032,49 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1017
1032
*/
1018
1033
def isSubArgs (args1 : List [Type ], args2 : List [Type ], tp1 : Type , tparams2 : List [ParamInfo ]): Boolean = {
1019
1034
1035
+ /** The bounds of parameter `tparam`, where all references to type paramneters
1036
+ * are replaced by corresponding arguments (or their approximations in the case of
1037
+ * wildcard arguments).
1038
+ */
1020
1039
def paramBounds (tparam : Symbol ): TypeBounds =
1021
1040
tparam.info.substApprox(tparams2.asInstanceOf [List [Symbol ]], args2).bounds
1022
1041
1023
- def recur (args1 : List [Type ], args2 : List [Type ], tparams2 : List [ParamInfo ]): Boolean =
1042
+ def recurArgs (args1 : List [Type ], args2 : List [Type ], tparams2 : List [ParamInfo ]): Boolean =
1024
1043
if (args1.isEmpty) args2.isEmpty
1025
1044
else args2.nonEmpty && {
1026
1045
val tparam = tparams2.head
1027
1046
val v = tparam.paramVariance
1028
1047
1048
+ /** Try a capture conversion:
1049
+ * If the original left-hand type `leftRoot` is a path `p.type`,
1050
+ * and the current widened left type is an application with wildcard arguments
1051
+ * such as `C[_]`, where `X` is `C`'s type parameter corresponding to the `_` argument,
1052
+ * compare with `C[p.X]` instead. Otherwise return `false`.
1053
+ * Also do a capture conversion in either of the following cases:
1054
+ *
1055
+ * - If we are after typer. We generally relax soundness requirements then.
1056
+ * We need the relaxed condition to correctly compute overriding relationships.
1057
+ * Missing this case led to AbstractMethod errors in the bootstrap.
1058
+ *
1059
+ * - If we are in mode TypevarsMissContext, which means we test implicits
1060
+ * for eligibility. In this case, we can be more permissive, since it's
1061
+ * just a pre-check. This relaxation is needed since the full
1062
+ * implicit typing might perform an adaptation that skolemizes the
1063
+ * type of a synthesized tree before comparing it with an expected type.
1064
+ * This adaptation gives the tree a path type, so that capture conversion
1065
+ * can do its thing. But no such adaptation is applied for implicit eligibility
1066
+ * testing, so we have to compensate.
1067
+ */
1068
+ def compareCaptured (arg1 : TypeBounds , arg2 : Type ) = tparam match {
1069
+ case tparam : Symbol
1070
+ if leftRoot.isStable || ctx.isAfterTyper || ctx.mode.is(Mode .TypevarsMissContext ) =>
1071
+ val captured = TypeRef (leftRoot, tparam)
1072
+ assert(captured.exists, i " $leftRoot has no member $tparam in isSubArgs( $args1, $args2, $tp1, $tparams2) " )
1073
+ isSubArg(captured, arg2)
1074
+ case _ =>
1075
+ false
1076
+ }
1077
+
1029
1078
def isSubArg (arg1 : Type , arg2 : Type ): Boolean = arg2 match {
1030
1079
case arg2 : TypeBounds =>
1031
1080
val arg1norm = arg1 match {
@@ -1040,14 +1089,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1040
1089
case _ =>
1041
1090
arg1 match {
1042
1091
case arg1 : TypeBounds =>
1043
- tparam match {
1044
- case tparam : Symbol
1045
- if leftRoot.isStable || ctx.isAfterTyper || ctx.mode.is(Mode .TypevarsMissContext ) =>
1046
- val captured = TypeRef (leftRoot, tparam)
1047
- isSubArg(captured, arg2)
1048
- case _ =>
1049
- false
1050
- }
1092
+ compareCaptured(arg1, arg2)
1051
1093
case _ =>
1052
1094
(v > 0 || isSubType(arg2, arg1)) &&
1053
1095
(v < 0 || isSubType(arg1, arg2))
@@ -1062,9 +1104,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1062
1104
val adapted2 = arg2.adaptHkVariances(tparam.paramInfo)
1063
1105
adapted2.ne(arg2) && isSubArg(arg1, adapted2)
1064
1106
}
1065
- } && recur (args1.tail, args2.tail, tparams2.tail)
1107
+ } && recurArgs (args1.tail, args2.tail, tparams2.tail)
1066
1108
1067
- recur (args1, args2, tparams2)
1109
+ recurArgs (args1, args2, tparams2)
1068
1110
}
1069
1111
1070
1112
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
@@ -1827,8 +1869,6 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1827
1869
1828
1870
object TypeComparer {
1829
1871
1830
- val oldScheme = true
1831
-
1832
1872
/** Class for unification variables used in `natValue`. */
1833
1873
private class AnyConstantType extends UncachedGroundType with ValueType {
1834
1874
var tpe : Type = NoType
@@ -1842,6 +1882,12 @@ object TypeComparer {
1842
1882
private val LoApprox = 1
1843
1883
private val HiApprox = 2
1844
1884
1885
+ /** The approximation state indicates how the pair of types currently compared
1886
+ * relates to the types compared originally.
1887
+ * - `NoApprox`: They are still the same types
1888
+ * - `LoApprox`: The left type is approximated (i.e widened)"
1889
+ * - `HiApprox`: The right type is approximated (i.e narrowed)"
1890
+ */
1845
1891
class ApproxState (private val bits : Int ) extends AnyVal {
1846
1892
override def toString : String = {
1847
1893
val lo = if ((bits & LoApprox ) != 0 ) " LoApprox" else " "
@@ -1855,6 +1901,11 @@ object TypeComparer {
1855
1901
}
1856
1902
1857
1903
val NoApprox : ApproxState = new ApproxState (0 )
1904
+
1905
+ /** A special approximation state to indicate that this is the first time we
1906
+ * compare (approximations of) this pair of types. It's converted to `NoApprox`
1907
+ * in `isSubType`, but also leads to `leftRoot` being set there.
1908
+ */
1858
1909
val FreshApprox : ApproxState = new ApproxState (4 )
1859
1910
1860
1911
/** Show trace of comparison operations when performing `op` as result string */
0 commit comments