Skip to content

Commit acee9bd

Browse files
committed
Implement variance checking for parameters of type lambdas.
1 parent e39e683 commit acee9bd

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,8 @@ class Typer extends Namer
13071307
index(tparams)
13081308
val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef])
13091309
val body1 = typedType(tree.body)
1310-
assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1)
1310+
VarianceChecker.checkLambda(
1311+
assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1))
13111312
}
13121313

13131314
def typedMatchTypeTree(tree: untpd.MatchTypeTree, pt: Type)(implicit ctx: Context): Tree = {
@@ -1500,7 +1501,8 @@ class Typer extends Namer
15001501
case rhs @ LambdaTypeTree(tparams, body) =>
15011502
val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]]
15021503
val body1 = typedType(body)
1503-
assignType(cpy.LambdaTypeTree(rhs)(tparams1, body1), tparams1, body1)
1504+
VarianceChecker.checkLambda(
1505+
assignType(cpy.LambdaTypeTree(rhs)(tparams1, body1), tparams1, body1))
15041506
case rhs =>
15051507
typedType(rhs)
15061508
}

compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Types._, Contexts._, Flags._, Symbols._, Trees._
77
import Decorators._
88
import Variances._
99
import NameKinds._
10+
import TypeApplications.varianceConforms
1011
import util.Positions._
1112
import config.Printers.variances
1213
import 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

2463
class VarianceChecker()(implicit ctx: Context) {

tests/neg/type-lambdas.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ object Test extends App {
33
trait Ord[X]
44

55
type TL1 = [X <: Ord[X]] => (X, X) // OK
6-
type TL2 = [X >: Ord[X]] => (X, X) // error
6+
type TL2 = [X >: Ord[X]] => (X, X) // error: illegal cyclic reference: lower bound Test.Ord[X] of type X refers back to the type itself
77

88
class C extends Ord[C]
99

@@ -14,7 +14,8 @@ object Test extends App {
1414
var x: X = init
1515
}
1616

17-
type TL3 = [+X] => Ref[X]
17+
type TL3 = [+X] => Ref[X] // error: covariant type parameter X occurs in nonvariant position in Test.Ref[X]
18+
type TL4[-X] = X => X // error: contravariant type parameter X occurs in covariant position in X => X
1819

1920
def f[F <: [+X] => Any](x: F[String]): F[Any] = x
2021

0 commit comments

Comments
 (0)