aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-12-05 16:51:31 +0100
committerMartin Odersky <odersky@gmail.com>2013-12-05 16:51:31 +0100
commitcd28a05fa16b5b2cf3569f0ab0a8c9c685e41bf1 (patch)
treee129ea30724491d72d9b27cc6a3d546fa83afd6d /src/dotty/tools
parentc2f101a33ea7f89681d6b74731bbcff948e7e6da (diff)
downloaddotty-cd28a05fa16b5b2cf3569f0ab0a8c9c685e41bf1.tar.gz
dotty-cd28a05fa16b5b2cf3569f0ab0a8c9c685e41bf1.tar.bz2
dotty-cd28a05fa16b5b2cf3569f0ab0a8c9c685e41bf1.zip
Fixes to named and default arguments.
Now come with test cases.
Diffstat (limited to 'src/dotty/tools')
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala22
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala7
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala77
-rw-r--r--src/dotty/tools/dotc/typer/EtaExpansion.scala13
4 files changed, 64 insertions, 55 deletions
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 84e321158..cd3de4fd4 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -23,7 +23,7 @@ object desugar {
/** var x: Int = expr
* ==>
* def x: Int = expr
- * def x_=($1: like (var x: Int = expr)): Unit = ()
+ * def x_=($1: <TypeTree()>): Unit = ()
*/
def valDef(vdef: ValDef)(implicit ctx: Context): Tree = {
val ValDef(mods, name, tpt, rhs) = vdef
@@ -33,7 +33,7 @@ object desugar {
// todo: copy of vdef as getter needed?
// val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ?
// right now vdef maps via expandedTree to a thicket which concerns itself.
- // I don't see a problem with that but if there is one we can avoid it by making a copy here.
+ // I don't see a problem with that but if there is one we can avoid it by making a copy here.
val setterParam = makeSyntheticParameter(tpt = TypeTree())
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral
val setter = cpy.DefDef(vdef,
@@ -87,13 +87,11 @@ object desugar {
cpy.DefDef(meth, mods, name, tparams1, vparamss1, tpt, rhs)
}
- /** The first n parameters in a possibly curried list of parameter sections */
- def take(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
+ /** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
+ def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
case vparams :: vparamss1 =>
val len = vparams.length
- if (n == 0) Nil
- else if (n < len) (vparams take n) :: Nil
- else vparams :: take(vparamss1, n - len)
+ if (n >= len) vparams :: takeUpTo(vparamss1, n - len) else Nil
case _ =>
Nil
}
@@ -101,25 +99,25 @@ object desugar {
def normalizedVparamss = vparamss map (_ map (vparam =>
cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, EmptyTree)))
- def defaultGetters(vparamss: List[List[ValDef]], n: Int = 0): List[DefDef] = vparamss match {
+ def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match {
case (vparam :: vparams) :: vparamss1 =>
def defaultGetter: DefDef =
DefDef(
mods = vparam.mods & AccessFlags,
- name = meth.name.defaultGetterName(n + 1),
+ name = meth.name.defaultGetterName(n),
tparams = meth.tparams,
- vparamss = take(normalizedVparamss, n),
+ vparamss = takeUpTo(normalizedVparamss, n),
tpt = TypeTree(),
rhs = vparam.rhs)
val rest = defaultGetters(vparams :: vparamss1, n + 1)
if (vparam.rhs.isEmpty) rest else defaultGetter :: rest
case Nil :: vparamss1 =>
- defaultGetters(vparamss1)
+ defaultGetters(vparamss1, n)
case nil =>
Nil
}
- val defGetters = defaultGetters(vparamss)
+ val defGetters = defaultGetters(vparamss, 0)
if (defGetters.isEmpty) meth1
else {
val mods1 = meth1.mods | DefaultParameterized
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index c9665577d..41384e346 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -203,10 +203,13 @@ object NameOps {
}
}
- /** Nominally, name$default$N, encoded for <init> */
+ /** Nominally, name$default$N, encoded for <init>
+ * @param Post the parameters position.
+ * @note Default getter name suffixes start at 1, so `pos` has to be adjusted by +1
+ */
def defaultGetterName(pos: Int): TermName = {
val prefix = if (name.isConstructorName) DEFAULT_GETTER_INIT else name
- prefix ++ DEFAULT_GETTER ++ pos.toString
+ prefix ++ DEFAULT_GETTER ++ (pos + 1).toString
}
/** Nominally, name from name$default$N, CONSTRUCTOR for <init> */
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index ed3759b20..3de835736 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -137,49 +137,48 @@ trait Applications extends Compatibility { self: Typer =>
/** Re-order arguments to correctly align named arguments */
def reorder[T >: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = {
- var namedToArg: Map[Name, Trees.Tree[T]] =
- (for (NamedArg(name, arg1) <- args) yield (name, arg1)).toMap
-
- def badNamedArg(arg: untpd.Tree): Unit = {
- val NamedArg(name, _) = arg
- def msg =
- if (methodType.paramNames contains name)
- s"parameter $name of $methString is already instantiated"
- else
- s"$methString does not have a parameter $name"
- fail(msg, arg.asInstanceOf[Arg])
- }
- def recur(pnames: List[Name], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = pnames match {
- case pname :: pnames1 =>
- namedToArg get pname match {
- case Some(arg) => // there is a named argument for this parameter; pick it
- namedToArg -= pname
- arg :: recur(pnames1, args)
- case None =>
- args match {
- case (arg @ NamedArg(aname, _)) :: args1 =>
- if (namedToArg contains aname) // argument is missing, pass an empty tree
- genericEmptyTree :: recur(pnames1, args)
- else { // name not (or no longer) available for named arg
- badNamedArg(arg)
- recur(pnames1, args1)
- }
- case arg :: args1 =>
- arg :: recur(pnames1, args1) // unnamed argument; pick it
- case Nil => // no more args, continue to pick up any preceding named args
- recur(pnames1, args)
+ /** @param pnames The list of parameter names that are missing arguments
+ * @param args The list of arguments that are not yet passed, or that are waiting to be dropped
+ * @param nameToArg A map from as yet unseen names to named arguments
+ * @param todrop A set of names that have aready be passed as named arguments
+ *
+ * For a well-typed application we have the invariants
+ *
+ * 1. `(args diff toDrop)` can be reordered to match `pnames`
+ * 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args`
+ */
+ def recur(pnames: List[Name], args: List[Trees.Tree[T]],
+ nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name]): List[Trees.Tree[T]] = pnames match {
+ case pname :: pnames1 if nameToArg contains pname =>
+ // there is a named argument for this parameter; pick it
+ nameToArg(pname) :: recur(pnames1, args, nameToArg - pname, toDrop + pname)
+ case _ =>
+ def pnamesRest = if (pnames.isEmpty) pnames else pnames.tail
+ args match {
+ case (arg @ NamedArg(aname, _)) :: args1 =>
+ if (toDrop contains aname) // argument is already passed
+ recur(pnames, args1, nameToArg, toDrop - aname)
+ else if (nameToArg contains aname) // argument is missing, pass an empty tree
+ genericEmptyTree :: recur(pnamesRest, args, nameToArg, toDrop)
+ else { // name not (or no longer) available for named arg
+ def msg =
+ if (methodType.paramNames contains aname)
+ s"parameter $aname of $methString is already instantiated"
+ else
+ s"$methString does not have a parameter $aname"
+ fail(msg, arg.asInstanceOf[Arg])
+ arg :: recur(pnamesRest, args1, nameToArg, toDrop)
}
+ case arg :: args1 =>
+ arg :: recur(pnamesRest, args1, nameToArg, toDrop) // unnamed argument; pick it
+ case Nil => // no more args, continue to pick up any preceding named args
+ if (pnames.isEmpty) Nil
+ else recur(pnamesRest, args, nameToArg, toDrop)
}
- case nil => // supernumerary arguments, can only be default args.
- if (hasNamedArg(args)) {
- val (namedArgs, otherArgs) = args partition isNamedArg
- namedArgs foreach badNamedArg
- otherArgs
- } else args
}
-
- recur(methodType.paramNames, args)
+ val nameAssocs = for (arg @ NamedArg(name, _) <- args) yield (name, arg)
+ recur(methodType.paramNames, args, nameAssocs.toMap, Set())
}
/** Splice new method reference into existing application */
diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala
index 2eb7486eb..8781eea4b 100644
--- a/src/dotty/tools/dotc/typer/EtaExpansion.scala
+++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala
@@ -42,6 +42,15 @@ object EtaExpansion {
tree
}
+ /** Lift a function argument, stripping any NamedArg wrapper */
+ def liftArg(defs: mutable.ListBuffer[Tree], arg: Tree, prefix: String = "")(implicit ctx: Context): Tree = {
+ val arg1 = arg match {
+ case NamedArg(_, arg1) => arg1
+ case arg => arg
+ }
+ lift(defs, arg1, prefix)
+ }
+
/** Lift arguments that are not-idempotent into ValDefs in buffer `defs`
* and replace by the idents of so created ValDefs.
*/
@@ -50,10 +59,10 @@ object EtaExpansion {
case MethodType(paramNames, paramTypes) =>
(args, paramNames, paramTypes).zipped map { (arg, name, tp) =>
if (tp isRef defn.ByNameParamClass) arg
- else lift(defs, arg, if (name contains '$') "" else name.toString)
+ else liftArg(defs, arg, if (name contains '$') "" else name.toString)
}
case _ =>
- args map (lift(defs, _))
+ args map (liftArg(defs, _))
}
/** Lift out function prefix and all arguments from application