1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
|
/* NSC -- new Scala compiler
* Copyright 2005-2011 LAMP/EPFL
* @author Martin Odersky
*/
package scala.tools.nsc
package typechecker
import symtab.Flags._
import scala.collection.mutable.{ListBuffer, WeakHashMap}
import scala.collection.immutable.Set
/**
* @author Lukas Rytz
* @version 1.0
*/
trait NamesDefaults { self: Analyzer =>
import global._
import definitions._
val defaultParametersOfMethod = new WeakHashMap[Symbol, Set[Symbol]] {
override def default(key: Symbol) = Set()
}
case class NamedApplyInfo(qual: Option[Tree], targs: List[Tree],
vargss: List[List[Tree]], blockTyper: Typer)
val noApplyInfo = NamedApplyInfo(None, Nil, Nil, null)
def nameOf(arg: Tree) = arg match {
case AssignOrNamedArg(Ident(name), rhs) => Some(name)
case _ => None
}
def isNamed(arg: Tree) = nameOf(arg).isDefined
/** @param pos maps indicies from old to new */
def reorderArgs[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = {
val res = new Array[T](args.length)
// (hopefully) faster than zipWithIndex
(0 /: args) { case (index, arg) => res(pos(index)) = arg; index + 1 }
res.toList
}
/** @param pos maps indicies from new to old (!) */
def reorderArgsInv[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = {
val argsArray = args.toArray
val res = new ListBuffer[T]
for (i <- 0 until argsArray.length)
res += argsArray(pos(i))
res.toList
}
/** returns `true' if every element is equal to its index */
def isIdentity(a: Array[Int]) = (0 until a.length).forall(i => a(i) == i)
/**
* Transform a function application into a Block, and assigns typer.context
* .namedApplyBlockInfo to the new block as side-effect. If tree has the form
* Apply(fun, args)
* first the function "fun" (which might be an application itself!) is transformed into a
* block of the form
* {
* val qual$1 = qualifier_of_fun
* val x$1 = arg_1_of_fun
* ...
* val x$n = arg_n_of_fun
* qual$1.fun[targs](x$1, ...)...(..., x$n)
* }
* then for each argument in args, a value is created and entered into the block. finally
* the application expression of the block is updated.
* {
* val qual$1 = ..
* ...
* val x$n = ...
* > val qual$n+1 = arg(1)
* > ...
* > val qual$n+m = arg(m)
* > qual$1.fun[targs](x$1, ...)...(..., x$n)(x$n+1, ..., x$n+m)
* }
*
* @param typer the typer calling this method; this method calls
* typer.doTypedApply
* @param mode the mode to use for calling typer.doTypedApply
* @param pt the expected type for calling typer.doTypedApply
*
* @param tree: the function application tree
* @argPos: a function mapping arguments from their current position to the
* position specified by the method type. example:
* def foo(a: Int, b: String)
* foo(b = "1", a = 2)
* calls
* transformNamedApplication(Apply(foo, List("1", 2), { 0 => 1, 1 => 0 })
*
* @return the transformed application (a Block) together with the NamedApplyInfo.
* if isNamedApplyBlock(tree), returns the existing context.namedApplyBlockInfo
*/
def transformNamedApplication(typer: Typer, mode: Int, pt: Type)
(tree: Tree, argPos: Int => Int): Tree = {
import typer._
import typer.infer._
val context = typer.context
import context.unit
/**
* Transform a function into a block, and passing context.namedApplyBlockInfo to
* the new block as side-effect.
*
* `baseFun' is typed, the resulting block must be typed as well.
*
* Fun is transformed in the following way:
* - Ident(f) ==> Block(Nil, Ident(f))
* - Select(qual, f) if (qual is stable) ==> Block(Nil, Select(qual, f))
* - Select(qual, f) otherwise ==> Block(ValDef(qual$1, qual), Select(qual$1, f))
* - TypeApply(fun, targs) ==> Block(Nil or qual$1, TypeApply(fun, targs))
* - Select(New(TypeTree()), <init>) ==> Block(Nil, Select(New(TypeTree()), <init>))
* - Select(New(Select(qual, typeName)), <init>) ==> Block(Nil, Select(...)) NOTE: qual must be stable in a `new'
*/
def baseFunBlock(baseFun: Tree): Tree = {
val isConstr = baseFun.symbol.isConstructor
val blockTyper = newTyper(context.makeNewScope(tree, context.owner))
// baseFun1: extract the function from a potential TypeApply
// funTargs: type arguments on baseFun, used to reconstruct TypeApply in blockWith(Out)Qualifier
// defaultTargs: type arguments to be used for calling defaultGetters. If the type arguments are given
// in the source code, re-use them for default getter. Otherwise infer the default getter's t-args.
val (baseFun1, funTargs, defaultTargs) = baseFun match {
case TypeApply(fun, targs) =>
val targsInSource =
if (targs.forall(a => context.undetparams contains a.symbol)) Nil
else targs
(fun, targs, targsInSource)
case Select(New(tpt @ TypeTree()), _) if isConstr =>
val targsInSource = tpt.tpe match {
case TypeRef(pre, sym, args)
if (!args.forall(a => context.undetparams contains a.typeSymbol)) =>
args.map(TypeTree(_))
case _ =>
Nil
}
(baseFun, Nil, targsInSource)
case Select(TypeApply(New(TypeTree()), targs), _) if isConstr =>
val targsInSource =
if (targs.forall(a => context.undetparams contains a.symbol)) Nil
else targs
(baseFun, Nil, targsInSource)
case _ => (baseFun, Nil, Nil)
}
// never used for constructor calls, they always have a stable qualifier
def blockWithQualifier(qual: Tree, selected: Name) = {
val sym = blockTyper.context.owner.newValue(qual.pos, unit.freshTermName("qual$"))
.setInfo(qual.tpe)
blockTyper.context.scope.enter(sym)
val vd = atPos(sym.pos)(ValDef(sym, qual).setType(NoType))
var baseFunTransformed = atPos(baseFun.pos.makeTransparent) {
// don't use treeCopy: it would assign opaque position.
val f = Select(gen.mkAttributedRef(sym), selected)
.setType(baseFun1.tpe).setSymbol(baseFun1.symbol)
if (funTargs.isEmpty) f
else TypeApply(f, funTargs).setType(baseFun.tpe)
}
val b = Block(List(vd), baseFunTransformed)
.setType(baseFunTransformed.tpe).setPos(baseFun.pos)
val defaultQual = Some(atPos(qual.pos.focus)(gen.mkAttributedRef(sym)))
context.namedApplyBlockInfo =
Some((b, NamedApplyInfo(defaultQual, defaultTargs, Nil, blockTyper)))
b
}
def blockWithoutQualifier(defaultQual: Option[Tree]) = {
val b = atPos(baseFun.pos)(Block(Nil, baseFun).setType(baseFun.tpe))
context.namedApplyBlockInfo =
Some((b, NamedApplyInfo(defaultQual, defaultTargs, Nil, blockTyper)))
b
}
def moduleQual(pos: Position, classType: Type) = {
// prefix does 'normalize', which fixes #3384
val pre = classType.prefix
if (pre == NoType) {
None
} else {
val module = companionModuleOf(baseFun.symbol.owner, context)
if (module == NoSymbol) None
else Some(atPos(pos.focus)(gen.mkAttributedRef(pre, module)))
}
}
baseFun1 match {
// constructor calls
case Select(New(tp @ TypeTree()), _) if isConstr =>
// 'moduleQual' fixes #3338. Same qualifier for selecting the companion object as for the class.
blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
case Select(TypeApply(New(tp @ TypeTree()), _), _) if isConstr =>
blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
case Select(New(tp @ Ident(_)), _) if isConstr =>
// 'moduleQual' fixes #3344
blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
case Select(TypeApply(New(tp @ Ident(_)), _), _) if isConstr =>
blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
case Select(New(tp @ Select(qual, _)), _) if isConstr =>
// in `new q.C()', q is always stable
assert(treeInfo.isPureExpr(qual), qual)
// 'moduleQual' fixes #2057
blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
case Select(TypeApply(New(tp @ Select(qual, _)), _), _) if isConstr =>
assert(treeInfo.isPureExpr(qual), qual)
blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
// super constructor calls
case Select(sp @ Super(_, _), _) if isConstr =>
// 'moduleQual' fixes #3207. selection of the companion module of the
// superclass needs to have the same prefix as the the superclass.
blockWithoutQualifier(moduleQual(baseFun.pos, sp.symbol.tpe.parents.head))
// self constructor calls (in secondary constructors)
case Select(tp, name) if isConstr =>
assert(treeInfo.isPureExpr(tp), tp)
blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
// other method calls
case Ident(_) =>
blockWithoutQualifier(None)
case Select(qual, name) =>
if (treeInfo.isPureExpr(qual))
blockWithoutQualifier(Some(qual.duplicate))
else
blockWithQualifier(qual, name)
}
}
/**
* For each argument (arg: T), create a local value
* x$n: T = arg
*
* assumes "args" are typed. owner of the definitions in the block is the owner of
* the block (see typedBlock), but the symbols have to be entered into the block's scope.
*
* For by-name parameters, create a value
* x$n: () => T = () => arg
*/
def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[ValDef] = {
val context = blockTyper.context
val symPs = (args, paramTypes).zipped map ((arg, tpe) => {
val byName = isByNameParamType(tpe)
val (argTpe, repeated) =
if (isScalaRepeatedParamType(tpe)) arg match {
case Typed(expr, Ident(tpnme.WILDCARD_STAR)) =>
(expr.tpe, true)
case _ =>
(seqType(arg.tpe), true)
} else (arg.tpe, false)
val s = context.owner.newValue(arg.pos, unit.freshTermName("x$"))
val valType = if (byName) functionType(List(), argTpe)
else if (repeated) argTpe
else argTpe
s.setInfo(valType)
(context.scope.enter(s), byName, repeated)
})
(symPs, args).zipped map {
case ((sym, byName, repeated), arg) =>
// resetAttrs required for #2290. given a block { val x = 1; x }, when wrapping into a function
// () => { val x = 1; x }, the owner of symbol x must change (to the apply method of the function).
val body = if (byName) blockTyper.typed(Function(List(), resetLocalAttrs(arg)))
else if (repeated) arg match {
case Typed(expr, Ident(tpnme.WILDCARD_STAR)) =>
expr
case _ =>
val factory = Select(gen.mkAttributedRef(SeqModule), nme.apply)
blockTyper.typed(Apply(factory, List(resetLocalAttrs(arg))))
} else arg
atPos(body.pos)(ValDef(sym, body).setType(NoType))
}
}
// begin transform
if (isNamedApplyBlock(tree)) {
context.namedApplyBlockInfo.get._1
} else tree match {
// `fun' is typed. `namelessArgs' might be typed or not, if they are types are kept.
case Apply(fun, namelessArgs) =>
val transformedFun = transformNamedApplication(typer, mode, pt)(fun, x => x)
if (transformedFun.isErroneous) setError(tree)
else {
assert(isNamedApplyBlock(transformedFun), transformedFun)
val NamedApplyInfo(qual, targs, vargss, blockTyper) =
context.namedApplyBlockInfo.get._2
val existingBlock @ Block(stats, funOnly) = transformedFun
// type the application without names; put the arguments in definition-site order
val typedApp = doTypedApply(tree, funOnly, reorderArgs(namelessArgs, argPos), mode, pt)
if (typedApp.tpe.isError) setError(tree)
else typedApp match {
// Extract the typed arguments, restore the call-site evaluation order (using
// ValDef's in the block), change the arguments to these local values.
case Apply(expr, typedArgs) =>
// typedArgs: definition-site order
val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, false, false)
// valDefs: call-site order
val valDefs = argValDefs(reorderArgsInv(typedArgs, argPos),
reorderArgsInv(formals, argPos),
blockTyper)
// refArgs: definition-site order again
val refArgs = (reorderArgs(valDefs, argPos), formals).zipped map ((vDef, tpe) => {
val ref = gen.mkAttributedRef(vDef.symbol)
atPos(vDef.pos.focus) {
// for by-name parameters, the local value is a nullary function returning the argument
if (isByNameParamType(tpe)) Apply(ref, List())
else if (isScalaRepeatedParamType(tpe)) Typed(ref, Ident(tpnme.WILDCARD_STAR))
else ref
}
})
// cannot call blockTyper.typedBlock here, because the method expr might be partially applied only
val res = blockTyper.doTypedApply(tree, expr, refArgs, mode, pt)
res.setPos(res.pos.makeTransparent)
val block = Block(stats ::: valDefs, res).setType(res.tpe).setPos(tree.pos)
context.namedApplyBlockInfo =
Some((block, NamedApplyInfo(qual, targs, vargss ::: List(refArgs), blockTyper)))
block
}
}
case baseFun => // also treats "case TypeApply(fun, targs)" and "case Select(New(..), <init>)"
baseFunBlock(baseFun)
}
}
def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOf _): (List[Symbol], Boolean) = {
val namedArgs = args.dropWhile(arg => {
val n = argName(arg)
n.isEmpty || params.forall(p => p.name != n.get)
})
val namedParams = params.drop(args.length - namedArgs.length)
// missing: keep those with a name which doesn't exist in namedArgs
val missingParams = namedParams.filter(p => namedArgs.forall(arg => {
val n = argName(arg)
n.isEmpty || n.get != p.name
}))
val allPositional = missingParams.length == namedParams.length
(missingParams, allPositional)
}
/**
* Extend the argument list `givenArgs' with default arguments. Defaults are added
* as named arguments calling the corresponding default getter.
*
* Example: given
* def foo(x: Int = 2, y: String = "def")
* foo(y = "lt")
* the argument list (y = "lt") is transformed to (y = "lt", x = foo$default$1())
*/
def addDefaults(givenArgs: List[Tree], qual: Option[Tree], targs: List[Tree],
previousArgss: List[List[Tree]], params: List[Symbol],
pos: util.Position, context: Context): (List[Tree], List[Symbol]) = {
if (givenArgs.length < params.length) {
val (missing, positional) = missingParams(givenArgs, params)
if (missing forall (_.hasDefaultFlag)) {
val defaultArgs = missing flatMap (p => {
val defGetter = defaultGetter(p, context)
if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649
else {
var default1 = qual match {
case Some(q) => gen.mkAttributedSelect(q.duplicate, defGetter)
case None => gen.mkAttributedRef(defGetter)
}
default1 = if (targs.isEmpty) default1
else TypeApply(default1, targs.map(_.duplicate))
val default2 = (default1 /: previousArgss)((tree, args) =>
Apply(tree, args.map(_.duplicate)))
Some(atPos(pos) {
if (positional) default2
else AssignOrNamedArg(Ident(p.name), default2)
})
}
})
(givenArgs ::: defaultArgs, Nil)
} else (givenArgs, missing filterNot (_.hasDefaultFlag))
} else (givenArgs, Nil)
}
/**
* For a parameter with default argument, find the method symbol of
* the default getter.
*/
def defaultGetter(param: Symbol, context: Context): Symbol = {
val i = param.owner.paramss.flatten.indexWhere(p => p.name == param.name) + 1
if (i > 0) {
val defGetterName = nme.defaultGetterName(param.owner.name, i)
if (param.owner.isConstructor) {
val mod = companionModuleOf(param.owner.owner, context)
mod.info.member(defGetterName)
}
else {
// isClass also works for methods in objects, owner is the ModuleClassSymbol
if (param.owner.owner.isClass) {
// .toInterface: otherwise we get the method symbol of the impl class
param.owner.owner.toInterface.info.member(defGetterName)
} else {
// the owner of the method is another method. find the default
// getter in the context.
context.lookup(defGetterName, param.owner.owner)
}
}
} else NoSymbol
}
/**
* Removes name assignments from args. Additionally, returns an array mapping
* argument indicies from call-site-order to definition-site-order.
*
* Verifies that names are not specified twice, positional args don't appear
* after named ones.
*/
def removeNames(typer: Typer)(args: List[Tree], params: List[Symbol]): (List[Tree], Array[Int]) = {
import typer.infer.errorTree
// maps indicies from (order written by user) to (order of definition)
val argPos = (new Array[Int](args.length)) map (x => -1)
var positionalAllowed = true
val namelessArgs = for ((arg, index) <- (args.zipWithIndex)) yield arg match {
case a @ AssignOrNamedArg(Ident(name), rhs) =>
val (pos, newName) = paramPos(params, name)
newName.foreach(n => {
typer.context.unit.deprecationWarning(arg.pos, "the parameter name "+ name +" has been deprecated. Use "+ n +" instead.")
})
if (pos == -1) {
if (positionalAllowed) {
argPos(index) = index
// prevent isNamed from being true when calling doTypedApply recursively,
// treat the arg as an assignment of type Unit
Assign(a.lhs, rhs).setPos(arg.pos)
} else {
errorTree(arg, "unknown parameter name: "+ name)
}
} else if (argPos contains pos) {
errorTree(arg, "parameter specified twice: "+ name)
} else {
// for named arguments, check whether the assignment expression would
// typecheck. if it does, report an ambiguous error.
val param = params(pos)
val paramtpe = params(pos).tpe.cloneInfo(param)
// replace type parameters by wildcard. in the below example we need to
// typecheck (x = 1) with wildcard (not T) so that it succeeds.
// def f[T](x: T) = x
// var x = 0
// f(x = 1) << "x = 1" typechecks with expected type WildcardType
val udp = typer.context.extractUndetparams()
val subst = new SubstTypeMap(udp, udp map (_ => WildcardType)) {
override def apply(tp: Type): Type = tp match {
case TypeRef(_, ByNameParamClass, List(arg)) => super.apply(arg)
case _ => super.apply(tp)
}
}
val reportAmbiguousErrors = typer.context.reportAmbiguousErrors
typer.context.reportAmbiguousErrors = false
val typedAssign = try {
typer.silent(_.typed(arg, subst(paramtpe)))
} catch {
// `silent` only catches and returns TypeErrors which are not
// CyclicReferences. Fix for #3685
case cr @ CyclicReference(sym, info) if sym.name == param.name =>
// If this condition included sym.isMethod, #4041 issues a sensible
// error instead of crashing; but some programs which now compile would
// require a return type annotation.
if (sym.isVariable || sym.isGetter && sym.accessed.isVariable) {
// named arg not allowed
typer.context.error(sym.pos,
"%s definition needs %s because '%s' is used as a named argument in its body.".format(
"variable", // "method"
"type", // "result type"
sym.name
)
)
typer.infer.setError(arg)
}
else cr
}
val res = typedAssign match {
case _: TypeError =>
// if the named argument is on the original parameter
// position, positional after named is allowed.
if (index != pos)
positionalAllowed = false
argPos(index) = pos
rhs
case t: Tree =>
if (!t.isErroneous) {
// This throws an exception which is caught in `tryTypedApply` (as it
// uses `silent`) - unfortunately, tryTypedApply recovers from the
// exception if you use errorTree(arg, ...) and conforms is allowed as
// a view (see tryImplicit in Implicits) because it tries to produce a
// new qualifier (if the old one was P, the new one will be
// conforms.apply(P)), and if that works, it pretends nothing happened.
//
// To make sure tryTypedApply fails, we would like to pass EmptyTree
// instead of arg, but can't do that because eventually setType(ErrorType)
// is called, and EmptyTree can only be typed NoType. Thus we need to
// disable conforms as a view...
errorTree(arg, "reference to "+ name +" is ambiguous; it is both, a parameter\n"+
"name of the method and the name of a variable currently in scope.")
} else t // error was reported above
}
typer.context.reportAmbiguousErrors = reportAmbiguousErrors
//@M note that we don't get here when an ambiguity was detected (during the computation of res),
// as errorTree throws an exception
typer.context.undetparams = udp
res
}
case _ =>
argPos(index) = index
if (positionalAllowed) arg
else errorTree(arg, "positional after named argument.")
}
(namelessArgs, argPos)
}
/**
* Returns
* - the position of the parameter named `name`
* - optionally, if `name` is @deprecatedName, the new name
*/
def paramPos(params: List[Symbol], name: Name): (Int, Option[Name]) = {
var i = 0
var rest = params
while (!rest.isEmpty) {
val p = rest.head
if (!p.isSynthetic) {
if (p.name == name) return (i, None)
if (deprecatedName(p) == Some(name)) return (i, Some(p.name))
}
i += 1
rest = rest.tail
}
(-1, None)
}
def deprecatedName(sym: Symbol): Option[Name] =
sym.getAnnotation(DeprecatedNameAttr).map(ann => (ann.args(0): @unchecked) match {
case Apply(fun, Literal(str) :: Nil) if (fun.symbol == Symbol_apply) =>
newTermName(str.stringValue)
})
}
|