@@ -18,47 +18,56 @@ object CacheAliasImplicits {
18
18
19
19
/** Flags that disable caching */
20
20
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
23
23
}
24
24
25
25
/** 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.
28
28
*
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
30
31
*
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
43
35
*/
44
36
class CacheAliasImplicits extends MiniPhase with IdentityDenotTransformer { thisPhase =>
45
37
import tpd ._
46
38
47
39
override def phaseName : String = CacheAliasImplicits .name
48
40
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
+ */
49
65
override def transformDefDef (tree : DefDef )(using Context ): Tree = {
50
66
val sym = tree.symbol
51
67
val isCached = ! sym.is(Inline ) && {
52
68
sym.info match {
53
69
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)
62
71
case _ => false
63
72
}
64
73
}
@@ -71,6 +80,30 @@ class CacheAliasImplicits extends MiniPhase with IdentityDenotTransformer { this
71
80
}
72
81
else tree
73
82
}
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
74
107
}
75
108
76
109
0 commit comments