aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-03-16 17:30:04 +0100
committerTobias Schlatter <tobias@meisch.ch>2014-03-21 11:24:03 +0100
commitee1251f37f844bdb4f4ea69177e8183ad74e7b3d (patch)
tree9540aad09833958cd80de1ddc7cc8087ba943f3e
parentbb90b26fbca27f432ade46ae572b82e1b8027b19 (diff)
downloaddotty-ee1251f37f844bdb4f4ea69177e8183ad74e7b3d.tar.gz
dotty-ee1251f37f844bdb4f4ea69177e8183ad74e7b3d.tar.bz2
dotty-ee1251f37f844bdb4f4ea69177e8183ad74e7b3d.zip
Fix of t0438 - lambdas and eta expansion
Two fixes were needed 1) When typing a function value (x1: T1, ..., xN: Tn) => e, don't unconditionally issue an error if the expected function type arity is different from N. Instead, issue an error only if one of the types T1, ..., Tn is absent. The idea is that only then we need to consult the expected type for the parameter type. This allows to fix the problem later by an implicit conversion applied to the function value. 2) When eta-expanding, do not automtically take the arity of the expected function value as the arity of the generated lambda. Instead, take the method's arity, and copy method parameters into the lambda in case the arities are different.
-rw-r--r--src/dotty/tools/dotc/typer/EtaExpansion.scala28
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala11
-rw-r--r--tests/pos/t0438.scala (renamed from tests/untried/pos/t0438.scala)2
-rw-r--r--tests/untried/pos/t0301.scala12
-rw-r--r--tests/untried/pos/t0304.scala5
-rw-r--r--tests/untried/pos/t0305.scala7
-rw-r--r--tests/untried/pos/t0453.scala6
7 files changed, 26 insertions, 45 deletions
diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala
index 46a1d3583..099dd943a 100644
--- a/src/dotty/tools/dotc/typer/EtaExpansion.scala
+++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala
@@ -95,25 +95,35 @@ object EtaExpansion {
/** Eta-expanding a tree means converting a method reference to a function value.
* @param tree The tree to expand
- * @param paramNames The names of the parameters to use in the expansion
- * Let `paramNames` be x1, ..., xn
+ * @param mt The type of the method reference
+ * @param xarity The arity of the expected function type
* and assume the lifted application of `tree` (@see liftApp) is
*
* { val xs = es; expr }
*
- * Then the eta-expansion is
+ * If xarity matches the number of parameters in `mt`, the eta-expansion is
*
- * { val xs = es; (x1, ..., xn) => expr(xx1, ..., xn) }
+ * { val xs = es; (x1, ..., xn) => expr(x1, ..., xn) }
*
- * This is an untyped tree, with `es` and `expr` as typed splices.
+ * Note that the function value's parameters are untyped, hence the type will
+ * be supplied by the environment (or if missing be supplied by the target
+ * method as a fallback). On the other hand, if `xarity` is different from
+ * the number of parameters in `mt`, then we cannot propagate parameter types
+ * from the expected type, and we fallback to using the method's original
+ * parameter types instead.
+ *
+ * In either case, the result is an untyped tree, with `es` and `expr` as typed splices.
*/
- def etaExpand(tree: Tree, paramNames: List[TermName])(implicit ctx: Context): untpd.Tree = {
+ def etaExpand(tree: Tree, mt: MethodType, xarity: Int)(implicit ctx: Context): untpd.Tree = {
import untpd._
val defs = new mutable.ListBuffer[tpd.Tree]
val lifted: Tree = TypedSplice(liftApp(defs, tree))
- val params = paramNames map (name =>
- ValDef(Modifiers(Synthetic | Param), name, TypeTree(), EmptyTree).withPos(tree.pos))
- val ids = paramNames map (name =>
+ val paramTypes: List[Tree] =
+ if (mt.paramTypes.length == xarity) mt.paramTypes map (_ => TypeTree())
+ else mt.paramTypes map TypeTree
+ val params = (mt.paramNames, paramTypes).zipped.map((name, tpe) =>
+ ValDef(Modifiers(Synthetic | Param), name, TypeTree(tpe), EmptyTree).withPos(tree.pos))
+ val ids = mt.paramNames map (name =>
Ident(name).withPos(tree.pos))
val body = Apply(lifted, ids)
val fn = untpd.Function(params, body)
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 67e5c5902..4fdee86a3 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -496,14 +496,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
errorType(s"missing parameter type for parameter ${param.name}$ofFun, expected = ${pt.show}", param.pos)
}
- if (protoFormals.length != params.length)
- ctx.error(s"wrong number of parameters, expected: ${protoFormals.length}", tree.pos)
+ def protoFormal(i: Int): Type =
+ if (protoFormals.length == params.length) protoFormals(i)
+ else errorType(s"wrong number of parameters, expected: ${protoFormals.length}", tree.pos)
val inferredParams: List[untpd.ValDef] =
- for ((param, formal) <- params zip protoFormals) yield
+ for ((param, i) <- params.zipWithIndex) yield
if (!param.tpt.isEmpty) param
else {
- val paramTpt = untpd.TypeTree(inferredParamType(param, formal))
+ val paramTpt = untpd.TypeTree(inferredParamType(param, protoFormal(i)))
cpy.ValDef(param, param.mods, param.name, paramTpt, param.rhs)
}
@@ -1135,7 +1136,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
else if (pt eq AnyFunctionProto) wtp.paramTypes.length
else -1
if (arity >= 0 && !tree.symbol.isConstructor)
- typed(etaExpand(tree, wtp.paramNames take arity), pt)
+ typed(etaExpand(tree, wtp, arity), pt)
else if (wtp.paramTypes.isEmpty)
adaptInterpolated(tpd.Apply(tree, Nil), pt)
else
diff --git a/tests/untried/pos/t0438.scala b/tests/pos/t0438.scala
index dd1e7f9a9..1615e3da7 100644
--- a/tests/untried/pos/t0438.scala
+++ b/tests/pos/t0438.scala
@@ -5,7 +5,7 @@ class Foo {
def foo(f: ((Int, Int)) => Int) = f
def bar(x: Int, y: Int) = x + y
- foo({ (x: Int, y: Int) => x + y }) // works
+ foo(((x: Int, y: Int) => x + y)) // works
foo(pair2fun2(bar _)) // works
foo(bar _) // error
foo(bar) // same error
diff --git a/tests/untried/pos/t0301.scala b/tests/untried/pos/t0301.scala
deleted file mode 100644
index 24b477601..000000000
--- a/tests/untried/pos/t0301.scala
+++ /dev/null
@@ -1,12 +0,0 @@
-package fos
-
-abstract class Expr
-case class Var() extends Expr
-
-object Analyzer {
- def substitution(expr: Expr, cls: (Var,Var)): Expr =
- expr match {
- case cls._2 => cls._1 // source of the error
- case _ => expr
- }
-}
diff --git a/tests/untried/pos/t0304.scala b/tests/untried/pos/t0304.scala
deleted file mode 100644
index 607a115db..000000000
--- a/tests/untried/pos/t0304.scala
+++ /dev/null
@@ -1,5 +0,0 @@
-object O {
- def f1 = -1;
- def f2 = 0-1;
- def f3 = f1 + f2;
-}
diff --git a/tests/untried/pos/t0305.scala b/tests/untried/pos/t0305.scala
deleted file mode 100644
index 4838b1fcf..000000000
--- a/tests/untried/pos/t0305.scala
+++ /dev/null
@@ -1,7 +0,0 @@
-object Test extends App {
-
- def foo(is:Int*) = 1;
- def foo(i:Int) = 2;
-
- assert(foo( List(3):_* ) == 1)
-}
diff --git a/tests/untried/pos/t0453.scala b/tests/untried/pos/t0453.scala
deleted file mode 100644
index dfacc5eed..000000000
--- a/tests/untried/pos/t0453.scala
+++ /dev/null
@@ -1,6 +0,0 @@
-object Test {
- val foo = new {
- trait Bar
- def l () : Bar = { new Bar {} }
- }
-}