Skip to content

Commit a4b53e2

Browse files
committed
Plug many variance holes (pos and neg)
1 parent 41e9827 commit a4b53e2

File tree

8 files changed

+158
-58
lines changed

8 files changed

+158
-58
lines changed

src/compiler/scala/tools/nsc/typechecker/RefChecks.scala

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -892,9 +892,9 @@ abstract class RefChecks extends Transform {
892892
case ClassInfoType(parents, _, clazz) => "supertype "+intersectionType(parents, clazz.owner)
893893
case _ => "type "+tp
894894
}
895-
override def issueVarianceError(base: Symbol, sym: Symbol, required: Variance): Unit = {
895+
override def issueVarianceError(base: Symbol, sym: Symbol, required: Variance, tpe: Type): Unit = {
896896
reporter.error(base.pos,
897-
s"${sym.variance} $sym occurs in $required position in ${tpString(base.info)} of $base")
897+
s"${sym.variance} $sym occurs in $required position in ${tpString(tpe)} of $base")
898898
}
899899
}
900900

@@ -1774,13 +1774,9 @@ abstract class RefChecks extends Transform {
17741774
result.transform(this)
17751775
}
17761776
result1 match {
1777-
case ClassDef(_, _, _, _)
1778-
| TypeDef(_, _, _, _)
1779-
| ModuleDef(_, _, _) =>
1777+
case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) | ModuleDef(_, _, _) =>
17801778
if (result1.symbol.isLocalToBlock || result1.symbol.isTopLevel)
17811779
varianceValidator.traverse(result1)
1782-
case tt @ TypeTree() if tt.original != null =>
1783-
varianceValidator.traverse(tt.original) // See scala/bug#7872
17841780
case _ =>
17851781
}
17861782

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

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ trait Variances {
4949
else escapedLocals += sym
5050
}
5151

52-
protected def issueVarianceError(base: Symbol, sym: Symbol, required: Variance): Unit = ()
52+
protected def issueVarianceError(base: Symbol, sym: Symbol, required: Variance, tpe: Type): Unit = ()
5353

