@@ -56,7 +56,7 @@ trait Variances {
56
56
57
57
private object ValidateVarianceMap extends VariancedTypeMap {
58
58
private [this ] var base : Symbol = _
59
- private [this ] var lowerBoundStack : List [ Symbol ] = Nil
59
+ private [this ] var inLowerBoundOf : Symbol = _
60
60
61
61
/** The variance of a symbol occurrence of `tvar` seen at the level of the definition of `base`.
62
62
* The search proceeds from `base` to the owner of `tvar`.
@@ -78,7 +78,9 @@ trait Variances {
78
78
@ tailrec
79
79
def loop (sym : Symbol , v : Variance ): Variance =
80
80
if (v.isBivariant) v
81
- else if (sym == tvar.owner) if (lowerBoundStack.contains(sym)) v.flip else v
81
+ else if (sym == tvar.owner)
82
+ // We can't move this to `shouldFlip`, because it's needed only once at the end.
83
+ if (inLowerBoundOf == sym) v.flip else v
82
84
else loop(sym.owner, nextVariance(sym, v))
83
85
84
86
loop(base, Covariant )
@@ -141,14 +143,32 @@ trait Variances {
141
143
sym.isAliasType && isExemptFromVariance(sym)
142
144
}
143
145
144
- /** Validate the variance of types in the definition of `base`. */
146
+ /** Validate the variance of types in the definition of `base`.
147
+ *
148
+ * Traverse the type signature of `base` and for each type parameter:
149
+ * - Calculate the relative variance between `base` and the type parameter's owner by
150
+ * walking the owner chain of `base`.
151
+ * - Calculate the required variance of the type parameter which is the product of the
152
+ * relative variance and the current variance in the type signature of `base`.
153
+ * - Ensure that the declared variance of the type parameter is compatible with the
154
+ * required variance, otherwise issue an error.
155
+ *
156
+ * Lower bounds need special handling. By default the variance is flipped when entering a
157
+ * lower bound. In most cases this is the correct behaviour except for the type parameters
158
+ * of higher-kinded types. E.g. in `Foo` below `x` occurs in covariant position:
159
+ * `class Foo[F[+_]] { type G[+x] >: F[x] }`
160
+ *
161
+ * To handle this special case, track when entering the lower bound of a HKT in a variable
162
+ * and flip the relative variance for its type parameters. (flipping the variance a second
163
+ * time negates the first flip).
164
+ */
145
165
def validateDefinition (base : Symbol ): Unit = {
146
166
this .base = base
147
167
base.info match {
148
168
case PolyType (_, TypeBounds (lo, hi)) =>
149
- lowerBoundStack :: = base
169
+ inLowerBoundOf = base
150
170
try flipped(apply(lo))
151
- finally lowerBoundStack = lowerBoundStack.tail
171
+ finally inLowerBoundOf = null
152
172
apply(hi)
153
173
case other =>
154
174
apply(other)
@@ -190,6 +210,14 @@ trait Variances {
190
210
}
191
211
}
192
212
213
+ /** Validate the variance of (the type parameters of) PolyTypes in `tpe`.
214
+ *
215
+ * `validateDefinition` cannot handle PolyTypes in arbitrary position, because in general
216
+ * the relative variance of such types cannot be computed by walking the owner chain.
217
+ *
218
+ * Instead this method applies a naive algorithm which is correct but less efficient:
219
+ * use `varianceInType` to check each type parameter of a PolyType separately.
220
+ */
193
221
def validateVarianceOfPolyTypesIn (tpe : Type ): Unit =
194
222
PolyTypeVarianceMap (tpe)
195
223
0 commit comments