From b7d9d197973022d4887635e5e5da6511ca72eee0 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 21 Jan 2020 17:22:18 +0100 Subject: [PATCH] Fix variance computation for parameterized type aliases The variance of a type alias was taken to be the variance of its right-hand side, but that doesn't make sense for a parameterized type alias which is free to appear in any position: variance checking should only kick in when it is applied to something. It was possible to work around this by using a type lambda instead of a type alias, cats just had to do that: https://github.com/typelevel/cats/pull/3264 --- src/reflect/scala/reflect/internal/Symbols.scala | 10 +++++++++- test/files/neg/variance-alias.check | 7 +++++++ test/files/neg/variance-alias.scala | 7 +++++++ test/files/pos/variance-alias.scala | 6 ++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 test/files/neg/variance-alias.check create mode 100644 test/files/neg/variance-alias.scala create mode 100644 test/files/pos/variance-alias.scala diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 10f0e5bddc12..2f7d79dba91d 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -3076,7 +3076,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => class AliasTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) { type TypeOfClonedSymbol = TypeSymbol - override def variance = if (isLocalToThis) Bivariant else info.typeSymbol.variance + override def variance = + // A non-applied parameterized type alias can appear in any variance position + if (typeParams.nonEmpty) + Invariant + else if (isLocalToThis) + Bivariant + else + info.typeSymbol.variance + override def isContravariant = variance.isContravariant override def isCovariant = variance.isCovariant final override def isAliasType = true diff --git a/test/files/neg/variance-alias.check b/test/files/neg/variance-alias.check new file mode 100644 index 000000000000..ed5cafdcbf60 --- /dev/null +++ b/test/files/neg/variance-alias.check @@ -0,0 +1,7 @@ +variance-alias.scala:5: error: covariant type T occurs in invariant position in type => Inv[T] of value a + val a: Inv[({type L[+X] = X})#L[T]] = new Inv[({type L[+X] = X})#L[T]] {} // error + ^ +variance-alias.scala:6: error: covariant type T occurs in invariant position in type => Inv[X.this.Id[T]] of value b + val b: Inv[Id[T]] = new Inv[Id[T]] {} // error + ^ +2 errors diff --git a/test/files/neg/variance-alias.scala b/test/files/neg/variance-alias.scala new file mode 100644 index 000000000000..fcdd4c8f5187 --- /dev/null +++ b/test/files/neg/variance-alias.scala @@ -0,0 +1,7 @@ +trait Inv[A] + +class X[+T] { + type Id[+A] = A + val a: Inv[({type L[+X] = X})#L[T]] = new Inv[({type L[+X] = X})#L[T]] {} // error + val b: Inv[Id[T]] = new Inv[Id[T]] {} // error +} diff --git a/test/files/pos/variance-alias.scala b/test/files/pos/variance-alias.scala new file mode 100644 index 000000000000..94477cea2572 --- /dev/null +++ b/test/files/pos/variance-alias.scala @@ -0,0 +1,6 @@ +trait Tc[F[_]] +object X { + type Id[+A] = A + val a: Tc[({type L[+X] = X})#L] = new Tc[({type L[+X] = X})#L] {} // ok, therefore the following should be too: + val b: Tc[Id] = new Tc[Id] {} // ok +}