5454
// Flip occurrences of type parameters and parameters, unless
5555
// - it's a constructor, or case class factory or extractor
@@ -66,6 +66,7 @@ trait Variances {
6666

6767
private object ValidateVarianceMap extends VariancedTypeMap {
6868
private[this] var base: Symbol = _
69+
private[this] var lowerBoundStack: List[Symbol] = Nil
6970

7071
/** The variance of a symbol occurrence of `tvar` seen at the level of the definition of `base`.
7172
* The search proceeds from `base` to the owner of `tvar`.
@@ -85,10 +86,11 @@ trait Variances {
8586
else v
8687

8788
@tailrec
88-
def loop(sym: Symbol, v: Variance): Variance = (
89-
if (sym == tvar.owner || v.isBivariant) v
89+
def loop(sym: Symbol, v: Variance): Variance =
90+
if (v.isBivariant) v
91+
else if (sym == tvar.owner) if (lowerBoundStack.contains(sym)) v.flip else v
9092
else loop(sym.owner, nextVariance(sym, v))
91-
)
93+
9294
loop(base, Covariant)
9395
}
9496
def isUncheckedVariance(tp: Type) = tp match {
@@ -104,17 +106,29 @@ trait Variances {
104106
def base_s = s"$base in ${base.owner}" + (if (base.owner.isClass) "" else " in " + base.owner.enclClass)
105107
log(s"verifying $sym_s is $required at $base_s")
106108
if (sym.variance != required)
107-
issueVarianceError(base, sym, required)
109+
issueVarianceError(base, sym, required, base.info)
108110
}
109111
}
110112
override def mapOver(decls: Scope): Scope = {
111113
decls foreach (sym => withVariance(if (sym.isAliasType) Invariant else variance)(this(sym.info)))
112114
decls
113115
}
116+
117+
private def ownerOf(pt: PolyType): Symbol =
118+
pt.typeParams.head.owner
119+
114120
private def resultTypeOnly(tp: Type) = tp match {
115-
case mt: MethodType => !inRefinement
116-
case pt: PolyType => true
117-
case _ => false
121+
case _: MethodType => !inRefinement
122+
case pt: PolyType => !ownerOf(pt).isLocalDummy
123+
case _ => false
124+
}
125+
126+
private def checkPolyType(pt: PolyType): Unit = pt.typeParams.foreach { tparam =>
127+
validateDefinition(tparam)(())
128+
if (!tparam.isInvariant) {
129+
val required = varianceInType(pt.resultType)(tparam)
130+
if (tparam.variance != required) issueVarianceError(ownerOf(pt), tparam, required, pt)
131+
}
118132
}
119133

120134
/** For PolyTypes, type parameters are skipped because they are defined
@@ -125,12 +139,13 @@ trait Variances {
125139
def apply(tp: Type): Type = {
126140
tp match {
127141
case _ if isUncheckedVariance(tp) =>
128-
case _ if resultTypeOnly(tp) => this(tp.resultType)
129-
case TypeRef(_, sym, _) if shouldDealias(sym) => this(tp.normalize)
130-
case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; tp.mapOver(this)
142+
case _ if resultTypeOnly(tp) => apply(tp.resultType)
143+
case TypeRef(_, sym, _) if shouldDealias(sym) => apply(tp.normalize)
144+
case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym); tp.mapOver(this)
131145
case RefinedType(_, _) => withinRefinement(tp.mapOver(this))
132-
case ClassInfoType(parents, _, _) => parents foreach this
133-
case mt @ MethodType(_, result) => flipped(mt.paramTypes foreach this) ; this(result)
146+
case ClassInfoType(parents, _, _) => parents.foreach(apply)
147+
case mt @ MethodType(_, result) => flipped(mt.paramTypes.foreach(apply)); apply(result)
148+
case pt @ PolyType(_, result) => checkPolyType(pt); apply(result)
134149
case _ => tp.mapOver(this)
135150
}
136151
// We're using TypeMap here for type traversal only. To avoid wasteful symbol
@@ -144,17 +159,24 @@ trait Variances {
144159
// As such, we need to expand references to them to retain soundness. Example: neg/t8079a.scala
145160
sym.isAliasType && isExemptFromVariance(sym)
146161
}
147-
def validateDefinition(base: Symbol): Unit = {
162+
163+
def validateDefinition(base: Symbol)(continue: => Unit): Unit = {
148164
val saved = this.base
149165
this.base = base
150-
try apply(base.info)
151-
finally this.base = saved
152-
}
153-
}
166+
try {
167+
base.info match {
168+
case PolyType(_, TypeBounds(lo, hi)) =>
169+
lowerBoundStack ::= base
170+
try flipped(apply(lo))
171+
finally lowerBoundStack = lowerBoundStack.tail
172+
apply(hi)
173+
case other =>
174+
apply(other)
175+
}
154176

155-
/** Validate variance of info of symbol `base` */
156-
private def validateVariance(base: Symbol): Unit = {
157-
ValidateVarianceMap validateDefinition base
177+
continue
178+
} finally this.base = saved
179+
}
158180
}
159181

