diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index f3bce4000079..cee5ce6df905 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -470,6 +470,14 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case _ => false } + /** Is tree a compiler-generated `.apply` node that refers to the + * apply of a function class? + */ + def isSyntheticApply(tree: Tree): Boolean = tree match { + case Select(qual, nme.apply) => tree.pos.end == qual.pos.end + case _ => false + } + /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */ def stripCast(tree: Tree)(implicit ctx: Context): Tree = { def isCast(sel: Tree) = sel.symbol == defn.Any_asInstanceOf diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 5e58e60c3603..e063c047167a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -809,8 +809,8 @@ class Definitions { */ def erasedFunctionClass(cls: Symbol): Symbol = { val arity = scalaClassName(cls).functionArity - if (arity > 22) defn.FunctionXXLClass - else if (arity >= 0) defn.FunctionClass(arity) + if (arity > 22) FunctionXXLClass + else if (arity >= 0) FunctionClass(arity) else NoSymbol } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 3ecdbdb85746..b3bd9e43cfe3 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -668,8 +668,7 @@ object messages { private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough" //TODO add def simpleParamName to ParamInfo - private val expectedArgString = fntpe - .widen.typeParams + private val expectedArgString = expectedArgs .map(_.paramName.unexpandedName.show) .mkString("[", ", ", "]") diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 378cd97cf7cd..67397d850935 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2006,9 +2006,48 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // prioritize method parameter types as parameter types of the eta-expanded closure 0 else defn.functionArity(ptNorm) - else if (pt eq AnyFunctionProto) wtp.paramInfos.length - else -1 - if (arity >= 0 && !tree.symbol.isConstructor) + else { + val nparams = wtp.paramInfos.length + if (nparams > 0 || pt.eq(AnyFunctionProto)) nparams + else -1 // no eta expansion in this case + } + + /** A synthetic apply should be eta-expanded if it is the apply of an implicit function + * class, and the expected type is a function type. This rule is needed so we can pass + * an implicit function to a regular function type. So the following is OK + * + * val f: implicit A => B = ??? + * val g: A => B = f + * + * and the last line expands to + * + * val g: A => B = (x$0: A) => f.apply(x$0) + * + * One could be tempted not to eta expand the rhs, but that would violate the invariant + * that expressions of implicit function types are always implicit closures, which is + * exploited by ShortcutImplicits. + * + * On the other hand, the following would give an error if there is no implicit + * instance of A available. + * + * val x: AnyRef = f + * + * That's intentional, we want to fail here, otherwise some unsuccesful implicit searches + * would go undetected. + * + * Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala. + */ + def isExpandableApply = + defn.isImplicitFunctionClass(tree.symbol.maybeOwner) && defn.isFunctionType(ptNorm) + + // Reasons NOT to eta expand: + // - we reference a constructor + // - we are in a patterm + // - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that) + if (arity >= 0 && + !tree.symbol.isConstructor && + !ctx.mode.is(Mode.Pattern) && + !(isSyntheticApply(tree) && !isExpandableApply)) typed(etaExpand(tree, wtp, arity), pt) else if (wtp.paramInfos.isEmpty) adaptInterpolated(tpd.Apply(tree, Nil), pt, EmptyTree) diff --git a/tests/neg/i1503.scala b/tests/neg/i1503.scala index 8e5dc53c6813..8433d6d35853 100644 --- a/tests/neg/i1503.scala +++ b/tests/neg/i1503.scala @@ -9,6 +9,6 @@ object Test { def main(args: Array[String]) = { (if (cond) foo1 else bar1)() // error: Unit does not take parameters - (if (cond) foo2 else bar2)(22) // error: missing arguments // error: missing arguments + (if (cond) foo2 else bar2)(22) // OK after changes to eta expansion } } diff --git a/tests/neg/i1716.scala b/tests/neg/i1716.scala index 1a3fd71d0a77..fab79c833e4c 100644 --- a/tests/neg/i1716.scala +++ b/tests/neg/i1716.scala @@ -1,9 +1,10 @@ object Fail { def f(m: Option[Int]): Unit = { m match { - case x @ Some[_] => // error + case x @ Some[_] => // error: unbound wildcard type case _ => } } - Some[_] // error + Some[_] // error: unbound wildcard type + } diff --git a/tests/neg/typedapply.scala b/tests/neg/typedapply.scala index 706b05da3592..bddbe73e54c1 100644 --- a/tests/neg/typedapply.scala +++ b/tests/neg/typedapply.scala @@ -6,8 +6,18 @@ object typedapply { foo[Int, String, String](1, "abc") // error: wrong number of type parameters + def fuz(x: Int, y: String) = (x, y) + + val x1 = fuz.curried // OK + val x2: Int => String => (Int, String) = x1 + def bar(x: Int) = x - bar[Int](1) // error: does not take parameters + bar[Int](1) // error: does not take type parameters + + bar.baz // error: baz is not a member of Int => Int + } + + diff --git a/tests/pos/i2570.scala b/tests/pos/i2570.scala index 1fa9a07567e3..350a4daf04e8 100644 --- a/tests/pos/i2570.scala +++ b/tests/pos/i2570.scala @@ -21,9 +21,18 @@ object Test { def sum2(x: Int, ys: Int*) = (x /: ys)(_ + _) val h1: ((Int, Seq[Int]) => Int) = sum2 -// Not yet: -// val h1 = repeat -// val h2: (String, Int, Int) = h1 -// val h3 = sum -// val h4: (Int, => Int) = h3 + val h3 = repeat + val h4: ((String, Int, Int) => String) = h3 + val h5 = sum + val h6: ((Int, => Int) => Int) = h5 + + class A + class B + class C + implicit object b extends B + + def foo(x: A)(implicit bla: B): C = ??? + + val h7 = foo + val h8: A => C = h7 }