@@ -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,48 @@ 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
+ * But no such adaptation is applied for implicit eligibility
1065
+ * testing, so we have to compensate.
1066
+ */
1067
+ def compareCaptured (arg1 : TypeBounds , arg2 : Type ) = tparam match {
1068
+ case tparam : Symbol
1069
+ if leftRoot.isStable || ctx.isAfterTyper || ctx.mode.is(Mode .TypevarsMissContext ) =>
1070
+ val captured = TypeRef (leftRoot, tparam)
1071
+ assert(captured.exists, i " $leftRoot has no member $tparam in isSubArgs( $args1, $args2, $tp1, $tparams2) " )
1072
+ isSubArg(captured, arg2)
1073
+ case _ =>
1074
+ false
1075
+ }
1076
+
1029
1077
def isSubArg (arg1 : Type , arg2 : Type ): Boolean = arg2 match {
1030
1078
case arg2 : TypeBounds =>
1031
1079
val arg1norm = arg1 match {
@@ -1040,13 +1088,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1040
1088
case _ =>
1041
1089
arg1 match {
1042
1090
case arg1 : TypeBounds =>
1043
- tparam match {
1044
- case tparam : Symbol if leftRoot.isStable || ctx.isAfterTyper =>
1045
- val captured = TypeRef (leftRoot, tparam)
1046
- isSubArg(captured, arg2)
1047
- case _ =>
1048
- false
1049
- }
1091
+ compareCaptured(arg1, arg2)
1050
1092
case _ =>
1051
1093
(v > 0 || isSubType(arg2, arg1)) &&
1052
1094
(v < 0 || isSubType(arg1, arg2))
@@ -1061,9 +1103,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1061
1103
val adapted2 = arg2.adaptHkVariances(tparam.paramInfo)
1062
1104
adapted2.ne(arg2) && isSubArg(arg1, adapted2)
1063
1105
}
1064
- } && recur (args1.tail, args2.tail, tparams2.tail)
1106
+ } && recurArgs (args1.tail, args2.tail, tparams2.tail)
1065
1107
1066
- recur (args1, args2, tparams2)
1108
+ recurArgs (args1, args2, tparams2)
1067
1109
}
1068
1110
1069
1111
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
@@ -1826,8 +1868,6 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
1826
1868
1827
1869
object TypeComparer {
1828
1870
1829
- val oldScheme = true
1830
-
1831
1871
/** Class for unification variables used in `natValue`. */
1832
1872
private class AnyConstantType extends UncachedGroundType with ValueType {
1833
1873
var tpe : Type = NoType
@@ -1841,6 +1881,12 @@ object TypeComparer {
1841
1881
private val LoApprox = 1
1842
1882
private val HiApprox = 2
1843
1883
1884
+ /** The approximation state indicates how the pair of types currently compared
1885
+ * relates to the types compared originally.
1886
+ * - `NoApprox`: They are still the same types
1887
+ * - `LoApprox`: The left type is approximated (i.e widened)"
1888
+ * - `HiApprox`: The right type is approximated (i.e narrowed)"
1889
+ */
1844
1890
class ApproxState (private val bits : Int ) extends AnyVal {
1845
1891
override def toString : String = {
1846
1892
val lo = if ((bits & LoApprox ) != 0 ) " LoApprox" else " "
@@ -1854,6 +1900,11 @@ object TypeComparer {
1854
1900
}
1855
1901
1856
1902
val NoApprox : ApproxState = new ApproxState (0 )
1903
+
1904
+ /** A special approximation state to indicate that this is the first time we
1905
+ * compare (approximations of) this pair of types. It's converted to `NoApprox`
1906
+ * in `isSubType`, but also leads to `leftRoot` being set there.
1907
+ */
1857
1908
val FreshApprox : ApproxState = new ApproxState (4 )
1858
1909
1859
1910
/** Show trace of comparison operations when performing `op` as result string */
0 commit comments