@@ -49,7 +49,7 @@ trait Variances {
49
49
else escapedLocals += sym
50
50
}
51
51
52
- protected def issueVarianceError (base : Symbol , sym : Symbol , required : Variance ): Unit = ()
52
+ protected def issueVarianceError (base : Symbol , sym : Symbol , required : Variance , tpe : Type ): Unit = ()
53
53
54
54
// Flip occurrences of type parameters and parameters, unless
55
55
// - it's a constructor, or case class factory or extractor
@@ -66,6 +66,7 @@ trait Variances {
66
66
67
67
private object ValidateVarianceMap extends VariancedTypeMap {
68
68
private [this ] var base : Symbol = _
69
+ private [this ] var lowerBoundStack : List [Symbol ] = Nil
69
70
70
71
/** The variance of a symbol occurrence of `tvar` seen at the level of the definition of `base`.
71
72
* The search proceeds from `base` to the owner of `tvar`.
@@ -85,10 +86,11 @@ trait Variances {
85
86
else v
86
87
87
88
@ 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
90
92
else loop(sym.owner, nextVariance(sym, v))
91
- )
93
+
92
94
loop(base, Covariant )
93
95
}
94
96
def isUncheckedVariance (tp : Type ) = tp match {
@@ -104,17 +106,29 @@ trait Variances {
104
106
def base_s = s " $base in ${base.owner}" + (if (base.owner.isClass) " " else " in " + base.owner.enclClass)
105
107
log(s " verifying $sym_s is $required at $base_s" )
106
108
if (sym.variance != required)
107
- issueVarianceError(base, sym, required)
109
+ issueVarianceError(base, sym, required, base.info )
108
110
}
109
111
}
110
112
override def mapOver (decls : Scope ): Scope = {
111
113
decls foreach (sym => withVariance(if (sym.isAliasType) Invariant else variance)(this (sym.info)))
112
114
decls
113
115
}
116
+
117
+ private def ownerOf (pt : PolyType ): Symbol =
118
+ pt.typeParams.head.owner
119
+
114
120
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
+ }
118
132
}
119
133
120
134
/** For PolyTypes, type parameters are skipped because they are defined
@@ -125,12 +139,13 @@ trait Variances {
125
139
def apply (tp : Type ): Type = {
126
140
tp match {
127
141
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 )
131
145
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)
134
149
case _ => tp.mapOver(this )
135
150
}
136
151
// We're using TypeMap here for type traversal only. To avoid wasteful symbol
@@ -144,17 +159,24 @@ trait Variances {
144
159
// As such, we need to expand references to them to retain soundness. Example: neg/t8079a.scala
145
160
sym.isAliasType && isExemptFromVariance(sym)
146
161
}
147
- def validateDefinition (base : Symbol ): Unit = {
162
+
163
+ def validateDefinition (base : Symbol )(continue : => Unit ): Unit = {
148
164
val saved = this .base
149
165
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
+ }
154
176
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
+ }
158
180
}
159
181
160
182
override def traverse (tree : Tree ): Unit = {
@@ -167,34 +189,31 @@ trait Variances {
167
189
|| sym.owner.isConstructor
168
190
|| sym.owner.isCaseApplyOrUnapply
169
191
)
192
+
170
193
tree match {
171
- case defn : MemberDef if skip =>
194
+ case ModuleDef (_, _, _) | ValDef (_, _, _, _) | DefDef (_, _, _, _, _, _) if skip =>
172
195
debuglog(s " Skipping variance check of ${sym.defString}" )
173
196
case ClassDef (_, _, _, _) | TypeDef (_, _, _, _) =>
174
- validateVariance(sym)
175
- tree.traverse(this )
197
+ ValidateVarianceMap .validateDefinition(sym)(tree.traverse(this ))
176
198
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)
190
211
// scala/bug#7872 These two cases make sure we don't miss variance exploits
191
212
// in originals, e.g. in `foo[({type l[+a] = List[a]})#l]`
192
213
case tt @ TypeTree () if tt.original != null =>
193
214
tt.original.traverse(this )
194
- case tt : TypTree =>
195
- tt.traverse(this )
196
-
197
215
case _ =>
216
+ tree.traverse(this )
198
217
}
199
218
}
200
219
}
@@ -230,7 +249,7 @@ trait Variances {
230
249
case ThisType (_) | ConstantType (_) => Bivariant
231
250
case TypeRef (_, tparam, _) if tparam eq this .tparam => Covariant
232
251
case NullaryMethodType (restpe) => inType(restpe)
233
- case SingleType (pre, sym) => inType(pre)
252
+ case SingleType (pre, _) => inType(pre)
234
253
case TypeRef (pre, _, _) if tp.isHigherKinded => inType(pre) // a type constructor cannot occur in tp's args
235
254
case TypeRef (pre, sym, args) => inType(pre) & inArgs(sym, args)
236
255
case TypeBounds (lo, hi) => inType(lo).flip & inType(hi)
0 commit comments