Skip to content

Fix #1252: Fix variance checking for parameterized typedefs #6318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,29 @@ object Trees {
def forwardTo: Tree[T] = tpt
}

/** [typeparams] -> tpt */
/** [typeparams] -> tpt
*
* Note: the type of such a tree is not necessarily a `HKTypeLambda`, it can
* also be a `TypeBounds` where the upper bound is an `HKTypeLambda`, and the
* lower bound is either a reference to `Nothing` or an `HKTypeLambda`,
* this happens because these trees are typed by `HKTypeLambda#fromParams` which
* makes sure to move bounds outside of the type lambda itself to simplify their
* handling in the compiler.
*
* You may ask: why not normalize the trees too? That way,
*
* LambdaTypeTree(X, TypeBoundsTree(A, B))
*
* would become,
*
* TypeBoundsTree(LambdaTypeTree(X, A), LambdaTypeTree(X, B))
*
* which would maintain consistency between a tree and its type. The problem
* with this definition is that the same tree `X` appears twice, therefore
* we'd have to create two symbols for it which makes it harder to relate the
* source code written by the user with the trees used by the compiler (for
* example, to make "find all references" work in the IDE).
*/
case class LambdaTypeTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TypTree[T] {
type ThisTree[-T >: Untyped] = LambdaTypeTree[T]
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3358,9 +3358,9 @@ object Types {
param.paramName.withVariance(param.paramVariance)

/** Distributes Lambda inside type bounds. Examples:
*
* type T[X] = U becomes type T = [X] -> U
* type T[X] <: U becomes type T >: Nothign <: ([X] -> U)
*
* type T[X] = U becomes type T = [X] -> U
* type T[X] <: U becomes type T >: Nothing <: ([X] -> U)
* type T[X] >: L <: U becomes type T >: ([X] -> L) <: ([X] -> U)
*/
override def fromParams[PI <: ParamInfo.Of[TypeName]](params: List[PI], resultType: Type)(implicit ctx: Context): Type = {
Expand Down
22 changes: 19 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ object VarianceChecker {
* Note: this is achieved by a mechanism separate from checking class type parameters.
* Question: Can the two mechanisms be combined in one?
*/
def checkLambda(tree: tpd.LambdaTypeTree)(implicit ctx: Context): Unit = tree.tpe match {
case tl: HKTypeLambda =>
def checkLambda(tree: tpd.LambdaTypeTree)(implicit ctx: Context): Unit = {
def checkType(tl: HKTypeLambda): Unit = {
val checkOK = new TypeAccumulator[Boolean] {
def error(tref: TypeParamRef) = {
val VariantName(paramName, v) = tl.paramNames(tref.paramNum).toTermName
Expand Down Expand Up @@ -56,7 +56,23 @@ object VarianceChecker {
}
}
checkOK.apply(true, tl.resType)
case _ =>
}

(tree.tpe: @unchecked) match {
case tl: HKTypeLambda =>
checkType(tl)
// The type of a LambdaTypeTree can be a TypeBounds, see the documentation
// of `LambdaTypeTree`.
case TypeBounds(lo, hi: HKTypeLambda) =>
// Can't assume that the lower bound is a type lambda, it could also be
// a reference to `Nothing`.
lo match {
case lo: HKTypeLambda =>
checkType(lo)
case _ =>
}
checkType(hi)
}
}
}

Expand Down
15 changes: 15 additions & 0 deletions tests/neg/type-lambdas-posttyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,19 @@ object Test extends App {
aref.x = 1
val s: String = sref.x

type Neg1[-X] = X // error
type Neg2[-X] >: X // error
type Neg3[-X] <: X // error

type Neg4 = [-X] => X // error
type Neg5 >: [-X] => X <: [-X] => Any // error
type Neg6 <: [-X] => X // error

type Pos1[+X] = Ref[X] // error
type Pos2[+X] >: Ref[X] // error
type Pos3[+X] <: Ref[X] // error

type Pos4 = [+X] => Ref[X] // error
type Pos5 >: [+X] => Ref[X] <: [+X] => Any // error
type Pos6 <: [+X] => Ref[X] // error
}