Skip to content

Commit 264566d

Browse files
committed
Peephole optimization to drop .apply from partially applied methods
1 parent a503b7a commit 264566d

File tree

3 files changed

+49
-16
lines changed

3 files changed

+49
-16
lines changed

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

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
614614
if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, "type prefix")
615615
checkLegalValue(select, pt)
616616
ConstFold(select)
617+
else if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then
618+
// Simplify `m.apply(...)` to `m(...)`
619+
qual
617620
else if couldInstantiateTypeVar(qual.tpe.widen) then
618621
// there's a simply visible type variable in the result; try again with a more defined qualifier type
619622
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
@@ -3699,6 +3702,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
36993702
|| Feature.warnOnMigration(MissingEmptyArgumentList(sym.show), tree.srcPos, version = `3.0`)
37003703
&& { patch(tree.span.endPos, "()"); true }
37013704

3705+
/** If this is a selection prototype of the form `.apply(...): R`, return the nested
3706+
* function prototype `(...)R`. Otherwise `pt`.
3707+
*/
3708+
def ptWithoutRedundantApply: Type = pt.revealIgnored match
3709+
case SelectionProto(nme.apply, mpt, _, _) =>
3710+
mpt.revealIgnored match
3711+
case fpt: FunProto => fpt
3712+
case _ => pt
3713+
case _ => pt
3714+
37023715
// Reasons NOT to eta expand:
37033716
// - we reference a constructor
37043717
// - we reference a typelevel method
@@ -3710,13 +3723,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
37103723
&& !ctx.mode.is(Mode.Pattern)
37113724
&& !(isSyntheticApply(tree) && !functionExpected)
37123725
then
3713-
if (!defn.isFunctionType(pt))
3714-
pt match {
3715-
case SAMType(_) if !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) =>
3716-
report.warning(ex"${tree.symbol} is eta-expanded even though $pt does not have the @FunctionalInterface annotation.", tree.srcPos)
3717-
case _ =>
3718-
}
3719-
simplify(typed(etaExpand(tree, wtp, arity), pt), pt, locked)
3726+
val pt1 = ptWithoutRedundantApply
3727+
if pt1 ne pt then
3728+
// Ignore `.apply` in `m.apply(...)`; it will later be simplified in typedSelect to `m(...)`
3729+
adapt1(tree, pt1, locked)
3730+
else
3731+
if (!defn.isFunctionType(pt))
3732+
pt match {
3733+
case SAMType(_) if !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) =>
3734+
report.warning(ex"${tree.symbol} is eta-expanded even though $pt does not have the @FunctionalInterface annotation.", tree.srcPos)
3735+
case _ =>
3736+
}
3737+
simplify(typed(etaExpand(tree, wtp, arity), pt), pt, locked)
37203738
else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol))
37213739
readaptSimplified(tpd.Apply(tree, Nil))
37223740
else if (wtp.isImplicitMethod)
@@ -3825,11 +3843,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38253843
def adaptNoArgs(wtp: Type): Tree = {
38263844
val ptNorm = underlyingApplied(pt)
38273845
def functionExpected = defn.isFunctionType(ptNorm)
3828-
def needsEta = pt match {
3829-
case _: SingletonType => false
3830-
case IgnoredProto(_: FunOrPolyProto) => false
3846+
def needsEta = pt.revealIgnored match
3847+
case _: SingletonType | _: FunOrPolyProto => false
38313848
case _ => true
3832-
}
38333849
var resMatch: Boolean = false
38343850
wtp match {
38353851
case wtp: ExprType =>
@@ -3846,17 +3862,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38463862
case wtp: MethodType if needsEta =>
38473863
val funExpected = functionExpected
38483864
val arity =
3849-
if (funExpected)
3850-
if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none))
3865+
if funExpected then
3866+
if !isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none) then
38513867
// if method type is fully defined, but expected type is not,
38523868
// prioritize method parameter types as parameter types of the eta-expanded closure
38533869
0
38543870
else defn.functionArity(ptNorm)
3855-
else {
3871+
else
38563872
val nparams = wtp.paramInfos.length
3857-
if (nparams > 0 || pt.eq(AnyFunctionProto)) nparams
3873+
if nparams > 0 || pt.eq(AnyFunctionProto) then nparams
38583874
else -1 // no eta expansion in this case
3859-
}
38603875
adaptNoArgsUnappliedMethod(wtp, funExpected, arity)
38613876
case _ =>
38623877
adaptNoArgsOther(wtp, functionExpected)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Map(0 -> 3.0, 1 -> 6.0, 2 -> 6.0)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import language.experimental.fewerBraces
2+
class A:
3+
def f(x: Int)(y: Int): Int = x + y
4+
5+
f(22).apply(33)
6+
7+
@main def Test =
8+
val theMap = Map(-1 -> 1, -2 -> 2, 0 -> 3, 1 -> 4, 2 -> 5)
9+
val res = theMap
10+
.groupMapReduce: (k, v) =>
11+
(k + 3) % 3
12+
.apply: (k, v) =>
13+
v.toDouble
14+
.apply: (x, y) =>
15+
x + y
16+
println(res)
17+

0 commit comments

Comments
 (0)