Skip to content

Commit 2bf5eb0

Browse files
authored
Merge pull request #7573 from dotty-staging/fix-#7567
Fix #7567: Variance checking fixes
2 parents 4d66d32 + 3f4f285 commit 2bf5eb0

File tree

2 files changed

+26
-17
lines changed

2 files changed

+26
-17
lines changed

compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,24 +83,21 @@ class VarianceChecker()(implicit ctx: Context) {
8383
private object Validator extends TypeAccumulator[Option[VarianceError]] {
8484
private var base: Symbol = _
8585

86-
/** Is no variance checking needed within definition of `base`? */
87-
def ignoreVarianceIn(base: Symbol): Boolean = (
88-
base.isTerm
89-
|| base.is(Package)
90-
|| base.isAllOf(PrivateLocal)
91-
)
92-
9386
/** The variance of a symbol occurrence of `tvar` seen at the level of the definition of `base`.
9487
* The search proceeds from `base` to the owner of `tvar`.
9588
* Initially the state is covariant, but it might change along the search.
9689
*/
9790
def relativeVariance(tvar: Symbol, base: Symbol, v: Variance = Covariant): Variance = /*trace(i"relative variance of $tvar wrt $base, so far: $v")*/
98-
if (base == tvar.owner) v
99-
else if (base.is(Param) && base.owner.isTerm)
91+
if base == tvar.owner then
92+
v
93+
else if base.is(Param) && base.owner.isTerm && !base.owner.isAllOf(PrivateLocal) then
10094
relativeVariance(tvar, paramOuter(base.owner), flip(v))
101-
else if (ignoreVarianceIn(base.owner)) Bivariant
102-
else if (base.isAliasType) relativeVariance(tvar, base.owner, Invariant)
103-
else relativeVariance(tvar, base.owner, v)
95+
else if base.owner.isTerm || base.owner.is(Package) || base.isAllOf(PrivateLocal) then
96+
Bivariant
97+
else if base.isAliasType then
98+
relativeVariance(tvar, base.owner, Invariant)
99+
else
100+
relativeVariance(tvar, base.owner, v)
104101

105102
/** The next level to take into account when determining the
106103
* relative variance with a method parameter as base. The method
@@ -189,11 +186,10 @@ class VarianceChecker()(implicit ctx: Context) {
189186
override def traverse(tree: Tree)(implicit ctx: Context) = {
190187
def sym = tree.symbol
191188
// No variance check for private/protected[this] methods/values.
192-
def skip =
193-
!sym.exists ||
194-
sym.isAllOf(PrivateLocal) ||
195-
sym.name.is(InlineAccessorName) || // TODO: should we exclude all synthetic members?
196-
sym.is(TypeParam) && sym.owner.isClass // already taken care of in primary constructor of class
189+
def skip = !sym.exists
190+
|| sym.name.is(InlineAccessorName) // TODO: should we exclude all synthetic members?
191+
|| sym.isAllOf(LocalParamAccessor) // local class parameters are construction only
192+
|| sym.is(TypeParam) && sym.owner.isClass // already taken care of in primary constructor of class
197193
try tree match {
198194
case defn: MemberDef if skip =>
199195
ctx.debuglog(s"Skipping variance check of ${sym.showDcl}")

tests/neg/i7567.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class A
2+
class B extends A
3+
class C extends A
4+
5+
object Foo {
6+
private[this] class Bar[+T](var x: T) // error: covariant type T occurs in contravariant position in type T of value x_=
7+
def foo: B = {
8+
val barB: Bar[B] = new Bar(new B)
9+
val barA: Bar[A] = barB
10+
barA.x = new C
11+
barB.x
12+
}
13+
}

0 commit comments

Comments
 (0)