Skip to content

Commit 45eb099

Browse files
committed
Variances: replace lowerBoundStack with a single flag
Add documentation to the two different variance validation methods: `validateDefinition` and `validateVarianceOfPolyTypesIn`.
1 parent e228a43 commit 45eb099

File tree

1 file changed

+33
-5
lines changed

1 file changed

+33
-5
lines changed

src/reflect/scala/reflect/internal/Variances.scala

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ trait Variances {
5656

5757
private object ValidateVarianceMap extends VariancedTypeMap {
5858
private[this] var base: Symbol = _
59-
private[this] var lowerBoundStack: List[Symbol] = Nil
59+
private[this] var inLowerBoundOf: Symbol = _
6060

6161
/** The variance of a symbol occurrence of `tvar` seen at the level of the definition of `base`.
6262
* The search proceeds from `base` to the owner of `tvar`.
@@ -78,7 +78,9 @@ trait Variances {
7878
@tailrec
7979
def loop(sym: Symbol, v: Variance): Variance =
8080
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
8284
else loop(sym.owner, nextVariance(sym, v))
8385

8486
loop(base, Covariant)
@@ -141,14 +143,32 @@ trait Variances {
141143
sym.isAliasType && isExemptFromVariance(sym)
142144
}
143145

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+
*/
145165
def validateDefinition(base: Symbol): Unit = {
146166
this.base = base
147167
base.info match {
148168
case PolyType(_, TypeBounds(lo, hi)) =>
149-
lowerBoundStack ::= base
169+
inLowerBoundOf = base
150170
try flipped(apply(lo))
151-
finally lowerBoundStack = lowerBoundStack.tail
171+
finally inLowerBoundOf = null
152172
apply(hi)
153173
case other =>
154174
apply(other)
@@ -190,6 +210,14 @@ trait Variances {
190210
}
191211
}
192212

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+
*/
193221
def validateVarianceOfPolyTypesIn(tpe: Type): Unit =
194222
PolyTypeVarianceMap(tpe)
195223

0 commit comments

Comments
 (0)