/* 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 import PartialFunction.{ cond => when } /** * @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()), ) ==> Block(Nil, Select(New(TypeTree()), )) * - Select(New(Select(qual, typeName)), ) ==> 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() 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 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(..), )" 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) { param.owner.owner.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, and 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 def matchesName(param: Symbol, name: Name, argIndex: Int) = { def warn(msg: String, since: String) = context0.deprecationWarning(args(argIndex).pos, param, msg, since) def checkDeprecation(anonOK: Boolean) = when (param.deprecatedParamName) { case Some(`name`) => true case Some(nme.NO_NAME) => anonOK } def version = param.deprecatedParamVersion.getOrElse("") def since = if (version.isEmpty) version else s" (since $version)" def checkName = { val res = param.name == name if (res && checkDeprecation(true)) warn(s"naming parameter $name is deprecated$since.", version) res } def checkAltName = { val res = checkDeprecation(false) if (res) warn(s"the parameter name $name is deprecated$since: use ${param.name} instead", version) res } !param.isSynthetic && (checkName || checkAltName) } // argPos maps indices from (order written by user) to (order of definition) val argPos = Array.fill(args.length)(-1) val namelessArgs = { var positionalAllowed = true def stripNamedArg(arg: AssignOrNamedArg, argIndex: Int): Tree = { val AssignOrNamedArg(Ident(name), rhs) = arg params indexWhere (p => matchesName(p, name, argIndex)) match { case -1 if positionalAllowed => // 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 case -1 => UnknownParameterNameNamesDefaultError(arg, name) case paramPos if argPos contains paramPos => val existingArgIndex = argPos.indexWhere(_ == paramPos) val otherName = Some(args(paramPos)) collect { case AssignOrNamedArg(Ident(oName), _) if oName != name => oName } DoubleParamNamesDefaultError(arg, name, existingArgIndex+1, otherName) case paramPos if isAmbiguousAssignment(typer, params(paramPos), arg) => AmbiguousReferenceInNamesDefaultError(arg, name) case paramPos if paramPos != argIndex => positionalAllowed = false // named arg is not in original parameter order: require names after this argPos(argIndex) = paramPos // fix up the arg position rhs case _ => rhs } } mapWithIndex(args) { case (arg: AssignOrNamedArg, argIndex) => val t = stripNamedArg(arg, argIndex) if (!t.isErroneous && argPos(argIndex) < 0) argPos(argIndex) = argIndex t case (arg, argIndex) => if (positionalAllowed) { argPos(argIndex) = argIndex arg } else PositionalAfterNamedNamesDefaultError(arg) } } (namelessArgs, argPos) } }