Skip to content

Commit 0109fef

Browse files
authored
Merge pull request #2701 from dotty-staging/fix-#2570
Changes to eta expansion
2 parents f77e8de + ff32e6e commit 0109fef

File tree

8 files changed

+82
-16
lines changed

8 files changed

+82
-16
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,14 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
474474
case _ => false
475475
}
476476

477+
/** Is tree a compiler-generated `.apply` node that refers to the
478+
* apply of a function class?
479+
*/
480+
def isSyntheticApply(tree: Tree): Boolean = tree match {
481+
case Select(qual, nme.apply) => tree.pos.end == qual.pos.end
482+
case _ => false
483+
}
484+
477485
/** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */
478486
def stripCast(tree: Tree)(implicit ctx: Context): Tree = {
479487
def isCast(sel: Tree) = sel.symbol == defn.Any_asInstanceOf

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,8 +809,8 @@ class Definitions {
809809
*/
810810
def erasedFunctionClass(cls: Symbol): Symbol = {
811811
val arity = scalaClassName(cls).functionArity
812-
if (arity > 22) defn.FunctionXXLClass
813-
else if (arity >= 0) defn.FunctionClass(arity)
812+
if (arity > 22) FunctionXXLClass
813+
else if (arity >= 0) FunctionClass(arity)
814814
else NoSymbol
815815
}
816816

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -702,8 +702,7 @@ object messages {
702702
private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough"
703703

704704
//TODO add def simpleParamName to ParamInfo
705-
private val expectedArgString = fntpe
706-
.widen.typeParams
705+
private val expectedArgString = expectedArgs
707706
.map(_.paramName.unexpandedName.show)
708707
.mkString("[", ", ", "]")
709708

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

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,9 +2006,48 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
20062006
// prioritize method parameter types as parameter types of the eta-expanded closure
20072007
0
20082008
else defn.functionArity(ptNorm)
2009-
else if (pt eq AnyFunctionProto) wtp.paramInfos.length
2010-
else -1
2011-
if (arity >= 0 && !tree.symbol.isConstructor)
2009+
else {
2010+
val nparams = wtp.paramInfos.length
2011+
if (nparams > 0 || pt.eq(AnyFunctionProto)) nparams
2012+
else -1 // no eta expansion in this case
2013+
}
2014+
2015+
/** A synthetic apply should be eta-expanded if it is the apply of an implicit function
2016+
* class, and the expected type is a function type. This rule is needed so we can pass
2017+
* an implicit function to a regular function type. So the following is OK
2018+
*
2019+
* val f: implicit A => B = ???
2020+
* val g: A => B = f
2021+
*
2022+
* and the last line expands to
2023+
*
2024+
* val g: A => B = (x$0: A) => f.apply(x$0)
2025+
*
2026+
* One could be tempted not to eta expand the rhs, but that would violate the invariant
2027+
* that expressions of implicit function types are always implicit closures, which is
2028+
* exploited by ShortcutImplicits.
2029+
*
2030+
* On the other hand, the following would give an error if there is no implicit
2031+
* instance of A available.
2032+
*
2033+
* val x: AnyRef = f
2034+
*
2035+
* That's intentional, we want to fail here, otherwise some unsuccesful implicit searches
2036+
* would go undetected.
2037+
*
2038+
* Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala.
2039+
*/
2040+
def isExpandableApply =
2041+
defn.isImplicitFunctionClass(tree.symbol.maybeOwner) && defn.isFunctionType(ptNorm)
2042+
2043+
// Reasons NOT to eta expand:
2044+
// - we reference a constructor
2045+
// - we are in a patterm
2046+
// - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that)
2047+
if (arity >= 0 &&
2048+
!tree.symbol.isConstructor &&
2049+
!ctx.mode.is(Mode.Pattern) &&
2050+
!(isSyntheticApply(tree) && !isExpandableApply))
20122051
typed(etaExpand(tree, wtp, arity), pt)
20132052
else if (wtp.paramInfos.isEmpty)
20142053
adaptInterpolated(tpd.Apply(tree, Nil), pt, EmptyTree)

tests/neg/i1503.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ object Test {
99

1010
def main(args: Array[String]) = {
1111
(if (cond) foo1 else bar1)() // error: Unit does not take parameters
12-
(if (cond) foo2 else bar2)(22) // error: missing arguments // error: missing arguments
12+
(if (cond) foo2 else bar2)(22) // OK after changes to eta expansion
1313
}
1414
}

tests/neg/i1716.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
object Fail {
22
def f(m: Option[Int]): Unit = {
33
m match {
4-
case x @ Some[_] => // error
4+
case x @ Some[_] => // error: unbound wildcard type
55
case _ =>
66
}
77
}
8-
Some[_] // error
8+
Some[_] // error: unbound wildcard type
9+
910
}

tests/neg/typedapply.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@ object typedapply {
66

77
foo[Int, String, String](1, "abc") // error: wrong number of type parameters
88

9+
def fuz(x: Int, y: String) = (x, y)
10+
11+
val x1 = fuz.curried // OK
12+
val x2: Int => String => (Int, String) = x1
13+
914
def bar(x: Int) = x
1015

11-
bar[Int](1) // error: does not take parameters
16+
bar[Int](1) // error: does not take type parameters
17+
18+
bar.baz // error: baz is not a member of Int => Int
19+
1220

1321
}
22+
23+

tests/pos/i2570.scala

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,18 @@ object Test {
2121
def sum2(x: Int, ys: Int*) = (x /: ys)(_ + _)
2222
val h1: ((Int, Seq[Int]) => Int) = sum2
2323

24-
// Not yet:
25-
// val h1 = repeat
26-
// val h2: (String, Int, Int) = h1
27-
// val h3 = sum
28-
// val h4: (Int, => Int) = h3
24+
val h3 = repeat
25+
val h4: ((String, Int, Int) => String) = h3
26+
val h5 = sum
27+
val h6: ((Int, => Int) => Int) = h5
28+
29+
class A
30+
class B
31+
class C
32+
implicit object b extends B
33+
34+
def foo(x: A)(implicit bla: B): C = ???
35+
36+
val h7 = foo
37+
val h8: A => C = h7
2938
}

0 commit comments

Comments
 (0)