@@ -7,6 +7,7 @@ import Types._, Contexts._, Flags._, Symbols._, Trees._
77import Decorators ._
88import Variances ._
99import NameKinds ._
10+ import TypeApplications .varianceConforms
1011import util .Positions ._
1112import config .Printers .variances
1213import reporting .trace
@@ -19,6 +20,44 @@ object VarianceChecker {
1920 case class VarianceError (tvar : Symbol , required : Variance )
2021 def check (tree : tpd.Tree )(implicit ctx : Context ): Unit =
2122 new VarianceChecker ()(ctx).Traverser .traverse(tree)
23+
24+ /** Check that variances of type lambda correspond to their occurrences in its body.
25+ * Note: this is achieved by a mechanism separate from checking class type parameters.
26+ * Question: Can the two mechanisms be combined in one?
27+ */
28+ def checkLambda (tree : tpd.LambdaTypeTree )(implicit ctx : Context ): tree.type = {
29+ tree.tpe match {
30+ case tl : HKTypeLambda =>
31+ val checkOK = new TypeAccumulator [Boolean ] {
32+ def error (tref : TypeParamRef ) = {
33+ val VariantName (paramName, v) = tl.paramNames(tref.paramNum).toTermName
34+ val paramVarianceStr = if (v == 0 ) " contra" else " co"
35+ val occursStr = variance match {
36+ case - 1 => " contra"
37+ case 0 => " non"
38+ case 1 => " co"
39+ }
40+ val pos = tree.tparams
41+ .find(_.name.toTermName == paramName)
42+ .map(_.pos)
43+ .getOrElse(tree.pos)
44+ ctx.error(em " ${paramVarianceStr}variant type parameter $paramName occurs in ${occursStr}variant position in ${tl.resType}" , pos)
45+ }
46+ def apply (x : Boolean , t : Type ) = x && {
47+ t match {
48+ case tref : TypeParamRef if tref.binder `eq` tl =>
49+ val v = tl.typeParams(tref.paramNum).paramVariance
50+ varianceConforms(variance, v) || { error(tref); false }
51+ case _ =>
52+ foldOver(x, t)
53+ }
54+ }
55+ }
56+ checkOK.apply(true , tl.resType)
57+ case _ =>
58+ }
59+ tree
60+ }
2261}
2362
2463class VarianceChecker ()(implicit ctx : Context ) {
0 commit comments