From cc6edf44edfe666d9ef90d6b724e20efcc3fe019 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 30 May 2020 12:53:21 +0200 Subject: [PATCH 1/2] Fix #9061: Treat `type F <: [X] => G` like `type F[X] <: G`. --- .../src/dotty/tools/dotc/core/Variances.scala | 2 ++ .../src/dotty/tools/dotc/typer/Namer.scala | 20 +++++++++++++++++-- .../src/dotty/tools/dotc/util/common.scala | 8 ++++---- tests/neg/i9061.scala | 9 +++++++++ 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 tests/neg/i9061.scala diff --git a/compiler/src/dotty/tools/dotc/core/Variances.scala b/compiler/src/dotty/tools/dotc/core/Variances.scala index 127c4e6e9a8a..a849152ae71b 100644 --- a/compiler/src/dotty/tools/dotc/core/Variances.scala +++ b/compiler/src/dotty/tools/dotc/core/Variances.scala @@ -156,4 +156,6 @@ object Variances { if (v > 0) "+" else if (v < 0) "-" else "" + + val alwaysInvariant: Any => Invariant.type = Function.const(Invariant) } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index acf103b91757..08894a794710 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -15,6 +15,7 @@ import util.Spans._ import util.Property import collection.mutable import tpd.ListOfTreeDecorator +import Variances.alwaysInvariant import config.{Config, Feature} import config.Printers.typr import Annotations._ @@ -968,7 +969,10 @@ class Namer { typer: Typer => override final def typeSig(sym: Symbol): Type = val tparamSyms = completerTypeParams(sym)(ictx) given ctx as Context = nestedCtx - def abstracted(tp: TypeBounds): TypeBounds = HKTypeLambda.boundsFromParams(tparamSyms, tp) + + def abstracted(tp: TypeBounds): TypeBounds = + HKTypeLambda.boundsFromParams(tparamSyms, tp) + val dummyInfo1 = abstracted(TypeBounds.empty) sym.info = dummyInfo1 sym.setFlag(Provisional) @@ -998,8 +1002,20 @@ class Namer { typer: Typer => } sym.info = dummyInfo2 + // If this type does not have type parameters itself, treat the parameters + // of a type lambda on the RHS as non-variant. E.g. + // type F <: [X] =>> G and type F[X] <: G + // are treated alike. + def addVariances(tp: Type): Type = tp match + case tp @ TypeBounds(lo, hi) => + tp.derivedTypeBounds(addVariances(lo), addVariances(hi)) + case tp: HKTypeLambda if tparamSyms.isEmpty && !tp.isDeclaredVarianceLambda => + tp.withVariances(tp.paramNames.map(alwaysInvariant)) + case _ => + tp + val rhs1 = typedAheadType(rhs) - val rhsBodyType: TypeBounds = rhs1.tpe.toBounds + val rhsBodyType: TypeBounds = addVariances(rhs1.tpe).toBounds val unsafeInfo = if (isDerived) rhsBodyType else abstracted(rhsBodyType) def opaqueToBounds(info: Type): Type = diff --git a/compiler/src/dotty/tools/dotc/util/common.scala b/compiler/src/dotty/tools/dotc/util/common.scala index dc71214d028a..85ce9a29f2df 100644 --- a/compiler/src/dotty/tools/dotc/util/common.scala +++ b/compiler/src/dotty/tools/dotc/util/common.scala @@ -6,8 +6,8 @@ import core.Types.WildcardType /** Common values hoisted out for performance */ object common { - val alwaysTrue: Any => Boolean = Function.const(true) _ - val alwaysFalse: Any => Boolean = Function.const(false) _ - val alwaysZero: Any => Int = Function.const(0) _ - val alwaysWildcardType: Any => WildcardType.type = Function.const(WildcardType) _ + val alwaysTrue: Any => Boolean = Function.const(true) + val alwaysFalse: Any => Boolean = Function.const(false) + val alwaysZero: Any => Int = Function.const(0) + val alwaysWildcardType: Any => WildcardType.type = Function.const(WildcardType) } diff --git a/tests/neg/i9061.scala b/tests/neg/i9061.scala new file mode 100644 index 000000000000..4688ee0eedd8 --- /dev/null +++ b/tests/neg/i9061.scala @@ -0,0 +1,9 @@ +case class Contra[-A](f: A => Int) + +case class Covarify[+F <: ([A] =>> Any), +A](fa: F[A]) // error: covariant type A occurs in invariant position in type F[A] of value fa + +@main def main = { + val x = Covarify[Contra, Int](Contra[Int](_ + 5)) + val y: Covarify[Contra, Any] = x + println(y.fa.f("abc")) +} \ No newline at end of file From 769e10829ce094a94ae10b1963512286341bc5e7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 30 May 2020 16:21:35 +0200 Subject: [PATCH 2/2] Also fix variances of curried bound parameters --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 16 +++++++++------- tests/neg/i9061.scala | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 08894a794710..a7dc9c6ee3b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1002,15 +1002,17 @@ class Namer { typer: Typer => } sym.info = dummyInfo2 - // If this type does not have type parameters itself, treat the parameters - // of a type lambda on the RHS as non-variant. E.g. - // type F <: [X] =>> G and type F[X] <: G + // Treat the parameters of an upper type lambda bound on the RHS as non-variant. + // E.g. type F <: [X] =>> G and type F[X] <: G // are treated alike. def addVariances(tp: Type): Type = tp match - case tp @ TypeBounds(lo, hi) => - tp.derivedTypeBounds(addVariances(lo), addVariances(hi)) - case tp: HKTypeLambda if tparamSyms.isEmpty && !tp.isDeclaredVarianceLambda => - tp.withVariances(tp.paramNames.map(alwaysInvariant)) + case tp: TypeBounds => + def recur(tp: Type): Type = tp match + case tp: HKTypeLambda if !tp.isDeclaredVarianceLambda => + tp.withVariances(tp.paramNames.map(alwaysInvariant)) + .derivedLambdaType(resType = recur(tp.resType)) + case tp => tp + tp.derivedTypeBounds(tp.lo, recur(tp.hi)) case _ => tp diff --git a/tests/neg/i9061.scala b/tests/neg/i9061.scala index 4688ee0eedd8..d8110401917e 100644 --- a/tests/neg/i9061.scala +++ b/tests/neg/i9061.scala @@ -1,6 +1,8 @@ case class Contra[-A](f: A => Int) case class Covarify[+F <: ([A] =>> Any), +A](fa: F[A]) // error: covariant type A occurs in invariant position in type F[A] of value fa +case class Covarify2[+F[+X] <: ([A] =>> Any), +A](fa: F[Int][A]) // error: covariant type A occurs in invariant position in type F[A] of value fa +case class Covarify3[+F <: [X] =>> [A] =>> Any, +A](fa: F[Int][A]) // error: covariant type A occurs in invariant position in type F[A] of value fa @main def main = { val x = Covarify[Contra, Int](Contra[Int](_ + 5))