summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
blob: 39cd610b1c776f288956cef6d75c483025292d42 (plain) (blame)
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
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc
package typechecker

import symtab.Flags._
import scala.collection.mutable
import scala.reflect.ClassTag

/**
 *  @author Lukas Rytz
 *  @version 1.0
 */
trait NamesDefaults { self: Analyzer =>

  import global._
  import definitions._
  import NamesDefaultsErrorsGen._
  import treeInfo.WildcardStarArg

  // Default getters of constructors are added to the companion object in the
  // typeCompleter of the constructor (methodSig). To compute the signature,
  // we need the ClassDef. To create and enter the symbols into the companion
  // object, we need the templateNamer of that module class. These two are stored
  // as an attachment in the companion module symbol
  class ConstructorDefaultsAttachment(val classWithDefault: ClassDef, var companionModuleClassNamer: Namer)

  // To attach the default getters of local (term-owned) methods to the method symbol.
  // Used in Namer.enterExistingSym: it needs to re-enter the method symbol and also
  // default getters, which could not be found otherwise.
  class DefaultsOfLocalMethodAttachment(val defaultGetters: mutable.Set[Symbol]) {
    def this(default: Symbol) = this(mutable.Set(default))
  }

  case class NamedApplyInfo(
    qual:       Option[Tree],
    targs:      List[Tree],
    vargss:     List[List[Tree]],
    blockTyper: Typer
  ) { }

  private def nameOfNamedArg(arg: Tree) = Some(arg) collect { case AssignOrNamedArg(Ident(name), _) => name }
  def isNamedArg(arg: Tree) = arg match {
    case AssignOrNamedArg(Ident(_), _) => true
    case _                             => false
  }

  /** @param pos maps indices from old to new */
  def reorderArgs[T: ClassTag](args: List[T], pos: Int => Int): List[T] = {
    val res = new Array[T](args.length)
    foreachWithIndex(args)((arg, index) => res(pos(index)) = arg)
    res.toList
  }

  /** @param pos maps indices from new to old (!) */
  private def reorderArgsInv[T: ClassTag](args: List[T], pos: Int => Int): List[T] = {
    val argsArray = args.toArray
    (argsArray.indices map (i => argsArray(pos(i)))).toList
  }

  /** returns `true` if every element is equal to its index */
  def allArgsArePositional(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: Mode, 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(unit.freshTermName(nme.QUAL_PREFIX), newFlags = ARTIFACT) setInfo uncheckedBounds(qual.tpe) setPos (qual.pos.makeTransparent)
        blockTyper.context.scope enter sym
        val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType)
        // it stays in Vegas: SI-5720, SI-5727
        qual changeOwner (blockTyper.context.owner -> sym)

        val newQual = atPos(qual.pos.focus)(blockTyper.typedQualifier(Ident(sym.name)))
        val baseFunTransformed = atPos(baseFun.pos.makeTransparent) {
          // setSymbol below is important because the 'selected' function might be overloaded. by
          // assigning the correct method symbol, typedSelect will just assign the type. the reason
          // to still call 'typed' is to correctly infer singleton types, SI-5259.
          val selectPos =
            if(qual.pos.isRange && baseFun1.pos.isRange) qual.pos.union(baseFun1.pos).withStart(Math.min(qual.pos.end, baseFun1.pos.end))
            else baseFun1.pos
          val f = blockTyper.typedOperator(Select(newQual, selected).setSymbol(baseFun1.symbol).setPos(selectPos))
          if (funTargs.isEmpty) f
          else TypeApply(f, funTargs).setType(baseFun.tpe)
        }

