From 036a9bbd0be2b71fc9d60954fd0b1b64c9a181af Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 26 Sep 2014 09:24:50 +1000 Subject: SI-8079 Only expand local aliases during variance checks We've been flip-flopping on this one through the years, right now we issue an two errors for the enclosed test. After this commit, variance validation only expands aliases that are `{private,protected}[this]`. The rest need not be expanded, as we have already variance validated the RHS of the alias. It also removes a seemingly incorrect check in `isLocalOnly`. This also means that we can use `@uncheckedVariance` to create variant type aliases for Java interfaces. However, if such a type alias is declared private local, it *will* be expanded. That shouldn't be a problem, other than for the fact that we run through an as-seen-from that strips the `@uV` annotations in the type expansion. This has been recorded in a pending test. --- src/reflect/scala/reflect/internal/Variances.scala | 10 ++++++---- test/files/neg/t8079a.check | 4 ++++ test/files/neg/t8079a.scala | 4 ++++ test/files/neg/variances.check | 2 +- test/files/pos/t8079b.scala | 7 +++++++ test/pending/pos/t8079c.scala | 7 +++++++ 6 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 test/files/neg/t8079a.check create mode 100644 test/files/neg/t8079a.scala create mode 100644 test/files/pos/t8079b.scala create mode 100644 test/pending/pos/t8079c.scala diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala index 69bade55f1..0b964e81d5 100644 --- a/src/reflect/scala/reflect/internal/Variances.scala +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -53,8 +53,7 @@ trait Variances { // return Bivariant if `sym` is local to a term // or is private[this] or protected[this] def isLocalOnly(sym: Symbol) = !sym.owner.isClass || ( - sym.isTerm // ?? shouldn't this be sym.owner.isTerm according to the comments above? - && (sym.isLocalToThis || sym.isSuperAccessor) // super accessors are implicitly local #4345 + (sym.isLocalToThis || sym.isSuperAccessor) // super accessors are implicitly local #4345 && !escapedLocals(sym) ) @@ -66,7 +65,7 @@ trait Variances { * Initially the state is covariant, but it might change along the search. * * A local alias type is treated as Bivariant; - * this is OK because we always expand aliases for variance checking. + * this is OK because such aliases are expanded for variance checking. * However, for an alias which might be externally visible, we must assume Invariant, * because there may be references to the type parameter that are not checked, * leading to unsoundness (see SI-6566). @@ -126,7 +125,7 @@ trait Variances { tp match { case _ if isUncheckedVariance(tp) => case _ if resultTypeOnly(tp) => this(tp.resultType) - case TypeRef(_, sym, _) if sym.isAliasType => this(tp.normalize) + case TypeRef(_, sym, _) if shouldDealias(sym) => this(tp.normalize) case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; mapOver(tp) case RefinedType(_, _) => withinRefinement(mapOver(tp)) case ClassInfoType(parents, _, _) => parents foreach this @@ -138,6 +137,9 @@ trait Variances { // than the result of the pattern match above, which normalizes types. tp } + private def shouldDealias(sym: Symbol): Boolean = { + sym.isAliasType && isLocalOnly(sym) + } def validateDefinition(base: Symbol) { val saved = this.base this.base = base diff --git a/test/files/neg/t8079a.check b/test/files/neg/t8079a.check new file mode 100644 index 0000000000..6bbe78afa6 --- /dev/null +++ b/test/files/neg/t8079a.check @@ -0,0 +1,4 @@ +t8079a.scala:3: error: contravariant type I occurs in covariant position in type C.this.X of value b + def f2(b: X): Unit + ^ +one error found diff --git a/test/files/neg/t8079a.scala b/test/files/neg/t8079a.scala new file mode 100644 index 0000000000..4997ea282e --- /dev/null +++ b/test/files/neg/t8079a.scala @@ -0,0 +1,4 @@ +trait C[-I] { + private[this] type X = C[I] + def f2(b: X): Unit +} diff --git a/test/files/neg/variances.check b/test/files/neg/variances.check index cb1a60a632..3c1545a375 100644 --- a/test/files/neg/variances.check +++ b/test/files/neg/variances.check @@ -19,7 +19,7 @@ variances.scala:74: error: covariant type A occurs in contravariant position in variances.scala:89: error: covariant type T occurs in invariant position in type T of type A type A = T ^ -variances.scala:90: error: covariant type T occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo +variances.scala:90: error: covariant type A occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo def foo: B[A] ^ 8 errors found diff --git a/test/files/pos/t8079b.scala b/test/files/pos/t8079b.scala new file mode 100644 index 0000000000..f3b7b78077 --- /dev/null +++ b/test/files/pos/t8079b.scala @@ -0,0 +1,7 @@ +trait F1[/* - */T, /* + */ R] + +object Test { + import scala.annotation.unchecked._ + type VariantF1[-T, +R] = F1[T @uncheckedVariance, R @uncheckedVariance] + trait C[+T] { def foo: VariantF1[Any, T] } +} diff --git a/test/pending/pos/t8079c.scala b/test/pending/pos/t8079c.scala new file mode 100644 index 0000000000..ae7f37e2bf --- /dev/null +++ b/test/pending/pos/t8079c.scala @@ -0,0 +1,7 @@ +trait F1[/* - */T, /* + */ R] + +object Test { + import scala.annotation.unchecked._ + private[this] type VariantF1[-T, +R] = F1[T @uncheckedVariance, R @uncheckedVariance] + trait C[+T] { def foo: VariantF1[Any, T] } +} -- cgit v1.2.3