@@ -257,20 +257,96 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
257
257
* @param boundss The list of type bounds
258
258
* @param instantiate A function that maps a bound type and the list of argument types to a resulting type.
259
259
* Needed to handle bounds that refer to other bounds.
260
+ * @param app The applied type whose arguments are checked, or NoType if
261
+ * arguments are for a TypeApply.
262
+ *
263
+ * This is particularly difficult for F-bounds that also contain wildcard arguments (see below).
264
+ * In fact the current treatment for this sitiuation can so far only be classified as "not obviously wrong",
265
+ * (maybe it still needs to be revised).
260
266
*/
261
- def boundsViolations (args : List [Tree ], boundss : List [TypeBounds ], instantiate : (Type , List [Type ]) => Type )(implicit ctx : Context ): List [BoundsViolation ] = {
267
+ def boundsViolations (args : List [Tree ], boundss : List [TypeBounds ], instantiate : (Type , List [Type ]) => Type , app : Type )(implicit ctx : Context ): List [BoundsViolation ] = {
262
268
val argTypes = args.tpes
269
+
270
+ /** Replace all wildcards in `tps` with `<app>#<tparam>` where `<tparam>` is the
271
+ * type parameter corresponding to the wildcard.
272
+ */
273
+ def skolemizeWildcardArgs (tps : List [Type ], app : Type ) = app match {
274
+ case AppliedType (tycon, args) if tycon.typeSymbol.isClass && ! scala2Mode =>
275
+ tps.zipWithConserve(tycon.typeSymbol.typeParams) {
276
+ (tp, tparam) => tp match {
277
+ case _ : TypeBounds => app.select(tparam)
278
+ case _ => tp
279
+ }
280
+ }
281
+ case _ => tps
282
+ }
283
+
284
+ // Skolemized argument types are used to substitute in F-bounds.
285
+ val skolemizedArgTypes = skolemizeWildcardArgs(argTypes, app)
263
286
val violations = new mutable.ListBuffer [BoundsViolation ]
287
+
264
288
for ((arg, bounds) <- args zip boundss) {
265
289
def checkOverlapsBounds (lo : Type , hi : Type ): Unit = {
266
- // println(i"instantiating ${bounds.hi} with $argTypes")
267
290
// println(i" = ${instantiate(bounds.hi, argTypes)}")
268
- val hiBound = instantiate(bounds.hi, argTypes.mapConserve(_.bounds.hi))
269
- val loBound = instantiate(bounds.lo, argTypes.mapConserve(_.bounds.lo))
270
- // Note that argTypes can contain a TypeBounds type for arguments that are
271
- // not fully determined. In that case we need to check against the hi bound of the argument.
272
- if (! (lo <:< hiBound)) violations += ((arg, " upper" , hiBound))
273
- if (! (loBound <:< hi)) violations += ((arg, " lower" , bounds.lo))
291
+
292
+ var checkCtx = ctx // the context to be used for bounds checking
293
+ if (argTypes ne skolemizedArgTypes) { // some of the arguments are wildcards
294
+
295
+ /** Is there a `LazyRef(TypeRef(_, sym))` reference in `tp`? */
296
+ def isLazyIn (sym : Symbol , tp : Type ): Boolean = {
297
+ def isReference (tp : Type ) = tp match {
298
+ case tp : LazyRef => tp.ref.isInstanceOf [TypeRef ] && tp.ref.typeSymbol == sym
299
+ case _ => false
300
+ }
301
+ tp.existsPart(isReference, forceLazy = false )
302
+ }
303
+
304
+ /** The argument types of the form `TypeRef(_, sym)` which appear as a LazyRef in `bounds`.
305
+ * This indicates that the application is used as an F-bound for the symbol referred to in the LazyRef.
306
+ */
307
+ val lazyRefs = skolemizedArgTypes collect {
308
+ case tp : TypeRef if isLazyIn(tp.symbol, bounds) => tp.symbol
309
+ }
310
+
311
+ for (sym <- lazyRefs) {
312
+
313
+ // If symbol `S` has an F-bound such as `C[_, S]` that contains wildcards,
314
+ // add a modifieed bound where wildcards are skolemized as a GADT bound for `S`.
315
+ // E.g. for `C[_, S]` we would add `C[C[_, S]#T0, S]` where `T0` is the first
316
+ // type parameter of `C`. The new bound is added as a GADT bound for `S` in
317
+ // `checkCtx`.
318
+ // This mirrors what we do for the bounds that are checked and allows us thus
319
+ // to bounds-check F-bounds with wildcards. A test case is pos/i6146.scala.
320
+
321
+ def massage (tp : Type ): Type = tp match {
322
+ case tp @ AppliedType (tycon, args) =>
323
+ tp.derivedAppliedType(tycon, skolemizeWildcardArgs(args, tp))
324
+ case tp : AndOrType =>
325
+ tp.derivedAndOrType(massage(tp.tp1), massage(tp.tp2))
326
+ case _ => tp
327
+ }
328
+ def narrowBound (bound : Type , fromBelow : Boolean ): Unit = {
329
+ val bound1 = massage(bound)
330
+ if (bound1 ne bound) {
331
+ if (checkCtx eq ctx) checkCtx = ctx.fresh.setFreshGADTBounds
332
+ if (! checkCtx.gadt.contains(sym)) checkCtx.gadt.addEmptyBounds(sym)
333
+ checkCtx.gadt.addBound(sym, bound1, fromBelow)
334
+ typr.println(" install GADT bound $bound1 for when checking F-bounded $sym" )
335
+ }
336
+ }
337
+ narrowBound(sym.info.loBound, fromBelow = true )
338
+ narrowBound(sym.info.hiBound, fromBelow = false )
339
+ }
340
+ }
341
+
342
+ val hiBound = instantiate(bounds.hi, skolemizedArgTypes)
343
+ val loBound = instantiate(bounds.lo, skolemizedArgTypes)
344
+
345
+ def check (implicit ctx : Context ) = {
346
+ if (! (lo <:< hiBound)) violations += ((arg, " upper" , hiBound))
347
+ if (! (loBound <:< hi)) violations += ((arg, " lower" , loBound))
348
+ }
349
+ check(checkCtx)
274
350
}
275
351
arg.tpe match {
276
352
case TypeBounds (lo, hi) => checkOverlapsBounds(lo, hi)
0 commit comments