        val b = Block(List(vd), baseFunTransformed)
                  .setType(baseFunTransformed.tpe).setPos(baseFun.pos.makeTransparent)
        context.namedApplyBlockInfo =
          Some((b, NamedApplyInfo(Some(newQual), 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 = companionSymbolOf(baseFun.symbol.owner, context)
          if (module == NoSymbol) None
          else {
            val ref = atPos(pos.focus)(gen.mkAttributedRef(pre, module))
            if (treeInfo.admitsTypeSelection(ref))  // fixes #4524. the type checker does the same for
              ref.setType(singleType(pre, module))  // typedSelect, it calls "stabilize" on the result.
            Some(ref)
          }
        }
      }

      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.isExprSafeToInline(qual), qual)
          // 'moduleQual' fixes #2057
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
        case Select(TypeApply(New(tp @ Select(qual, _)), _), _) if isConstr =>
          assert(treeInfo.isExprSafeToInline(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 superclass.
          blockWithoutQualifier(moduleQual(baseFun.pos, sp.symbol.tpe.firstParent))

        // self constructor calls (in secondary constructors)
        case Select(tp, name) if isConstr =>
          assert(treeInfo.isExprSafeToInline(tp), tp)
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))

        // other method calls

        case Ident(_) =>
          blockWithoutQualifier(None)

