@@ -18,47 +18,56 @@ object CacheAliasImplicits {
1818
1919 /** Flags that disable caching */
2020 val NoCacheFlags =
21- StableRealizable | // It's a simple forwarder, leave it as one
22- Exported // Export forwarders are never cached
21+ StableRealizable // It's a simple forwarder, leave it as one
22+ | Exported // Export forwarders are never cached
2323}
2424
2525/** This phase ensures that the right hand side of parameterless alias implicits
26- * is cached. It applies to all alias implicits that have neither type parameters
27- * nor a given clause. Example: The alias
26+ * is cached if necessary. Dually, it optimizes lazy vak alias implicit to be uncached
27+ * if that does not change runtime behavior.
2828 *
29- * TC = rhs
29+ * A definition does not need to be cached if its right hand side has a stable type
30+ * and is of one of them forms
3031 *
31- * is expanded before this phase to:
32- *
33- * implicit def a: TC = rhs
34- *
35- * It is then expanded further as follows:
36- *
37- * 1. If `rhs` is a simple name `x` (possibly with a `this.` prefix) that
38- * refers to a value, leave it as is.
39- *
40- * 2. Otherwise, replace the definition with
41- *
42- * lazy implicit val a: TC = rhs
32+ * this
33+ * this.y
34+ * y
4335 */
4436class CacheAliasImplicits extends MiniPhase with IdentityDenotTransformer { thisPhase =>
4537 import tpd ._
4638
4739 override def phaseName : String = CacheAliasImplicits .name
4840
41+ private def needsCache (sym : Symbol , rhs : Tree )(using Context ): Boolean = rhs.tpe match
42+ case rhsTpe @ TermRef (NoPrefix , _)
43+ if rhsTpe.isStable => false
44+ case rhsTpe @ TermRef (pre : ThisType , _)
45+ if rhsTpe.isStable && pre.cls == sym.owner.enclosingClass => false
46+ case rhsTpe : ThisType => false
47+ case _ => true
48+
49+ /** Transform
50+ *
51+ * given def x = rhs
52+ *
53+ * to
54+ *
55+ * lazy val x = rhs
56+ *
57+ * unless `rhs` has a stable type and is of one of them forms
58+ *
59+ * this
60+ * this.y
61+ * y
62+ *
63+ * Parameterless given defs are generated during typeclass derivation.
64+ */
4965 override def transformDefDef (tree : DefDef )(using Context ): Tree = {
5066 val sym = tree.symbol
5167 val isCached = ! sym.is(Inline ) && {
5268 sym.info match {
5369 case ExprType (resTpe) if sym.is(Given , butNot = CacheAliasImplicits .NoCacheFlags ) =>
54- tree.rhs.tpe match {
55- case rhsTpe @ TermRef (NoPrefix , _)
56- if rhsTpe.isStable => false
57- case rhsTpe @ TermRef (pre : ThisType , _)
58- if rhsTpe.isStable && pre.cls == sym.owner.enclosingClass => false
59- case rhsTpe : ThisType => false
60- case _ => true
61- }
70+ needsCache(sym, tree.rhs)
6271 case _ => false
6372 }
6473 }
@@ -71,6 +80,30 @@ class CacheAliasImplicits extends MiniPhase with IdentityDenotTransformer { this
7180 }
7281 else tree
7382 }
83+
84+ /** Transform
85+ *
86+ * lazy given val x = rhs
87+ *
88+ * to
89+ *
90+ * def x = rhs
91+ *
92+ * provided `rhs` has a stable type and is of one of them forms
93+ *
94+ * this
95+ * this.y
96+ * y
97+ */
98+ override def transformValDef (tree : ValDef )(using Context ): Tree =
99+ val sym = tree.symbol
100+ if sym.isAllOf(Given , Lazy ) && ! needsCache(sym, tree.rhs) then
101+ sym.copySymDenotation(
102+ initFlags = sym.flags &~ Lazy | Method ,
103+ info = ExprType (sym.info))
104+ .installAfter(thisPhase)
105+ cpy.DefDef (tree)(tree.name, Nil , Nil , tree.tpt, tree.rhs)
106+ else tree
74107}
75108
76109
0 commit comments