@@ -6,6 +6,8 @@ import MegaPhase.MiniPhase
66import core .*
77import Symbols .* , Contexts .* , Types .* , Decorators .*
88import StdNames .nme
9+ import SymUtils .*
10+ import NameKinds .AdaptedClosureName
911
1012/** Rewrite `(x1, ... xN) => f(x1, ... xN)` for N >= 0 to `f`,
1113 * provided `f` is a pure path of function type.
@@ -15,6 +17,11 @@ import StdNames.nme
1517 * where a context function is expected, unless that value has the
1618 * syntactic form of a context function literal.
1719 *
20+ * Also handle variants of eta-expansions where
21+ * - result f.apply(X_1,...,X_n) is subject to a synthetic cast, or
22+ * - the application uses a specialized apply method, or
23+ * - the closure is adapted (see Erasure#adaptClosure)
24+ *
1825 * Without this phase, when a contextual function is passed as an argument to a
1926 * recursive function, that would have the unfortunate effect of a linear growth
2027 * in transient thunks of identical type wrapped around each other, leading
@@ -27,20 +34,36 @@ class EtaReduce extends MiniPhase:
2734
2835 override def description : String = EtaReduce .description
2936
30- override def transformBlock (tree : Block )(using Context ): Tree = tree match
31- case Block (( meth : DefDef ) :: Nil , closure : Closure )
32- if meth.symbol == closure.meth.symbol =>
33- meth.rhs match
34- case Apply ( Select (fn, nme.apply), args )
35- if meth .paramss.head.corresponds(args)((param, arg) =>
37+ override def transformBlock (tree : Block )(using Context ): Tree =
38+
39+ def tryReduce ( mdef : DefDef , rhs : Tree ) : Tree = rhs match
40+ case Apply ( Select (fn, name), args)
41+ if (name == nme.apply || defn. FunctionSpecializedApplyNames .contains(name) )
42+ && mdef .paramss.head.corresponds(args)((param, arg) =>
3643 arg.isInstanceOf [Ident ] && arg.symbol == param.symbol)
37- && isPurePath(fn)
38- && fn.tpe <:< tree.tpe
39- && defn.isFunctionClass(fn.tpe.widen.typeSymbol) =>
40- report.log(i " eta reducing $tree --> $fn" )
41- fn
42- case _ => tree
43- case _ => tree
44+ && isPurePath(fn)
45+ && fn.tpe <:< tree.tpe
46+ && defn.isFunctionClass(fn.tpe.widen.typeSymbol) =>
47+ report.log(i " eta reducing $tree --> $fn" )
48+ fn
49+ case TypeApply (Select (qual, _), _) if rhs.symbol.isTypeCast && rhs.span.isSynthetic =>
50+ tryReduce(mdef, qual)
51+ case _ =>
52+ tree
53+
54+ tree match
55+ case Block ((meth : DefDef ) :: Nil , expr) if meth.symbol.isAnonymousFunction =>
56+ expr match
57+ case closure : Closure if meth.symbol == closure.meth.symbol =>
58+ tryReduce(meth, meth.rhs)
59+ case Block ((adapted : DefDef ) :: Nil , closure : Closure )
60+ if adapted.name.is(AdaptedClosureName ) && adapted.symbol == closure.meth.symbol =>
61+ tryReduce(meth, meth.rhs)
62+ case _ =>
63+ tree
64+ case _ =>
65+ tree
66+ end transformBlock
4467
4568end EtaReduce
4669
0 commit comments