        case Select(qual, name) =>
          if (treeInfo.isExprSafeToInline(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
     *
     * For Ident(<unapply-selector>) arguments, no ValDef is created (SI-3353).
     */
    def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[Option[ValDef]] = {
      val context = blockTyper.context
      val symPs = map2(args, paramTypes)((arg, paramTpe) => arg match {
        case Ident(nme.SELECTOR_DUMMY) =>
          None // don't create a local ValDef if the argument is <unapply-selector>
        case _ =>
          val byName   = isByNameParamType(paramTpe)
          val repeated = isScalaRepeatedParamType(paramTpe)
          val argTpe = (
            if (repeated) arg match {
              case WildcardStarArg(expr) => expr.tpe
              case _                     => seqType(arg.tpe)
            }
            else {
              // TODO In 83c9c764b, we tried to a stable type here to fix SI-7234. But the resulting TypeTree over a
              //      singleton type without an original TypeTree fails to retypecheck after a resetAttrs (SI-7516),
              //      which is important for (at least) macros.
              arg.tpe
            }
          ).widen // have to widen or types inferred from literal defaults will be singletons
          val s = context.owner.newValue(unit.freshTermName(nme.NAMEDARG_PREFIX), arg.pos, newFlags = ARTIFACT) setInfo {
            val tp = if (byName) functionType(Nil, argTpe) else argTpe
            uncheckedBounds(tp)
          }
          Some((context.scope.enter(s), byName, repeated))
      })
      map2(symPs, args) {
        case (None, _) => None
        case (Some((sym, byName, repeated)), arg) =>
          val body =
            if (byName) {
              val res = blockTyper.typed(Function(List(), arg))
              new ChangeOwnerTraverser(context.owner, res.symbol) traverse arg // fixes #2290
              res
            } else {
              new ChangeOwnerTraverser(context.owner, sym) traverse arg // fixes #4502
              if (repeated) arg match {
                case WildcardStarArg(expr) => expr
                case _                     => blockTyper typed gen.mkSeqApply(resetAttrs(arg))
              } else arg
            }
          Some(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 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)
          typedApp match {
            case Apply(expr, typedArgs) if (typedApp :: typedArgs).exists(_.isErrorTyped) =>
              setError(tree) // bail out with and erroneous Apply *or* erroneous arguments, see SI-7238, SI-7509
            case Apply(expr, typedArgs) =>
              // Extract the typed arguments, restore the call-site evaluation order (using
              // ValDef's in the block), change the arguments to these local values.

              // typedArgs: definition-site order
              val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, removeByName = false, removeRepeated = false)
              // valDefs: call-site order
              val valDefs = argValDefs(reorderArgsInv(typedArgs, argPos),
                                       reorderArgsInv(formals, argPos),
                                       blockTyper)
              // refArgs: definition-site order again
              val refArgs = map3(reorderArgs(valDefs, argPos), formals, typedArgs)((vDefOpt, tpe, origArg) => vDefOpt match {
                case None => origArg
                case Some(vDef) =>
                  val ref = gen.mkAttributedRef(vDef.symbol)
                  atPos(vDef.pos.focus) {
                    // for by-name parameters, the local value is a nullary function returning the argument
                    tpe.typeSymbol match {
                      case ByNameParamClass   => Apply(ref, Nil)
                      case RepeatedParamClass => Typed(ref, Ident(tpnme.WILDCARD_STAR))
                      case _                  => 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.flatten, res).setType(res.tpe).setPos(tree.pos.makeTransparent)
              context.namedApplyBlockInfo =
                Some((block, NamedApplyInfo(qual, targs, vargss :+ refArgs, blockTyper)))
              block
            case _ => tree
          }
        }

      case baseFun => // also treats "case TypeApply(fun, targs)" and "case Select(New(..), <init>)"
        baseFunBlock(baseFun)

    }
  }

  def makeNamedTypes(syms: List[Symbol]) = syms map (sym => NamedType(sym.name, sym.tpe))

  /**
   * Returns the parameter symbols of an invocation expression that are not defined by the list
   * of arguments.
   *
   * @param args    The list of arguments
   * @param params  The list of parameter symbols of the invoked method
   * @param argName A function that extracts the name of an argument expression, if it is a named argument.
   */
  def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name]): (List[Symbol], Boolean) = {
    // The argument list contains first a mix of positional args and named args that are on the
    // right parameter position, and then a number or named args on different positions.

    // collect all named arguments whose position does not match the parameter they define
    val namedArgsOnChangedPosition = args.zip(params) dropWhile {
      case (arg, param) =>
        val n = argName(arg)
        // drop the argument if
        //  - it's not named, or
        //  - it's named, but defines the parameter on its current position, or
        //  - it's named, but none of the parameter names matches (treated as a positional argument, an assignment expression)
        n.isEmpty || n.get == param.name || params.forall(_.name != n.get)
    } map (_._1)

    val paramsWithoutPositionalArg = params.drop(args.length - namedArgsOnChangedPosition.length)

    // missing parameters: those with a name which is not specified in one of the namedArgsOnChangedPosition
    val missingParams = paramsWithoutPositionalArg.filter(p => namedArgsOnChangedPosition.forall { arg =>
      val n = argName(arg)
      n.isEmpty || n.get != p.name
    })
    val allPositional = missingParams.length == paramsWithoutPositionalArg.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: scala.reflect.internal.util.Position, context: Context): (List[Tree], List[Symbol]) = {
    if (givenArgs.length < params.length) {
      val (missing, positional) = missingParams(givenArgs, params, nameOfNamedArg)
      if (missing forall (_.hasDefault)) {
        val defaultArgs = missing flatMap (p => {
          val defGetter = defaultGetter(p, context)
          // TODO #3649 can create spurious errors when companion object is gone (because it becomes unlinked from scope)
          if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649
          else {
            var default1: Tree = 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 (_.hasDefault))
    } 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 = companionSymbolOf(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
  }

  /** A full type check is very expensive; let's make sure there's a name
   *  somewhere which could potentially be ambiguous before we go that route.
   */
  private def isAmbiguousAssignment(typer: Typer, param: Symbol, arg: Tree) = {
    import typer.context
    (context isNameInScope param.name) && {
      // for named arguments, check whether the assignment expression would
      // typecheck. if it does, report an ambiguous error.
      val paramtpe = param.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 = context.undetparams
      context.savingUndeterminedTypeParams(reportAmbiguous = false) {
        val subst = new SubstTypeMap(udp, udp map (_ => WildcardType)) {
          override def apply(tp: Type): Type = super.apply(dropByName(tp))
        }
        // 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...
        val errsBefore = reporter.ERROR.count
        try typer.silent { tpr =>
          val res = tpr.typed(arg.duplicate, subst(paramtpe))
          // better warning for SI-5044: if `silent` was not actually silent give a hint to the user
          // [H]: the reason why `silent` is not silent is because the cyclic reference exception is
          // thrown in a context completely different from `context` here. The exception happens while
          // completing the type, and TypeCompleter is created/run with a non-silent Namer `context`
          // and there is at the moment no way to connect the two unless we go through some global state.
          if (errsBefore < reporter.ERROR.count)
            WarnAfterNonSilentRecursiveInference(param, arg)(context)
          res
        } match {
          case SilentResultValue(t)  =>
            !t.isErroneous // #4041
          case SilentTypeError(e: NormalTypeErrorFromCyclicReference) =>
            // If we end up here, the CyclicReference was reported in a silent context. This can
            // happen for local definitions, when the completer for a definition is created during
            // type checking in silent mode. ContextErrors.TypeSigError catches that cyclic reference
            // and transforms it into a NormalTypeErrorFromCyclicReference.
            // The cycle needs to be reported, because the program cannot be typed: we don't know
            // if we have an assignment or a named arg.
            context.issue(e)
            // 'err = true' is required because we're in a silent context
            WarnAfterNonSilentRecursiveInference(param, arg)(context)
            false
          case _        =>
            // We got a type error, so it cannot be an assignment (it doesn't type check as one).
            false
        }
        catch {
          // `silent` only catches and returns TypeErrors which are not
          // CyclicReferences.  Fix for #3685
          case cr @ CyclicReference(sym, _) =>
            (sym.name == param.name) && sym.accessedOrSelf.isVariable && {
              NameClashError(sym, arg)(context)
              true
            }
        }
      }
    }
  }

  /**
   * Removes name assignments from args. Additionally, returns an array mapping
   * argument indices 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]) = {
    implicit val context0 = typer.context
    // maps indices from (order written by user) to (order of definition)
    val argPos            = Array.fill(args.length)(-1)
    var positionalAllowed = true
    val namelessArgs = mapWithIndex(args) { (arg, argIndex) =>
      arg match {
        case arg @ AssignOrNamedArg(Ident(name), rhs) =>
          def matchesName(param: Symbol) = !param.isSynthetic && (
            (param.name == name) || (param.deprecatedParamName match {
              case Some(`name`) =>
                context0.deprecationWarning(arg.pos, param,
                  s"the parameter name $name has been deprecated. Use ${param.name} instead.")
                true
              case _ => false
            })
          )
          val paramPos = params indexWhere matchesName
          if (paramPos == -1) {
            if (positionalAllowed) {
              argPos(argIndex) = argIndex
              // prevent isNamed from being true when calling doTypedApply recursively,
              // treat the arg as an assignment of type Unit
              Assign(arg.lhs, rhs) setPos arg.pos
            }
            else UnknownParameterNameNamesDefaultError(arg, name)
          }
          else if (argPos contains paramPos) {
            val existingArgIndex = argPos.indexWhere(_ == paramPos)
            val otherName = args(paramPos) match {
              case AssignOrNamedArg(Ident(oName), rhs) if oName != name => Some(oName)
              case _ => None
            }
            DoubleParamNamesDefaultError(arg, name, existingArgIndex+1, otherName)
          } else if (isAmbiguousAssignment(typer, params(paramPos), arg))
            AmbiguousReferenceInNamesDefaultError(arg, name)
          else {
            // if the named argument is on the original parameter
            // position, positional after named is allowed.
            if (argIndex != paramPos)
              positionalAllowed = false
            argPos(argIndex) = paramPos
            rhs
          }
        case _ =>
          argPos(argIndex) = argIndex
          if (positionalAllowed) arg
          else PositionalAfterNamedNamesDefaultError(arg)
      }
    }

    (namelessArgs, argPos)
  }
}