@@ -4,6 +4,8 @@ package transform.localopt
4
4
import core .Constants .Constant
5
5
import core .Contexts .Context
6
6
import core .Decorators ._
7
+ import core .Names .Name
8
+ import core .Types .Type
7
9
import core .NameOps ._
8
10
import core .StdNames ._
9
11
import core .Symbols ._
@@ -13,46 +15,67 @@ import scala.collection.mutable
13
15
import transform .SymUtils ._
14
16
import config .Printers .simplify
15
17
16
- /** Inline case classes as vals, this essentially (local) implements multi
17
- * parameter value classes. The main motivation is to get ride of all the
18
- * intermediate tuples coming from pattern matching expressions.
18
+ /** Inline case classes as vals.
19
+ *
20
+ * In other words, implements (local) multi parameter value classes. The main
21
+ * motivation is to get ride of all the intermediate tuples created by the
22
+ * pattern matcher.
19
23
*/
20
24
class InlineLocalObjects extends Optimisation {
21
25
import ast .tpd ._
22
26
23
27
// In the end only calls constructor. Reason for unconditional inlining
24
28
val hasPerfectRHS = mutable.HashMap [Symbol , Boolean ]()
29
+
25
30
// If all values have perfect RHS than key has perfect RHS
26
31
val checkGood = mutable.HashMap [Symbol , Set [Symbol ]]()
32
+
27
33
val forwarderWritesTo = mutable.HashMap [Symbol , Symbol ]()
34
+
28
35
val gettersCalled = mutable.HashSet [Symbol ]()
29
36
37
+ def symbolAccessors (s : Symbol )(implicit ctx : Context ): List [Symbol ] = {
38
+ val accessors = s.info.classSymbol.caseAccessors.filter(_.isGetter)
39
+ if (accessors.isEmpty)
40
+ s.info.classSymbol.caseAccessors
41
+ else accessors
42
+ }
43
+
30
44
def followTailPerfect (t : Tree , symbol : Symbol )(implicit ctx : Context ): Unit = {
31
45
t match {
32
- case Block (_, expr) => followTailPerfect(expr, symbol)
33
- case If (_, thenp, elsep) => followTailPerfect(thenp, symbol); followTailPerfect(elsep, symbol);
46
+ case Block (_, expr) =>
47
+ followTailPerfect(expr, symbol)
48
+
49
+ case If (_, thenp, elsep) =>
50
+ followTailPerfect(thenp, symbol)
51
+ followTailPerfect(elsep, symbol)
52
+
34
53
case Apply (fun, _) if fun.symbol.isConstructor && t.tpe.widenDealias == symbol.info.widenDealias.finalResultType.widenDealias =>
35
54
hasPerfectRHS(symbol) = true
55
+
36
56
case Apply (fun, _) if fun.symbol.is(Label ) && (fun.symbol ne symbol) =>
37
57
checkGood.put(symbol, checkGood.getOrElse(symbol, Set .empty) + fun.symbol)
38
58
// assert(forwarderWritesTo.getOrElse(t.symbol, symbol) == symbol)
39
59
forwarderWritesTo(t.symbol) = symbol
60
+
40
61
case t : Ident if ! t.symbol.owner.isClass && (t.symbol ne symbol) =>
41
62
checkGood.put(symbol, checkGood.getOrElse(symbol, Set .empty) + t.symbol)
63
+
42
64
case _ =>
43
65
}
44
66
}
45
67
46
68
def visitor (implicit ctx : Context ): Tree => Unit = {
47
- case vdef : ValDef if (vdef .symbol.info.classSymbol is CaseClass ) &&
48
- ! vdef .symbol.is(Lazy ) &&
49
- ! vdef .symbol.info.classSymbol.caseAccessors.exists(x => x .is(Mutable )) =>
50
- followTailPerfect(vdef .rhs, vdef .symbol)
69
+ case t : ValDef if (t .symbol.info.classSymbol is CaseClass ) &&
70
+ ! t .symbol.is(Lazy ) &&
71
+ ! t .symbol.info.classSymbol.caseAccessors.exists(_ .is(Mutable )) =>
72
+ followTailPerfect(t .rhs, t .symbol)
51
73
52
74
case Assign (lhs, rhs) if ! lhs.symbol.owner.isClass =>
53
75
checkGood.put(lhs.symbol, checkGood.getOrElse(lhs.symbol, Set .empty) + rhs.symbol)
54
76
55
- case t @ Select (qual, _) if (t.symbol.isGetter && ! t.symbol.is(Mutable )) ||
77
+ case t @ Select (qual, _) if
78
+ (t.symbol.isGetter && ! t.symbol.is(Mutable | Lazy )) ||
56
79
(t.symbol.maybeOwner.derivesFrom(defn.ProductClass ) && t.symbol.maybeOwner.is(CaseClass ) && t.symbol.name.isSelectorName) ||
57
80
(t.symbol.is(CaseAccessor ) && ! t.symbol.is(Mutable )) =>
58
81
gettersCalled(qual.symbol) = true
@@ -65,9 +88,9 @@ class InlineLocalObjects extends Optimisation {
65
88
66
89
def transformer (implicit ctx : Context ): Tree => Tree = {
67
90
var hasChanged = true
68
- while (hasChanged) {
91
+ while (hasChanged) {
69
92
hasChanged = false
70
- checkGood.foreach{ case (key, values) =>
93
+ checkGood.foreach { case (key, values) =>
71
94
values.foreach { value =>
72
95
if (hasPerfectRHS.getOrElse(key, false )) {
73
96
hasChanged = ! hasPerfectRHS.put(value, true ).getOrElse(false )
@@ -77,54 +100,70 @@ class InlineLocalObjects extends Optimisation {
77
100
}
78
101
79
102
val newMappings : Map [Symbol , Map [Symbol , Symbol ]] =
80
- hasPerfectRHS.iterator.map(x => x._1).filter(x => ! x.is(Method ) && ! x.is(Label ) && gettersCalled.contains(x.symbol) && (x.symbol.info.classSymbol is CaseClass ))
81
- .map { refVal =>
82
- simplify.println(s " replacing ${refVal.symbol.fullName} with stack-allocated fields " )
83
- var accessors = refVal.info.classSymbol.caseAccessors.filter(_.isGetter) // TODO: drop mutable ones
84
- if (accessors.isEmpty) accessors = refVal.info.classSymbol.caseAccessors
85
- val productAccessors = (1 to accessors.length).map(i => refVal.info.member(nme.productAccessorName(i)).symbol) // TODO: disambiguate
86
- val newLocals = accessors.map(x =>
87
- // TODO: it would be nice to have an additional optimisation that
88
- // TODO: is capable of turning those mutable ones into immutable in common cases
89
- ctx.newSymbol(ctx.owner.enclosingMethod, (refVal.name + " $" + x.name).toTermName, Synthetic | Mutable , x.asSeenFrom(refVal.info).info.finalResultType.widenDealias)
90
- )
91
- val fieldMapping = accessors zip newLocals
92
- val productMappings = productAccessors zip newLocals
93
- (refVal, (fieldMapping ++ productMappings).toMap)
94
- }.toMap
103
+ hasPerfectRHS.iterator
104
+ .map(_._1)
105
+ .filter(x => ! x.is(Method | Label ) && gettersCalled.contains(x.symbol) && x.symbol.info.classSymbol.is(CaseClass ))
106
+ .map { refVal =>
107
+ simplify.println(s " replacing ${refVal.symbol.fullName} with stack-allocated fields " )
108
+ var accessors = refVal.info.classSymbol.caseAccessors.filter(_.isGetter) // TODO: drop mutable ones
109
+ if (accessors.isEmpty) accessors = refVal.info.classSymbol.caseAccessors
110
+
111
+ val productAccessors = (1 to accessors.length).map { i =>
112
+ refVal.info.member(nme.productAccessorName(i)).symbol
113
+ } // TODO: disambiguate
114
+
115
+ val newLocals = accessors.map { x =>
116
+ // TODO: it would be nice to have an additional optimisation that
117
+ // TODO: is capable of turning those mutable ones into immutable in common cases
118
+ val owner : Symbol = ctx.owner.enclosingMethod
119
+ val name : Name = (refVal.name + " $" + x.name).toTermName
120
+ val flags : FlagSet = Synthetic | Mutable
121
+ val info : Type = x.asSeenFrom(refVal.info).info.finalResultType.widenDealias
122
+ ctx.newSymbol(owner, name, flags, info)
123
+ }
124
+ val fieldMapping = accessors zip newLocals
125
+ val productMappings = productAccessors zip newLocals
126
+ (refVal, (fieldMapping ++ productMappings).toMap)
127
+ }.toMap
128
+
95
129
val toSplit : mutable.Set [Symbol ] = mutable.Set .empty ++ newMappings.keySet
96
130
97
131
def splitWrites (t : Tree , target : Symbol ): Tree = {
98
132
t match {
99
- case tree@ Block (stats, expr) => cpy.Block (tree)(stats, splitWrites(expr, target))
100
- case tree@ If (_, thenp, elsep) => cpy.If (tree)(thenp = splitWrites(thenp, target), elsep = splitWrites(elsep, target))
101
- case Apply (sel , args) if sel.symbol.isConstructor && t.tpe.widenDealias == target.info.widenDealias.finalResultType.widenDealias =>
133
+ case tree @ Block (stats, expr) =>
134
+ cpy.Block (tree)(stats, splitWrites(expr, target))
135
+
136
+ case tree @ If (_, thenp, elsep) =>
137
+ cpy.If (tree)(thenp = splitWrites(thenp, target), elsep = splitWrites(elsep, target))
138
+
139
+ case Apply (sel, args) if sel.symbol.isConstructor && t.tpe.widenDealias == target.info.widenDealias.finalResultType.widenDealias =>
102
140
val fieldsByAccessors = newMappings(target)
103
- var accessors = target.info.classSymbol.caseAccessors.filter(_.isGetter) // TODO: when is this filter needed?
104
- if (accessors.isEmpty) accessors = target.info.classSymbol.caseAccessors
105
- val assigns = (accessors zip args) map (x => ref(fieldsByAccessors(x._1)).becomes(x._2))
141
+ var accessors = symbolAccessors(target)
142
+ val assigns = (accessors zip args).map(x => ref(fieldsByAccessors(x._1)).becomes(x._2))
106
143
val recreate = sel.appliedToArgs(accessors.map(x => ref(fieldsByAccessors(x))))
107
144
Block (assigns, recreate)
145
+
108
146
case Apply (fun, _) if fun.symbol.is(Label ) =>
109
147
t // Do nothing. It will do on its own.
148
+
110
149
case t : Ident if ! t.symbol.owner.isClass && newMappings.contains(t.symbol) && t.symbol.info.classSymbol == target.info.classSymbol =>
111
150
val fieldsByAccessorslhs = newMappings(target)
112
151
val fieldsByAccessorsrhs = newMappings(t.symbol)
113
- val accessors = target.info.classSymbol.caseAccessors.filter(_.isGetter )
114
- val assigns = accessors map (x => ref(fieldsByAccessorslhs(x)).becomes(ref(fieldsByAccessorsrhs(x))))
152
+ val accessors = symbolAccessors(target )
153
+ val assigns = accessors. map(x => ref(fieldsByAccessorslhs(x)).becomes(ref(fieldsByAccessorsrhs(x))))
115
154
Block (assigns, t)
116
- // If `t` is itself split, push writes.
155
+ // If `t` is itself split, push writes.
156
+
117
157
case _ =>
118
158
evalOnce(t){ev =>
119
159
if (ev.tpe.derivesFrom(defn.NothingClass )) ev
120
160
else {
121
161
val fieldsByAccessors = newMappings(target)
122
- val accessors = target.info.classSymbol.caseAccessors.filter(_.isGetter )
123
- val assigns = accessors map (x => ref(fieldsByAccessors(x)).becomes(ev.select(x)))
162
+ val accessors = symbolAccessors(target )
163
+ val assigns = accessors. map(x => ref(fieldsByAccessors(x)).becomes(ev.select(x)))
124
164
Block (assigns, ev)
125
165
}
126
166
} // Need to eval-once and update fields.
127
-
128
167
}
129
168
}
130
169
@@ -142,33 +181,37 @@ class InlineLocalObjects extends Optimisation {
142
181
gettersCalled.clear()
143
182
144
183
val res : Tree => Tree = {
145
- case ddef : DefDef if ddef .symbol.is(Label ) =>
146
- newMappings.get(followCases(ddef .symbol)) match {
184
+ case t : DefDef if t .symbol.is(Label ) =>
185
+ newMappings.get(followCases(t .symbol)) match {
147
186
case Some (mappings) =>
148
- cpy.DefDef (ddef )(rhs = splitWrites(ddef .rhs, followCases(ddef .symbol)))
149
- case _ => ddef
187
+ cpy.DefDef (t )(rhs = splitWrites(t .rhs, followCases(t .symbol)))
188
+ case _ => t
150
189
}
151
- case a : ValDef if toSplit.contains(a.symbol) =>
152
- toSplit -= a.symbol
190
+
191
+ case t : ValDef if toSplit.contains(t.symbol) =>
192
+ toSplit -= t.symbol
153
193
// Break ValDef apart into fields + boxed value
154
- val newFields = newMappings(a .symbol).values.toSet
194
+ val newFields = newMappings(t .symbol).values.toSet
155
195
Thicket (
156
196
newFields.map(x => ValDef (x.asTerm, defaultValue(x.symbol.info.widenDealias))).toList :::
157
- List (cpy.ValDef (a)(rhs = splitWrites(a.rhs, a.symbol))))
158
- case ass : Assign =>
159
- newMappings.get(ass.lhs.symbol) match {
160
- case None => ass
197
+ List (cpy.ValDef (t)(rhs = splitWrites(t.rhs, t.symbol))))
198
+
199
+ case t : Assign =>
200
+ newMappings.get(t.lhs.symbol) match {
201
+ case None => t
161
202
case Some (mapping) =>
162
- val updates = mapping.filter(x => x._1.is(CaseAccessor )).map(x => ref(x._2).becomes(ref(ass .lhs.symbol).select(x._1))).toList
163
- Thicket (ass :: updates)
203
+ val updates = mapping.filter(x => x._1.is(CaseAccessor )).map(x => ref(x._2).becomes(ref(t .lhs.symbol).select(x._1))).toList
204
+ Thicket (t :: updates)
164
205
}
165
- case t @ Select (rec, _) if (t.symbol.isGetter && ! t.symbol.is(Mutable )) ||
206
+
207
+ case t @ Select (rec, _) if (t.symbol.isGetter && ! t.symbol.is(Mutable | Lazy )) ||
166
208
(t.symbol.maybeOwner.derivesFrom(defn.ProductClass ) && t.symbol.owner.is(CaseClass ) && t.symbol.name.isSelectorName) ||
167
209
(t.symbol.is(CaseAccessor ) && ! t.symbol.is(Mutable )) =>
168
210
newMappings.getOrElse(rec.symbol, Map .empty).get(t.symbol) match {
169
211
case None => t
170
212
case Some (newSym) => ref(newSym)
171
213
}
214
+
172
215
case t => t
173
216
}
174
217
res
0 commit comments