@@ -10,7 +10,7 @@ import core.Flags._
10
10
import ast .Trees ._
11
11
import Simplify .desugarIdent
12
12
13
- /** Removes side effect free statements in blocks.
13
+ /** Removes side effect free statements in blocks and Defdef .
14
14
* Note: BoxedUnit currently messes up this phase when run after erasure
15
15
*/
16
16
class DropNoEffects (val simplifyPhase : Simplify )(implicit val ctx : Context ) extends Optimisation {
@@ -19,120 +19,137 @@ class DropNoEffects(val simplifyPhase: Simplify)(implicit val ctx: Context) exte
19
19
val visitor = NoVisitor
20
20
21
21
def transformer (localCtx : Context ): Tree => Tree = {
22
+ // Remove empty blocks
22
23
case Block (Nil , expr) => expr
24
+
25
+ // Keep only side effect free statements in blocks
23
26
case a : Block =>
24
27
val newStats0 = a.stats.mapConserve(keepOnlySideEffects)
28
+
29
+ // Flatten nested blocks
25
30
val newStats1 = if (newStats0 eq a.stats) newStats0 else newStats0.flatMap {
26
- case x : Block => x.stats ::: List (x.expr)
31
+ case x : Block => x.stats ::: List (x.expr)
27
32
case EmptyTree => Nil
28
33
case t => t :: Nil
29
34
}
30
35
val (newStats2, newExpr) = a.expr match {
31
36
case Block (stats2, expr) => (newStats1 ++ stats2, expr)
32
37
case _ => (newStats1, a.expr)
33
38
}
39
+
34
40
if (newStats2.nonEmpty)
35
41
cpy.Block (a)(stats = newStats2, newExpr)
36
42
else newExpr
37
- case a : DefDef =>
38
- if (a.symbol.info.finalResultType.derivesFrom(defn.UnitClass ) &&
39
- ! a.rhs.tpe.derivesFrom(defn.UnitClass ) &&
40
- ! a.rhs.tpe.derivesFrom(defn.NothingClass )) {
41
- def insertUnit (t : Tree ) = {
42
- if (! t.tpe.derivesFrom(defn.UnitClass )) Block (t :: Nil , unitLiteral)
43
- else t
44
- }
45
- cpy.DefDef (a)(rhs = insertUnit(keepOnlySideEffects(a.rhs)), tpt = TypeTree (defn.UnitType ))
46
- } else a
43
+
44
+ // Keep only side effect free statements unit returning functions
45
+ case a : DefDef if (a.symbol.info.finalResultType.derivesFrom(defn.UnitClass ) &&
46
+ ! a.rhs.tpe.derivesFrom(defn.UnitClass ) &&
47
+ ! a.rhs.tpe.derivesFrom(defn.NothingClass )) =>
48
+ def insertUnit (t : Tree ) = {
49
+ if (! t.tpe.derivesFrom(defn.UnitClass )) Block (t :: Nil , unitLiteral)
50
+ else t
51
+ }
52
+ cpy.DefDef (a)(rhs = insertUnit(keepOnlySideEffects(a.rhs)), tpt = TypeTree (defn.UnitType ))
53
+
47
54
case t => t
48
55
}
49
56
50
- def keepOnlySideEffects (t : Tree ): Tree = {
51
- t match {
52
- case l : Literal =>
53
- EmptyTree
54
- case t : This =>
55
- EmptyTree
56
- case Typed (exp, tpe) =>
57
- keepOnlySideEffects(exp)
58
- case t @ If (cond, thenp, elsep) =>
59
- val nthenp = keepOnlySideEffects(thenp)
60
- val nelsep = keepOnlySideEffects(elsep)
61
- if (thenp.isEmpty && elsep.isEmpty) keepOnlySideEffects(cond)
62
- else cpy.If (t)(
63
- thenp = nthenp.orElse(if (thenp.isInstanceOf [Literal ]) thenp else unitLiteral),
64
- elsep = nelsep.orElse(if (elsep.isInstanceOf [Literal ]) elsep else unitLiteral))
65
- case Select (rec, _) if
66
- (t.symbol.isGetter && ! t.symbol.is(Mutable | Lazy )) ||
67
- (t.symbol.owner.derivesFrom(defn.ProductClass ) && t.symbol.owner.is(CaseClass ) && t.symbol.name.isSelectorName) ||
68
- (t.symbol.is(CaseAccessor ) && ! t.symbol.is(Mutable )) =>
69
- keepOnlySideEffects(rec) // Accessing a field of a product
70
- case s @ Select (qual, name) if
71
- // !name.eq(nme.TYPE_) && // Keep the .TYPE added by ClassOf, would be needed for AfterErasure
72
- ! t.symbol.is(Mutable | Lazy ) && ! t.symbol.is(Method ) =>
73
- keepOnlySideEffects(qual)
74
- case Block (List (t : DefDef ), s : Closure ) =>
75
- EmptyTree
76
- case bl @ Block (stats, expr) =>
77
- val stats1 = stats.mapConserve(keepOnlySideEffects)
78
- val stats2 = if (stats1 ne stats) stats1.filter(x=> x ne EmptyTree ) else stats1
79
- val expr2 : Tree = expr match {
80
- case t : Literal if t.tpe.derivesFrom(defn.UnitClass ) => expr
81
- case _ => keepOnlySideEffects(expr).orElse(unitLiteral)
82
- }
83
- cpy.Block (bl)(stats2, expr2)
84
- case t : Ident if ! t.symbol.is(Method | Lazy ) && ! t.symbol.info.isInstanceOf [ExprType ] || effectsDontEscape(t) =>
85
- desugarIdent(t) match {
86
- case Some (t) if ! (t.qualifier.symbol.is(JavaDefined ) && t.qualifier.symbol.is(Package )) => t
87
- case _ => EmptyTree
88
- }
89
- case app : Apply if app.fun.symbol.is(Label ) && ! app.tpe.finalResultType.derivesFrom(defn.UnitClass ) =>
90
- // This is "the scary hack". It changes the return type to Unit, then
91
- // invalidates the denotation cache. Because this optimisation only
92
- // operates locally, this should be fine.
93
- val denot = app.fun.symbol.denot
94
- if (! denot.info.finalResultType.derivesFrom(defn.UnitClass )) {
95
- val newLabelType = app.symbol.info match {
96
- case mt : MethodType =>
97
- mt.derivedLambdaType(mt.paramNames, mt.paramInfos, defn.UnitType )
98
- case et : ExprType =>
99
- et.derivedExprType(defn.UnitType )
100
- }
101
- val newD = app.symbol.asSymDenotation.copySymDenotation(info = newLabelType)
102
- newD.installAfter(simplifyPhase)
103
- }
57
+ val keepOnlySideEffects : Tree => Tree = {
58
+ case l : Literal =>
59
+ EmptyTree
104
60
105
- ref(app.symbol).appliedToArgs(app.args)
106
- case t @ Apply (fun, _) if effectsDontEscape(t) =>
107
- def getArgsss (a : Tree ): List [Tree ] = a match {
108
- case a : Apply => getArgsss(a.fun) ::: a.args
109
- case _ => Nil
110
- }
111
- def getSel (t : Tree ): Tree = {t match {
112
- case t : Apply => getSel(t.fun)
113
- case t : Select => t.qualifier
114
- case t : TypeApply => getSel(t.fun)
115
- case _ => t
116
- }}
117
- val args = getArgsss(t)
118
- val rec = getSel(t)
119
- val prefix = rec match {
120
- case t : New =>
121
- args.map(keepOnlySideEffects)
122
- case _ =>
123
- rec :: args.map(keepOnlySideEffects)
61
+ case t : This =>
62
+ EmptyTree
63
+
64
+ case Typed (exp, tpe) =>
65
+ keepOnlySideEffects(exp)
66
+
67
+ // If is pure, propagade the simplification
68
+ case t @ If (cond, thenp, elsep) =>
69
+ val nthenp = keepOnlySideEffects(thenp)
70
+ val nelsep = keepOnlySideEffects(elsep)
71
+ if (thenp.isEmpty && elsep.isEmpty) keepOnlySideEffects(cond)
72
+ else cpy.If (t)(
73
+ thenp = nthenp.orElse(if (thenp.isInstanceOf [Literal ]) thenp else unitLiteral),
74
+ elsep = nelsep.orElse(if (elsep.isInstanceOf [Literal ]) elsep else unitLiteral))
75
+
76
+ // Accessing a field of a product
77
+ case t @ Select (rec, _) if
78
+ (t.symbol.isGetter && ! t.symbol.is(Mutable | Lazy )) ||
79
+ (t.symbol.owner.derivesFrom(defn.ProductClass ) && t.symbol.owner.is(CaseClass ) && t.symbol.name.isSelectorName) ||
80
+ (t.symbol.is(CaseAccessor ) && ! t.symbol.is(Mutable )) =>
81
+ keepOnlySideEffects(rec)
82
+
83
+ case s @ Select (qual, name) if
84
+ // !name.eq(nme.TYPE_) && // Keep the .TYPE added by ClassOf, would be needed for AfterErasure
85
+ ! s.symbol.is(Mutable | Lazy ) && ! s.symbol.is(Method ) =>
86
+ keepOnlySideEffects(qual)
87
+
88
+ case Block (List (t : DefDef ), s : Closure ) =>
89
+ EmptyTree
90
+
91
+ case bl @ Block (stats, expr) =>
92
+ val stats1 = stats.mapConserve(keepOnlySideEffects)
93
+ val stats2 = if (stats1 ne stats) stats1.filter(_ ne EmptyTree ) else stats1
94
+ val expr2 : Tree = expr match {
95
+ case t : Literal if t.tpe.derivesFrom(defn.UnitClass ) => expr
96
+ case _ => keepOnlySideEffects(expr).orElse(unitLiteral)
97
+ }
98
+ cpy.Block (bl)(stats2, expr2)
99
+
100
+ case t : Ident if ! t.symbol.is(Method | Lazy ) && ! t.symbol.info.isInstanceOf [ExprType ] || effectsDontEscape(t) =>
101
+ desugarIdent(t) match {
102
+ case Some (t) if ! (t.qualifier.symbol.is(JavaDefined ) && t.qualifier.symbol.is(Package )) => t
103
+ case _ => EmptyTree
104
+ }
105
+
106
+ case app : Apply if app.fun.symbol.is(Label ) && ! app.tpe.finalResultType.derivesFrom(defn.UnitClass ) =>
107
+ // This is "the scary hack". It changes the return type to Unit, then
108
+ // invalidates the denotation cache. Because this optimisation only
109
+ // operates locally, this should be fine.
110
+ val denot = app.fun.symbol.denot
111
+ if (! denot.info.finalResultType.derivesFrom(defn.UnitClass )) {
112
+ val newLabelType = app.symbol.info match {
113
+ case mt : MethodType =>
114
+ mt.derivedLambdaType(mt.paramNames, mt.paramInfos, defn.UnitType )
115
+ case et : ExprType =>
116
+ et.derivedExprType(defn.UnitType )
124
117
}
125
- Block (prefix, unitLiteral)
118
+ val newD = app.symbol.asSymDenotation.copySymDenotation(info = newLabelType)
119
+ newD.installAfter(simplifyPhase)
120
+ }
126
121
127
- case t @ TypeApply (Select (rec, _), List (testType)) if t.symbol.eq(defn.Any_asInstanceOf ) && testType.tpe.widenDealias.typeSymbol.exists =>
128
- val receiverType = TypeErasure .erasure(rec.tpe)
129
- val erazedTestedType = TypeErasure .erasure(testType.tpe)
130
- if (receiverType.derivesFrom(erazedTestedType.typeSymbol))
131
- rec
132
- else t
122
+ ref(app.symbol).appliedToArgs(app.args)
133
123
134
- case _ => t
135
- }
124
+ case t @ Apply (fun, _) if effectsDontEscape(t) =>
125
+ def getArgsss (a : Tree ): List [Tree ] = a match {
126
+ case a : Apply => getArgsss(a.fun) ::: a.args
127
+ case _ => Nil
128
+ }
129
+ def getSel (t : Tree ): Tree = {t match {
130
+ case t : Apply => getSel(t.fun)
131
+ case t : Select => t.qualifier
132
+ case t : TypeApply => getSel(t.fun)
133
+ case _ => t
134
+ }}
135
+ val args = getArgsss(t)
136
+ val rec = getSel(t)
137
+ val prefix = rec match {
138
+ case t : New =>
139
+ args.map(keepOnlySideEffects)
140
+ case _ =>
141
+ rec :: args.map(keepOnlySideEffects)
142
+ }
143
+ Block (prefix, unitLiteral)
144
+
145
+ case t @ TypeApply (Select (rec, _), List (testType)) if t.symbol.eq(defn.Any_asInstanceOf ) && testType.tpe.widenDealias.typeSymbol.exists =>
146
+ val receiverType = TypeErasure .erasure(rec.tpe)
147
+ val erazedTestedType = TypeErasure .erasure(testType.tpe)
148
+ if (receiverType.derivesFrom(erazedTestedType.typeSymbol))
149
+ rec
150
+ else t
151
+
152
+ case t => t
136
153
}
137
154
138
155
val constructorWhiteList : Set [String ] = Set (
@@ -178,10 +195,11 @@ class DropNoEffects(val simplifyPhase: Simplify)(implicit val ctx: Context) exte
178
195
" scala.runtime.BoxesRunTime.unboxToFloat"
179
196
)
180
197
198
+ /** Does this tree has side effects? This is an approximation awaiting real purity analysis... */
181
199
def effectsDontEscape (t : Tree ): Boolean = t match {
182
- case Apply (fun, args ) if fun.symbol.isConstructor && constructorWhiteList.contains(fun.symbol.owner.fullName.toString) =>
200
+ case Apply (fun, _ ) if fun.symbol.isConstructor && constructorWhiteList.contains(fun.symbol.owner.fullName.toString) =>
183
201
true
184
- case Apply (fun, args ) if methodsWhiteList.contains(fun.symbol.fullName.toString) =>
202
+ case Apply (fun, _ ) if methodsWhiteList.contains(fun.symbol.fullName.toString) =>
185
203
true
186
204
case Ident (_) if t.symbol.is(Module ) && (t.symbol.is(Synthetic ) || moduleWhiteList.contains(t.symbol.fullName.toString)) =>
187
205
true
0 commit comments