From 0e170e4b695adafb3f3d5823026ccf8d10047be3 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Sat, 20 Jun 2009 08:19:40 +0000 Subject: improvements to names / defaults (implicits, ty... improvements to names / defaults (implicits, type of defaults, #2064, ...) --- .../scala/tools/nsc/transform/LambdaLift.scala | 9 +++ .../scala/tools/nsc/typechecker/Infer.scala | 17 ++---- .../scala/tools/nsc/typechecker/Namers.scala | 26 +++++++- .../tools/nsc/typechecker/NamesDefaults.scala | 62 ++++++++++--------- .../scala/tools/nsc/typechecker/Typers.scala | 71 ++++++++++++++-------- 5 files changed, 115 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 41b2cc53a7..9cf11f2284 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -146,6 +146,15 @@ abstract class LambdaLift extends InfoTransform { if (!(ss contains sym)) { ss addEntry sym renamable addEntry sym + atPhase(currentRun.picklerPhase) { + // The param symbol in the MethodType should not be renamed, only the symbol in scope. This way, + // parameter names for named arguments are not changed. Example: without cloning the MethodType, + // def closure(x: Int) = { () => x } + // would have the signatrue + // closure: (x$1: Int)() => Int + if (sym.hasFlag(PARAM) && sym.owner.info.paramss.exists(_.contains(sym))) + sym.owner.setInfo(sym.owner.info.cloneInfo(sym.owner)) + } changedFreeVars = true if (settings.debug.value) log("" + sym + " is free in " + owner); if (sym.isVariable && !(sym hasFlag CAPTURED)) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index e57e8bcc71..d922d23f94 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -735,18 +735,13 @@ trait Infer { } } else { // not enough arguments, check if applicable using defaults - val namedArgtpes = argtpes0.dropWhile { - case NamedType(name, _) => params.forall(_.name != name) - case _ => true - } - val namedParams = params.drop(argtpes0.length - namedArgtpes.length) - val missingParams = namedParams.filter(p => namedArgtpes.forall { - case NamedType(name, _) => name != p.name - case _ => true - }) - if (missingParams.exists(!_.hasFlag(DEFAULTPARAM))) tryTupleApply + val missing = missingParams[Type](argtpes0, params, { + case NamedType(name, _) => Some(name) + case _ => None + })._1 + if (missing.exists(!_.hasFlag(DEFAULTPARAM))) tryTupleApply else { - val argtpes1 = argtpes0 ::: missingParams.map { + val argtpes1 = argtpes0 ::: missing.map { p => NamedType(p.name, p.tpe) // add defaults as named arguments } isApplicable(undetparams, ftpe, argtpes1, pt) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 9c41249426..61e59f2b2e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -940,9 +940,6 @@ trait Namers { self: Analyzer => UnTyper.traverse(p1) p1 })) - // let the compiler infer the return type of the defaultGetter. needed in "foo[T](a: T = 1)" - val defTpt = TypeTree() - val defRhs = copyUntyped(vparam.rhs) val parentNamer = if (isConstr) { val (cdef, nmr) = moduleNamer.getOrElse { @@ -965,6 +962,29 @@ trait Namers { self: Analyzer => } } + // If the parameter type mentions any type parameter of the method, let the compiler infer the + // return type of the default getter => allow "def foo[T](x: T = 1)" to compile. + // This is better than always inferring the result type, for example in + // def f(i: Int, m: Int => Int = identity _) = m(i) + // if we infer the default's type, we get "Nothing => Nothing", and the default is not usable. + val names = deftParams map { case TypeDef(_, name, _, _) => name } + object subst extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Ident(name) if (names contains name) => + TypeTree() + case _ => + super.transform(tree) + } + def apply(tree: Tree) = { + val r = transform(tree) + if (r.find(_.isEmpty).isEmpty) r + else TypeTree() + } + } + + val defTpt = subst(copyUntyped(vparam.tpt)) + val defRhs = copyUntyped(vparam.rhs) + val defaultTree = atPos(vparam.pos) { DefDef( Modifiers(meth.flags & (PRIVATE | PROTECTED | FINAL)) | SYNTHETIC | DEFAULTPARAM | oflag, diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 0ede740b00..20cf790ca8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -115,23 +115,25 @@ trait NamesDefaults { self: Analyzer => val isConstr = baseFun.symbol.isConstructor val blockTyper = newTyper(context.makeNewScope(tree, context.owner)(BlockScopeKind(context.depth))) - // baseFun1: the extract the function from a potential TypeApply - // defaultTargs: type arguments to be used for calling defaultGetters - // funTargs: type arguments on baseFun, used to reconstruct TypeApply in blockWith(Out)Qualifier - val (baseFun1, defaultTargs, funTargs) = baseFun match { + // 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, targsInSource, targs) + (fun, targs, targsInSource) case Select(New(tpt @ TypeTree()), _) if isConstr => - val targs = tpt.tpe match { - case TypeRef(pre, sym, args) if (args forall (a => context.undetparams contains a)) => + 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, targs, Nil) + (baseFun, Nil, targsInSource) case _ => (baseFun, Nil, Nil) } @@ -286,6 +288,20 @@ trait NamesDefaults { self: Analyzer => } } + 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 @@ -297,35 +313,24 @@ trait NamesDefaults { self: Analyzer => * 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]): (List[Tree], List[Symbol]) = { + previousArgss: List[List[Tree]], params: List[Symbol], pos: util.Position): (List[Tree], List[Symbol]) = { if (givenArgs.length < params.length) { - val namedArgs = givenArgs.dropWhile( arg => { - val n = nameOf(arg) - !(n.isDefined && params.exists(p => p.name == n.get)) - }) - val namedParams = params.drop(givenArgs.length - namedArgs.length) - - def missing(p: Symbol): Boolean = !namedArgs.exists { - case Assign(Ident(name), _) => name == p.name - case _ => false - } - - val missingParams = namedParams filter missing - - if (missingParams forall (_.hasFlag(DEFAULTPARAM))) { - val defaultArgs = missingParams map (p => { + val (missing, positional) = missingParams(givenArgs, params) + if (missing forall (_.hasFlag(DEFAULTPARAM))) { + val defaultArgs = missing map (p => { var default1 = qual match { case Some(q) => gen.mkAttributedSelect(q.duplicate, p.defaultGetter) case None => gen.mkAttributedRef(p.defaultGetter) } default1 = if (targs.isEmpty) default1 - else TypeApply(default1, targs.map(_.duplicate)).setPos(p.pos) + else TypeApply(default1, targs.map(_.duplicate)).setPos(pos) val default2 = (default1 /: previousArgss)((tree, args) => - Apply(tree, args.map(_.duplicate)).setPos(p.pos)) - Assign(Ident(p.name), default2) + Apply(tree, args.map(_.duplicate)).setPos(pos)) + if (positional) default2 + else Assign(Ident(p.name), default2) }) (givenArgs ::: defaultArgs, Nil) - } else (givenArgs, missingParams filter (! _.hasFlag(DEFAULTPARAM))) + } else (givenArgs, missing filter (! _.hasFlag(DEFAULTPARAM))) } else (givenArgs, Nil) } @@ -342,7 +347,6 @@ trait NamesDefaults { self: Analyzer => // maps indicies from (order written by user) to (order of definition) val argPos = (new Array[Int](args.length)) map (x => -1) var positionalAllowed = true - // @LUC TODO: make faster (don't use zipWithIndex) val namelessArgs = for ((arg, index) <- (args.zipWithIndex)) yield arg match { case Assign(Ident(name), rhs) => val pos = params.indexWhere(p => p.name == name && !p.hasFlag(SYNTHETIC)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e324e5793f..71e0af421f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -174,23 +174,32 @@ trait Typers { self: Analyzer => /** Find implicit arguments and pass them to given tree. */ - def applyImplicitArgs(tree: Tree): Tree = tree.tpe match { + def applyImplicitArgs(fun: Tree): Tree = fun.tpe match { case MethodType(params, _) => - def implicitArg(pt: Type): SearchResult = { - val result = inferImplicit(tree, pt, true, context) - if (result == SearchFailure) - context.error(tree.pos, "no implicit argument matching parameter type "+pt+" was found.") - result - } + def implicitArg(pt: Type): SearchResult = + inferImplicit(fun, pt, true, context) + + var positional = true val argResults = params map (_.tpe) map implicitArg - val args = argResults map (_.tree) + val args = argResults.zip(params) flatMap { + case (arg, param) => + if (arg != SearchFailure) { + if (positional) List(arg.tree) + else List(atPos(arg.tree.pos)(Assign(Ident(param.name), (arg.tree)))) + } else { + if (!param.hasFlag(DEFAULTPARAM)) + context.error(fun.pos, "could not find implicit value for parameter "+ param.name +":"+ param.tpe +".") + positional = false + Nil + } + } for (s <- argResults map (_.subst)) { - s traverse tree + s traverse fun for (arg <- args) s traverse arg } - Apply(tree, args) setPos tree.pos + Apply(fun, args) setPos fun.pos case ErrorType => - tree + fun } /** Infer an implicit conversion (``view'') between two types. @@ -795,7 +804,7 @@ trait Typers { self: Analyzer => val tree1 = etaExpand(context.unit, tree) //println("eta "+tree+" ---> "+tree1+":"+tree1.tpe) typed(tree1, mode, pt) - } else if (!meth.isConstructor && mt.paramTypes.isEmpty) { // (4.3) + } else if (!meth.isConstructor && mt.params.isEmpty) { // (4.3) adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt) } else if (context.implicitsEnabled) { errorTree(tree, "missing arguments for "+meth+meth.locationString+ @@ -1990,6 +1999,9 @@ trait Typers { self: Analyzer => */ def tryNamesDefaults: Tree = { if (mt.isErroneous) setError(tree) + else if ((mode & PATTERNmode) != 0) + // #2064 + errorTree(tree, "wrong number of arguments for "+ treeSymTypeMsg(fun)) else if (args.length > formals.length) { tryTupleApply.getOrElse { errorTree(tree, "too many arguments for "+treeSymTypeMsg(fun)) @@ -2020,22 +2032,27 @@ trait Typers { self: Analyzer => if (fun1.isErroneous) setError(tree) else { assert(isNamedApplyBlock(fun1), fun1) - val NamedApplyInfo(qual, targs, previousArgss, _) = - context.namedApplyBlockInfo.get._2 - val (allArgs, missing) = addDefaults(args, qual, targs, previousArgss, mt.params) + val NamedApplyInfo(qual, targs, previousArgss, _) = context.namedApplyBlockInfo.get._2 + val blockIsEmpty = fun1 match { + case Block(Nil, _) => + // if the block does not have any ValDef we can remove it. Note that the call to + // "transformNamedApplication" is always needed in order to obtain targs/previousArgss + context.namedApplyBlockInfo = None + true + case _ => false + } + val (allArgs, missing) = addDefaults(args, qual, targs, previousArgss, params, fun.pos) if (allArgs.length == formals.length) { - // a default for each missing argument was found - val (namelessArgs, argPos) = removeNames(Typer.this)(allArgs, params) - if (namelessArgs exists (_.isErroneous)) setError(tree) - else - transformNamedApplication(Typer.this, mode, pt)( - treeCopy.Apply(tree, fun1, namelessArgs), argPos) + doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { tryTupleApply.getOrElse { val suffix = if (missing.isEmpty) "" - else ", unspecified parameter"+ (if (missing.length > 1) "s: " else ": ") + - (missing.take(3).mkString(", ")) + (if (missing.length > 3) ", ..." else "") + else { + val missingStr = missing.take(3).map(_.name).mkString(", ") + (if (missing.length > 3) ", ..." else ".") + val sOpt = if (missing.length > 1) "s" else "" + ".\nUnspecified value parameter"+ sOpt +" "+ missingStr + } errorTree(tree, "not enough arguments for "+treeSymTypeMsg(fun) + suffix) } } @@ -2043,10 +2060,10 @@ trait Typers { self: Analyzer => } } - if (formals.length != args.length || // wrong nb of arguments - args.exists(isNamed(_)) || // uses a named argument - isNamedApplyBlock(fun)) { // fun was transformed to a named apply block => - // integrate this application into the block + if (formals.length != args.length || // wrong nb of arguments + args.exists(isNamed(_)) || // uses a named argument + isNamedApplyBlock(fun)) { // fun was transformed to a named apply block => + // integrate this application into the block tryNamesDefaults } else { val tparams = context.extractUndetparams() -- cgit v1.2.3