Skip to content

Commit 66430e0

Browse files
authored
Merge pull request scala#5280 from retronym/ticket/8079
SI-8079 Only expand local aliases during variance checks
2 parents 3304bc3 + 8ef3e6e commit 66430e0

File tree

8 files changed

+43
-9
lines changed

8 files changed

+43
-9
lines changed

src/reflect/scala/reflect/internal/Variances.scala

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,9 @@ trait Variances {
5050
sym.isParameter
5151
&& !(tvar.isTypeParameterOrSkolem && sym.isTypeParameterOrSkolem && tvar.owner == sym.owner)
5252
)
53-
// return Bivariant if `sym` is local to a term
54-
// or is private[this] or protected[this]
55-
def isLocalOnly(sym: Symbol) = !sym.owner.isClass || (
56-
sym.isTerm // ?? shouldn't this be sym.owner.isTerm according to the comments above?
57-
&& (sym.isLocalToThis || sym.isSuperAccessor) // super accessors are implicitly local #4345
53+
// Is `sym` is local to a term or is private[this] or protected[this]?
54+
def isExemptFromVariance(sym: Symbol): Boolean = !sym.owner.isClass || (
55+
(sym.isLocalToThis || sym.isSuperAccessor) // super accessors are implicitly local #4345
5856
&& !escapedLocals(sym)
5957
)
6058

@@ -66,15 +64,15 @@ trait Variances {
6664
* Initially the state is covariant, but it might change along the search.
6765
*
6866
* A local alias type is treated as Bivariant;
69-
* this is OK because we always expand aliases for variance checking.
67+
* this is OK because such aliases are expanded for variance checking.
7068
* However, for an alias which might be externally visible, we must assume Invariant,
7169
* because there may be references to the type parameter that are not checked,
7270
* leading to unsoundness (see SI-6566).
7371
*/
7472
def relativeVariance(tvar: Symbol): Variance = {
7573
def nextVariance(sym: Symbol, v: Variance): Variance = (
7674
if (shouldFlip(sym, tvar)) v.flip
77-
else if (isLocalOnly(sym)) Bivariant
75+
else if (isExemptFromVariance(sym)) Bivariant
7876
else if (sym.isAliasType) (
7977
// Unsound pre-2.11 behavior preserved under -Xsource:2.10
8078
if (settings.isScala211 || sym.isOverridingSymbol) Invariant
@@ -126,7 +124,7 @@ trait Variances {
126124
tp match {
127125
case _ if isUncheckedVariance(tp) =>
128126
case _ if resultTypeOnly(tp) => this(tp.resultType)
129-
case TypeRef(_, sym, _) if sym.isAliasType => this(tp.normalize)
127+
case TypeRef(_, sym, _) if shouldDealias(sym) => this(tp.normalize)
130128
case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; mapOver(tp)
131129
case RefinedType(_, _) => withinRefinement(mapOver(tp))
132130
case ClassInfoType(parents, _, _) => parents foreach this
@@ -138,6 +136,12 @@ trait Variances {
138136
// than the result of the pattern match above, which normalizes types.
139137
tp
140138
}
139+
private def shouldDealias(sym: Symbol): Boolean = {
140+
// The RHS of (private|protected)[this] type aliases are excluded from variance checks. This is
141+
// implemented in relativeVariance.
142+
// As such, we need to expand references to them to retain soundness. Example: neg/t8079a.scala
143+
sym.isAliasType && isExemptFromVariance(sym)
144+
}
141145
def validateDefinition(base: Symbol) {
142146
val saved = this.base
143147
this.base = base

test/files/neg/t8079a.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t8079a.scala:3: error: contravariant type I occurs in covariant position in type C.this.X of value b
2+
def f2(b: X): Unit
3+
^
4+
one error found

test/files/neg/t8079a.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
trait C[-I] {
2+
private[this] type X = C[I]
3+
def f2(b: X): Unit
4+
}

test/files/neg/variances.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ variances.scala:74: error: covariant type A occurs in contravariant position in
1919
variances.scala:89: error: covariant type T occurs in invariant position in type T of type A
2020
type A = T
2121
^
22-
variances.scala:90: error: covariant type T occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo
22+
variances.scala:90: error: covariant type A occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo
2323
def foo: B[A]
2424
^
2525
8 errors found

test/files/pos/t8079b.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
trait F1[/* - */T, /* + */ R]
2+
3+
object Test {
4+
import scala.annotation.unchecked._
5+
type VariantF1[-T, +R] = F1[T @uncheckedVariance, R @uncheckedVariance]
6+
trait C[+T] { def foo: VariantF1[Any, T] }
7+
}

test/pending/neg/t8079d.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t8079d.scala:3: error: contravariant type I occurs in covariant position in type C.this.X of value b
2+
def f2(b: X): Unit
3+
^
4+
one error found

test/pending/neg/t8079d.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
trait C[-I] {
2+
protected[this] type X = C[I]
3+
def f2(b: X): Unit
4+
}

test/pending/pos/t8079c.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
trait F1[/* - */T, /* + */ R]
2+
3+
object Test {
4+
import scala.annotation.unchecked._
5+
private[this] type VariantF1[-T, +R] = F1[T @uncheckedVariance, R @uncheckedVariance]
6+
trait C[+T] { def foo: VariantF1[Any, T] }
7+
}

0 commit comments

Comments
 (0)