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..a7dc9c6ee3b7 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,22 @@ class Namer { typer: Typer => } sym.info = dummyInfo2 + // 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 => + 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 + 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..d8110401917e --- /dev/null +++ b/tests/neg/i9061.scala @@ -0,0 +1,11 @@ +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)) + val y: Covarify[Contra, Any] = x + println(y.fa.f("abc")) +} \ No newline at end of file