160182
override def traverse(tree: Tree): Unit = {
@@ -167,34 +189,31 @@ trait Variances {
167189
|| sym.owner.isConstructor
168190
|| sym.owner.isCaseApplyOrUnapply
169191
)
192+
170193
tree match {
171-
case defn: MemberDef if skip =>
194+
case ModuleDef(_, _, _) | ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) if skip =>
172195
debuglog(s"Skipping variance check of ${sym.defString}")
173196
case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) =>
174-
validateVariance(sym)
175-
tree.traverse(this)
197+
ValidateVarianceMap.validateDefinition(sym)(tree.traverse(this))
176198
case ModuleDef(_, _, _) =>
177-
validateVariance(sym.moduleClass)
178-
tree.traverse(this)
179-
case ValDef(_, _, _, _) =>
180-
validateVariance(sym)
181-
case DefDef(_, _, tparams, vparamss, _, _) =>
182-
validateVariance(sym)
183-
traverseTrees(tparams)
184-
traverseTreess(vparamss)
185-
case Template(_, _, _) =>
186-
tree.traverse(this)
187-
case CompoundTypeTree(templ) =>
188-
tree.traverse(this)
189-
199+
ValidateVarianceMap.validateDefinition(sym.moduleClass)(tree.traverse(this))
200+
case ValDef(_, _, _, rhs) =>
201+
ValidateVarianceMap.validateDefinition(sym)(traverse(rhs))
202+
case DefDef(_, _, tparams, vparamss, _, rhs) =>
203+
ValidateVarianceMap.validateDefinition(sym) {
204+
traverseTrees(tparams)
205+
traverseTreess(vparamss)
206+
traverse(rhs)
207+
}
208+
case TypeApply(fun, targs) =>
209+
for (targ <- targs) ValidateVarianceMap(targ.tpe)
210+
traverse(fun)
190211
// scala/bug#7872 These two cases make sure we don't miss variance exploits
191212
// in originals, e.g. in `foo[({type l[+a] = List[a]})#l]`
192213
case tt @ TypeTree() if tt.original != null =>
193214
tt.original.traverse(this)
194-
case tt : TypTree =>
195-
tt.traverse(this)
196-
197215
case _ =>
216+
tree.traverse(this)
198217
}
199218
}
200219
}
@@ -230,7 +249,7 @@ trait Variances {
230249
case ThisType(_) | ConstantType(_) => Bivariant
231250
case TypeRef(_, tparam, _) if tparam eq this.tparam => Covariant
232251
case NullaryMethodType(restpe) => inType(restpe)
233-
case SingleType(pre, sym) => inType(pre)
252+
case SingleType(pre, _) => inType(pre)
234253
case TypeRef(pre, _, _) if tp.isHigherKinded => inType(pre) // a type constructor cannot occur in tp's args
235254
case TypeRef(pre, sym, args) => inType(pre) & inArgs(sym, args)
236255
case TypeBounds(lo, hi) => inType(lo).flip & inType(hi)

test/files/neg/t7872.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
t7872.scala:5: error: contravariant type a occurs in covariant position in type [-a]Cov[a] of type l
2+
type l[-a] = Cov[a]
3+
^
14
t7872.scala:6: error: contravariant type a occurs in covariant position in type [-a]Cov[a] of type l
25
type x = {type l[-a] = Cov[a]}
36
^
4-
t7872.scala:8: error: covariant type a occurs in contravariant position in type [+a]Inv[a] of type l
7+
t7872.scala:8: error: covariant type a occurs in contravariant position in type [+a]Inv[a] of value <local l>
58
foo[({type l[+a] = Inv[a]})#l]
69
^
7-
t7872.scala:5: error: contravariant type a occurs in covariant position in type [-a]Cov[a] of type l
8-
type l[-a] = Cov[a]
9-
^
1010
three errors found

test/files/neg/t7872b.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
t7872b.scala:8: error: contravariant type a occurs in covariant position in type [-a]List[a] of type l
1+
t7872b.scala:8: error: contravariant type a occurs in covariant position in type [-a]List[a] of value <local l>
22
def oops1 = down[({type l[-a] = List[a]})#l](List('whatever: Object)).head + "oops"
33
^
4-
t7872b.scala:19: error: covariant type a occurs in contravariant position in type [+a]coinv.Stringer[a] of type l
4+
t7872b.scala:19: error: covariant type a occurs in contravariant position in type [+a]a => String of value <local l>
55
def oops2 = up[({type l[+a] = Stringer[a]})#l]("printed: " + _)
66
^
77
two errors found

test/files/neg/variance-holes.check

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
variance-holes.scala:9: error: covariant type x occurs in contravariant position in type [+x, +y] >: F[x,y] of type F2
2+
def asWiden[F2[+x, +y] >: F[x, y]]: F2[Int, Int] = v
3+
^
4+
variance-holes.scala:2: error: contravariant type A occurs in covariant position in type [-A] >: List[A] of type Lower1
5+
type Lower1[-A] >: List[A]
6+
^
7+
variance-holes.scala:5: error: covariant type x occurs in contravariant position in type [+x] >: F[x] of type G
8+
type G[+x] >: F[x]
9+
^
10+
variance-holes.scala:13: error: covariant type A occurs in contravariant position in type => AnyRef{type T >: A} of method foo
11+
def foo: { type T >: A }
12+
^
13+
variance-holes.scala:17: error: covariant type A occurs in contravariant position in type AnyRef{type T <: A} of value x
14+
def foo(x: { type T <: A }): Unit
15+
^
16+
variance-holes.scala:20: error: covariant type A occurs in contravariant position in type [+A]AnyRef {
17+
def <init>(): Test.Refined3[A]
18+
} of class Refined3
19+
class Refined3[+A] {
20+
^
21+
variance-holes.scala:24: error: covariant type A occurs in contravariant position in type <: AnyRef{type T >: A} of type x
22+
class RefinedLower[+A, x <: { type T >: A }]
23+
^
24+
variance-holes.scala:25: error: covariant type A occurs in contravariant position in type A of value x_=
25+
private[this] class PrivateThis[+A](var x: A)
26+
^
27+
8 errors found

test/files/neg/variance-holes.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
object Test {
2+
type Lower1[-A] >: List[A]
3+
4+
class Lower2[F[-_]] {
5+
type G[+x] >: F[x]
6+
}
7+
8+
class Lower3[F[-_, -_]](v: F[Int, Int]) {
9+
def asWiden[F2[+x, +y] >: F[x, y]]: F2[Int, Int] = v
10+
}
11+
12+
trait Refined1[+A] {
13+
def foo: { type T >: A }
14+
}
15+
16+
trait Refined2[+A] {
17+
def foo(x: { type T <: A }): Unit
18+
}
19+
20+
class Refined3[+A] {
21+
generic[{ type T <: A } => Int]
22+
}
23+
24+
class RefinedLower[+A, x <: { type T >: A }]
25+
private[this] class PrivateThis[+A](var x: A)
26+
27+
def generic[A]: Unit = ()
28+
}

test/files/neg/variances.check

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
variances.scala:4: error: covariant type A occurs in contravariant position in type test.Vector[A] of value x
22
def append(x: Vector[A]): Vector[A]
33
^
4-
variances.scala:75: error: covariant type A occurs in contravariant position in type => A => A of value m
5-
val m: A => A
6-
^
74
variances.scala:18: error: covariant type A occurs in contravariant position in type A of value a
85
private def setA3(a : A) = this.a = a
96
^
@@ -22,4 +19,4 @@ variances.scala:89: error: covariant type T occurs in invariant position in type
2219
variances.scala:90: error: covariant type A occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo
2320
def foo: B[A]
2421
^
25-
8 errors found
22+
7 errors found

test/files/pos/variance-holes.scala

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
object Test {
2+
type Lower1[+A] >: List[A]
3+
4+
class Lower2[F[+_]] {
5+
type G[+x] >: F[x]
6+
}
7+
8+
class Lower3[F[+_, +_]](v: F[Int, Int]) {
9+
def asWiden[F2[+x, +y] >: F[x, y]]: F2[Int, Int] = v
10+
}
11+
12+
trait Refined1[+A] {
13+
def foo: { type T <: A }
14+
}
15+
16+
trait Refined2[+A] {
17+
def foo(x: { type T >: A }): Unit
18+
}
19+
20+
class Refined3[+A] {
21+
generic[{ type T >: A } => Int]
22+
}
23+
24+
class RefinedUpper1[+A, x <: { type T <: A }]
25+
class RefinedUpper2[+A, x <: { type T[_ <: A] }]
26+
trait RefinedLower[+A, x <: { type T[_ >: A] }]
27+
28+
class PrivateThis[+A] {
29+
private[this] object Foo { var x: A = _ }
30+
}
31+
32+
def generic[A]: Unit = ()
33+
}

0 commit comments

Comments
 (0)