From 115dcf1b3d42274fde2f78dfd87b60b8a059592c Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 2 Jun 2009 14:33:05 +0000 Subject: named argument disallowed when assignment expre... named argument disallowed when assignment expression would typecheck. minor fixe to names / defaults. --- .../tools/nsc/typechecker/NamesDefaults.scala | 30 +++++++++++++++++++--- .../scala/tools/nsc/typechecker/Typers.scala | 29 +++++++++++++++------ test/files/neg/names-defaults-neg.check | 24 ++++++----------- test/files/run/names-defaults.check | 4 +++ test/files/run/names-defaults.scala | 9 +++++++ 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 55a409775b..5ccc22f4dd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -349,7 +349,7 @@ trait NamesDefaults { self: Analyzer => // @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.findIndexOf(p => p.name == name && !p.hasFlag(SYNTHETIC)) + val pos = params.indexWhere(p => p.name == name && !p.hasFlag(SYNTHETIC)) if (pos == -1) { if (positionalAllowed) { argPos(index) = index @@ -362,9 +362,31 @@ trait NamesDefaults { self: Analyzer => } else if (argPos contains pos) { errorTree(arg, "parameter specified twice: "+ name) } else { - positionalAllowed = false - argPos(index) = pos - rhs + // for named arguments, check wether the assignment expression would + // typecheck. if it does, report an ambiguous error. + val param = params(pos) + val paramtpe = params(pos).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 = typer.context.extractUndetparams() + val subst = new SubstTypeMap(udp, udp map (_ => WildcardType)) + val res = typer.silent(_.typed(arg, subst(paramtpe))) match { + case _: TypeError => + positionalAllowed = false + argPos(index) = pos + // if `rhs' has the form `x = ...`, wrap it into a block, prevent + // treating it as named argument again. + if (isNamed(rhs)) Block(List(), rhs) + else rhs + case t: Tree => + errorTree(arg, "reference to "+ name +" is ambiguous; it is both, a parameter\n"+ + "name of the method and the name of a variable currently in scope.") + } + typer.context.undetparams = udp + res } case _ => argPos(index) = index diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 02f6d20522..89cfcb21e2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1510,12 +1510,23 @@ trait Typers { self: Analyzer => // only one overloaded method is allowed to have defaults if (meth.owner.isClass && meth.paramss.exists(_.exists(_.hasFlag(DEFAULTPARAM)))) { - val overloads = meth.owner.info.member(meth.name) - val otherHasDefault = overloads.filter(alt => { - alt != meth && alt.paramss.exists(_.exists(_.hasFlag(DEFAULTPARAM))) - }) != NoSymbol - if (otherHasDefault) - error(meth.pos, "multiple overloaded alternatives of "+ meth +" define default arguments") + // don't do the check if it has already failed for another alternatvie + if (meth.paramss.exists(_.exists(p => p.hasFlag(DEFAULTPARAM) && + !p.defaultGetter.tpe.isError))) { + val overloads = meth.owner.info.member(meth.name) + val others = overloads.filter(alt => { + alt != meth && alt.paramss.exists(_.exists(_.hasFlag(DEFAULTPARAM))) + }) + if (others != NoSymbol) { + // setting `ErrorType' to defaultGetters prevents the error + // messages saying "foo$default$1 is defined twice" + for (ps <- meth.paramss; p <- ps) + if (p hasFlag DEFAULTPARAM) p.defaultGetter.setInfo(ErrorType) + for (alt <- others.alternatives; ps <- alt.paramss; p <- ps) + if (p hasFlag DEFAULTPARAM) p.defaultGetter.setInfo(ErrorType) + error(meth.pos, "multiple overloaded alternatives of "+ meth +" define default arguments") + } + } if (meth.paramss.exists(_.exists(_.tpe.typeSymbol == RepeatedParamClass))) error(meth.pos, "methods with `*'-parameters are not allowed to have default arguments") @@ -2018,8 +2029,10 @@ trait Typers { self: Analyzer => if (allArgs.length == formals.length) { // a default for each missing argument was found val (namelessArgs, argPos) = removeNames(Typer.this)(allArgs, params) - transformNamedApplication(Typer.this, mode, pt)( - treeCopy.Apply(tree, fun1, namelessArgs), argPos) + if (namelessArgs exists (_.isErroneous)) setError(tree) + else + transformNamedApplication(Typer.this, mode, pt)( + treeCopy.Apply(tree, fun1, namelessArgs), argPos) } else { tryTupleApply.getOrElse { val suffix = diff --git a/test/files/neg/names-defaults-neg.check b/test/files/neg/names-defaults-neg.check index 5e621c9690..c7f7b80ade 100644 --- a/test/files/neg/names-defaults-neg.check +++ b/test/files/neg/names-defaults-neg.check @@ -12,6 +12,10 @@ names-defaults-neg.scala:8: error: positional after named argument. names-defaults-neg.scala:9: error: positional after named argument. test1(b = "(*", 23) ^ +names-defaults-neg.scala:14: error: reference to x is ambiguous; it is both, a parameter +name of the method and the name of a variable currently in scope. + test2(x = 1) + ^ names-defaults-neg.scala:16: error: not found: value c test1(c = 0, b = "joke") ^ @@ -70,29 +74,17 @@ match argument types (a: Int,b: java.lang.String) and expected result type Any names-defaults-neg.scala:75: error: multiple overloaded alternatives of method foo define default arguments def foo(a: Int = 0) = a ^ -names-defaults-neg.scala:76: error: multiple overloaded alternatives of method foo define default arguments - def foo(b: String = "another") = b - ^ -names-defaults-neg.scala:76: error: method foo$default$1 is defined twice - def foo(b: String = "another") = b - ^ names-defaults-neg.scala:85: error: multiple overloaded alternatives of method foo define default arguments override def foo(a: Int = 1092) = a ^ -names-defaults-neg.scala:86: error: multiple overloaded alternatives of method foo define default arguments - def foo(b: String = "lskdfj") - ^ -names-defaults-neg.scala:88: error: multiple overloaded alternatives of method bar define default arguments - def bar(i: Int = 129083) = i - ^ names-defaults-neg.scala:88: error: type mismatch; found : Int(129083) required: java.lang.String def bar(i: Int = 129083) = i ^ -names-defaults-neg.scala:86: error: method foo$default$1 is defined twice - def foo(b: String = "lskdfj") - ^ +names-defaults-neg.scala:88: error: multiple overloaded alternatives of method bar define default arguments + def bar(i: Int = 129083) = i + ^ names-defaults-neg.scala:93: error: using named or default arguments in a super constructor call is not allowed class B1 extends A1(10) ^ @@ -105,4 +97,4 @@ names-defaults-neg.scala:99: error: using named or default arguments in a self c names-defaults-neg.scala:105: error: using named or default arguments in a self constructor call is not allowed this(sep + b + sep) ^ -28 errors found +25 errors found diff --git a/test/files/run/names-defaults.check b/test/files/run/names-defaults.check index eea0bdc51a..643615368d 100644 --- a/test/files/run/names-defaults.check +++ b/test/files/run/names-defaults.check @@ -88,3 +88,7 @@ Factory(-1,blabla) Fact2(ju,1) Fact2(1,1) Fact2(10,blabla) +test5 +2 +test5 +3 \ No newline at end of file diff --git a/test/files/run/names-defaults.scala b/test/files/run/names-defaults.scala index 01eebd42f8..38ba317029 100644 --- a/test/files/run/names-defaults.scala +++ b/test/files/run/names-defaults.scala @@ -167,6 +167,14 @@ object Test extends Application { println(Fact2(10)().copy(y = "blabla")()) + // assignment to var <-> named argument + var argName = 1 + test5(argName = (argName = 2)) + println(argName) // should be 2 + test5({argName = 3; ()}) + println(argName) // should be 3 + + // DEFINITIONS def test1(a: Int, b: String) = println(a +": "+ b) def test2(u: Int, v: Int)(k: String, l: Int) = println(l +": "+ k +", "+ (u + v)) @@ -177,6 +185,7 @@ object Test extends Application { def inner(b: Int = a, c: String) = println(b +": "+ c) inner(c = "/") } + def test5(argName: Unit) = println("test5") } -- cgit v1.2.3