1+ package dotty .tools
2+ package dotc
3+ package transform
4+
5+ import MegaPhase .MiniPhase
6+ import core .*
7+ import Symbols .* , Contexts .* , Types .* , Decorators .*
8+ import StdNames .nme
9+ import ast .Trees .*
10+
11+ /** Rewrite `(x1, ... xN) => f(x1, ... xN)` for N >= 0 to `f`,
12+ * provided `f` is a pure path of function type.
13+ *
14+ * This optimization is crucial for context functions. The compiler
15+ * produces a contextual closure around values passed as arguments
16+ * where a context function is expected, unless that value has the
17+ * syntactic form of a context function literal.
18+ *
19+ * Without this phase, when a contextual function is passed as an argument to a
20+ * recursive function, that would have the unfortunate effect of a linear growth
21+ * in transient thunks of identical type wrapped around each other, leading
22+ * to performance degradation, and in some cases, stack overflows.
23+ */
24+ class EtaReduce extends MiniPhase :
25+ import ast .tpd ._
26+
27+ override def phaseName : String = " etaReduce"
28+
29+ override def transformBlock (tree : Block )(using Context ): Tree = tree match
30+ case Block ((meth : DefDef ) :: Nil , closure : Closure )
31+ if meth.symbol == closure.meth.symbol =>
32+ meth.rhs match
33+ case Apply (Select (fn, nme.apply), args)
34+ if meth.paramss.head.corresponds(args)((param, arg) =>
35+ arg.isInstanceOf [Ident ] && arg.symbol == param.symbol)
36+ && isPurePath(fn)
37+ && fn.tpe <:< tree.tpe
38+ && defn.isFunctionClass(fn.tpe.widen.typeSymbol) =>
39+ report.log(i " eta reducing $tree --> $fn" )
40+ fn
41+ case _ => tree
42+ case _ => tree
43+
44+ end EtaReduce
0 commit comments