diff options
-rw-r--r-- | TODO | 16 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/LambdaLift.scala | 9 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 17 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 26 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala | 62 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 71 | ||||
-rw-r--r-- | test/files/neg/bug550.check | 2 | ||||
-rw-r--r-- | test/files/neg/names-defaults-neg.check | 41 | ||||
-rw-r--r-- | test/files/neg/names-defaults-neg.scala | 21 | ||||
-rw-r--r-- | test/files/neg/t0226.check | 2 | ||||
-rw-r--r-- | test/files/run/names-defaults.check | 9 | ||||
-rw-r--r-- | test/files/run/names-defaults.scala | 43 |
12 files changed, 212 insertions, 107 deletions
@@ -1,16 +0,0 @@ -Ideas and things to be done, in random order -============================================ - -* Private members that are specialized should be name-mangled. The are - now turned to 'protected'. Probably 'expandName' is enough (keep the old - symbols). - -* specialize methods only when representation changes (basically when - value parameters have naked type parameters or arrays of naked type - params). - -* handle specialized type members (whatever that may mean) - -* Could we have better runtime types for specialized instances? - -* Could we have user provided specializations? 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() diff --git a/test/files/neg/bug550.check b/test/files/neg/bug550.check index 1cdb7931ca..bf92f6bb05 100644 --- a/test/files/neg/bug550.check +++ b/test/files/neg/bug550.check @@ -1,7 +1,7 @@ bug550.scala:6: error: type List takes type parameters def sum[a](xs: List)(implicit m: Monoid[a]): a = ^ -bug550.scala:8: error: no implicit argument matching parameter type Monoid[a] was found. +bug550.scala:8: error: could not find implicit value for parameter m:Monoid[a]. sum(List(1,2,3)) ^ two errors found diff --git a/test/files/neg/names-defaults-neg.check b/test/files/neg/names-defaults-neg.check index b208157c6a..f1db7676dd 100644 --- a/test/files/neg/names-defaults-neg.check +++ b/test/files/neg/names-defaults-neg.check @@ -1,4 +1,5 @@ -names-defaults-neg.scala:63: error: not enough arguments for method apply: (a: Int,b: String)(c: Int*)Fact in object Fact, unspecified parameter: value b +names-defaults-neg.scala:63: error: not enough arguments for method apply: (a: Int,b: String)(c: Int*)Fact in object Fact. +Unspecified value parameter b. val fac = Fact(1)(2, 3) ^ names-defaults-neg.scala:5: error: type mismatch; @@ -71,23 +72,33 @@ and method f in object t8 of type (a: Int,b: java.lang.Object)java.lang.String match argument types (a: Int,b: java.lang.String) and expected result type Any println(t8.f(a = 0, b = "1")) // ambigous reference ^ -names-defaults-neg.scala:75: error: multiple overloaded alternatives of method foo define default arguments +names-defaults-neg.scala:67: error: wrong number of arguments for <none>: (x: Int,y: String)A1 + A1() match { case A1(_) => () } + ^ +names-defaults-neg.scala:74: error: inferred kinds of the type arguments (List[Int]) do not conform to the expected kinds of the type parameters (type T). +List[Int]'s type parameters do not match type T's expected parameters: class List has one type parameter, but type T has one + test4() + ^ +error: type mismatch; + found : List[Int] + required: List[List[List[List[X forSome { type X }]]]] +names-defaults-neg.scala:77: error: type mismatch; + found : List[Int] + required: List[List[?]] + def test6[T](x: List[List[T]] = List(1,2)) = x + ^ +names-defaults-neg.scala:80: error: type mismatch; + found : Int + required: String + new A2[String]() + ^ +names-defaults-neg.scala:90: error: multiple overloaded alternatives of method foo define default arguments def foo(a: Int = 0) = a ^ -names-defaults-neg.scala:85: error: multiple overloaded alternatives of method foo define default arguments +names-defaults-neg.scala:100: error: multiple overloaded alternatives of method foo define default arguments override def foo(a: Int = 1092) = a ^ -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:88: error: multiple overloaded alternatives of method bar define default arguments +names-defaults-neg.scala:103: error: multiple overloaded alternatives of method bar define default arguments def bar(i: Int = 129083) = i ^ -names-defaults-neg.scala:95: error: type mismatch; - found : java.lang.Object - required: java.lang.String -class B5 extends A5 { override def foo(a: Object = new Object) = 1 } - ^ -22 errors found
\ No newline at end of file +25 errors found diff --git a/test/files/neg/names-defaults-neg.scala b/test/files/neg/names-defaults-neg.scala index 68cdcf782f..2e9f755174 100644 --- a/test/files/neg/names-defaults-neg.scala +++ b/test/files/neg/names-defaults-neg.scala @@ -63,6 +63,21 @@ object Test extends Application { val fac = Fact(1)(2, 3) val facc = fac.copy(b = "dlkfj")() + // no defaults in patterns + A1() match { case A1(_) => () } + + + // return types of default getters + + // definition compiles, but default cannot be used, it doesn't conform + def test4[T[P]](x: T[T[List[T[X forSome { type X }]]]] = List(1,2)) = x + test4() + + // doesn't compile + def test6[T](x: List[List[T]] = List(1,2)) = x + + // correct error message + new A2[String]() // DEFINITIONS def test1(a: Int, b: String) = a +": "+ b @@ -90,6 +105,6 @@ class C extends B { case class Fact(a: Int, b: String)(c: Int*) -// overriding default must have same type -class A5 { def foo(a: Object = "dlkf") = 0 } -class B5 extends A5 { override def foo(a: Object = new Object) = 1 } +case class A1(x: Int = 1, y: String = "2") + +class A2[T](a: T = 1) diff --git a/test/files/neg/t0226.check b/test/files/neg/t0226.check index 0935a3b14e..8464d72914 100644 --- a/test/files/neg/t0226.check +++ b/test/files/neg/t0226.check @@ -4,7 +4,7 @@ t0226.scala:5: error: not found: type A1 t0226.scala:5: error: not found: type A1 (implicit _1: Foo[List[A1]], _2: Foo[A2]): Foo[Tuple2[List[A1], A2]] = ^ -t0226.scala:8: error: no implicit argument matching parameter type Test.this.Foo[((List[Char], Int), (object Nil, Int))] was found. +t0226.scala:8: error: could not find implicit value for parameter rep:Test.this.Foo[((List[Char], Int), (object Nil, Int))]. foo(((List('b'), 3), (Nil, 4))) ^ three errors found diff --git a/test/files/run/names-defaults.check b/test/files/run/names-defaults.check index 07a7d7d1be..e8c088fe7a 100644 --- a/test/files/run/names-defaults.check +++ b/test/files/run/names-defaults.check @@ -97,4 +97,11 @@ slkdfj1 lskfdjlk 11 2 -20
\ No newline at end of file +20 +10 +jaa +kldfj110101 +klfj1 +blublu1 +my text +List(1, 2) diff --git a/test/files/run/names-defaults.scala b/test/files/run/names-defaults.scala index 6ec391af2f..3efefc9e71 100644 --- a/test/files/run/names-defaults.scala +++ b/test/files/run/names-defaults.scala @@ -198,6 +198,40 @@ object Test extends Application { new B4() new B5() + // no re-naming of parameters which are free in a closure of the body (lambdalift) + println(test6(10)()) + test7("jaa") + + // implicits + defaults + { + implicit val implInt = 10101 + println(test8()) + } + + println(test9) + + { + implicit val implString = "blublu" + println(test9) + } + + + // result type of default getters: parameter type, except if this one mentions any type + // parameter, in which case the result type is inferred. examples: + + // result type of default getter is "String => String". if it were infered, the compiler + // would put "Nothing => Nothing", which is useless + def transform(s: String, f: String => String = identity _) = f(s) + println(transform("my text")) + + // result type of the default getter is inferred (parameter type mentions type parameter T) + def test10[T](x: List[T] = List(1,2)) = x + println(test10()) + + // some complicated type which mentions T + def test11[T[P]](x: T[T[List[T[X forSome { type X }]]]] = List(1,2)) = x + // (cannot call f using the default, List(1,2) doesn't match the param type) + // DEFINITIONS def test1(a: Int, b: String) = println(a +": "+ b) @@ -210,6 +244,11 @@ object Test extends Application { inner(c = "/") } def test5(argName: Unit) = println("test5") + def test6(x: Int) = { () => x } + def test7(s: String) = List(1).foreach(_ => println(s)) + + def test8(x: Int = 1)(implicit y: Int, z: String = "kldfj") = z + x + y + def test9(implicit x: Int = 1, z: String = "klfj") = z + x } @@ -288,3 +327,7 @@ class B4 extends A5(10)() { class B5 extends A5(y = 20, x = 2)() { println(y) } + +// overriding default can be less specific (but has to conform to argument type!) +class A6 { def foo(a: Object = "dlkf") = 0 } +class B6 extends A6 { override def foo(a: Object = new Object) = 1 } |