Skip to content

Commit 1058374

Browse files
committed
Fix #2778: Fix handling of named parameters
If a named parameter conflicts with a positional one, we should report an error. We silently re-ordered parameters before.
1 parent 1d05796 commit 1058374

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -260,37 +260,46 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
260260
* 1. `(args diff toDrop)` can be reordered to match `pnames`
261261
* 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args`
262262
*/
263-
def recur(pnames: List[Name], args: List[Trees.Tree[T]],
264-
nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name]): List[Trees.Tree[T]] = pnames match {
263+
def handleNamed(pnames: List[Name], args: List[Trees.Tree[T]],
264+
nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name]): List[Trees.Tree[T]] = pnames match {
265265
case pname :: pnames1 if nameToArg contains pname =>
266266
// there is a named argument for this parameter; pick it
267-
nameToArg(pname) :: recur(pnames1, args, nameToArg - pname, toDrop + pname)
267+
nameToArg(pname) :: handleNamed(pnames1, args, nameToArg - pname, toDrop + pname)
268268
case _ =>
269269
def pnamesRest = if (pnames.isEmpty) pnames else pnames.tail
270270
args match {
271271
case (arg @ NamedArg(aname, _)) :: args1 =>
272272
if (toDrop contains aname) // argument is already passed
273-
recur(pnames, args1, nameToArg, toDrop - aname)
273+
handleNamed(pnames, args1, nameToArg, toDrop - aname)
274274
else if ((nameToArg contains aname) && pnames.nonEmpty) // argument is missing, pass an empty tree
275-
genericEmptyTree :: recur(pnames.tail, args, nameToArg, toDrop)
275+
genericEmptyTree :: handleNamed(pnames.tail, args, nameToArg, toDrop)
276276
else { // name not (or no longer) available for named arg
277277
def msg =
278278
if (methodType.paramNames contains aname)
279279
s"parameter $aname of $methString is already instantiated"
280280
else
281281
s"$methString does not have a parameter $aname"
282282
fail(msg, arg.asInstanceOf[Arg])
283-
arg :: recur(pnamesRest, args1, nameToArg, toDrop)
283+
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop)
284284
}
285285
case arg :: args1 =>
286-
arg :: recur(pnamesRest, args1, nameToArg, toDrop) // unnamed argument; pick it
286+
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop) // unnamed argument; pick it
287287
case Nil => // no more args, continue to pick up any preceding named args
288288
if (pnames.isEmpty) Nil
289-
else recur(pnamesRest, args, nameToArg, toDrop)
289+
else handleNamed(pnamesRest, args, nameToArg, toDrop)
290290
}
291291
}
292-
val nameAssocs = for (arg @ NamedArg(name, _) <- args) yield (name, arg)
293-
recur(methodType.paramNames, args, nameAssocs.toMap, Set())
292+
293+
def handlePositional(pnames: List[Name], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] =
294+
args match {
295+
case (arg: NamedArg) :: _ =>
296+
val nameAssocs = for (arg @ NamedArg(name, _) <- args) yield (name, arg)
297+
handleNamed(pnames, args, nameAssocs.toMap, Set())
298+
case arg :: args1 => arg :: handlePositional(pnames.tail, args1)
299+
case Nil => Nil
300+
}
301+
302+
handlePositional(methodType.paramNames, args)
294303
}
295304

296305
/** Splice new method reference into existing application */

tests/neg/i2778.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Main {
2+
def printName(first: String, last: String): Unit = {
3+
println(first + " " + last)
4+
}
5+
6+
def main(args: Array[String]): Unit = {
7+
printName("John", first = "Smith") // error: parameter is already instantiated
8+
}
9+
}

0 commit comments

Comments
 (0)