@@ -28,7 +28,7 @@ object DropBreaks:
28
28
var otherRefs : Int = 0
29
29
30
30
private val LabelUsages = new Property .Key [Map [Symbol , LabelUsage ]]
31
- private val LabelsShadowedByTry = new Property .Key [Set [Symbol ]]
31
+ private val ShadowedLabels = new Property .Key [Set [Symbol ]]
32
32
33
33
/** Rewrites local Break throws to labeled returns.
34
34
* Drops `try` statements on breaks if no other uses of its label remain.
@@ -38,7 +38,7 @@ object DropBreaks:
38
38
* - the throw and the boundary are in the same method, and
39
39
* - there is no try expression inside the boundary that encloses the throw.
40
40
*/
41
- class DropBreaks extends MiniPhase :
41
+ class DropBreaks extends MiniPhase , RecordStackChange :
42
42
import DropBreaks .*
43
43
44
44
import tpd ._
@@ -99,6 +99,31 @@ class DropBreaks extends MiniPhase:
99
99
None
100
100
end BreakBoundary
101
101
102
+ private object Break :
103
+
104
+ private def isBreak (sym : Symbol )(using Context ): Boolean =
105
+ sym.name == nme.apply && sym.owner == defn.breakModule.moduleClass
106
+
107
+ /** `(local, arg)` provided `tree` matches
108
+ *
109
+ * break[...](arg)(local)
110
+ *
111
+ * or `(local, ())` provided `tree` matches
112
+ *
113
+ * break()(local)
114
+ */
115
+ def unapply (tree : Tree )(using Context ): Option [(Symbol , Tree )] = tree match
116
+ case Apply (Apply (fn, args), id :: Nil )
117
+ if isBreak(fn.symbol) =>
118
+ stripInlined(id) match
119
+ case id : Ident =>
120
+ val arg = (args : @ unchecked) match
121
+ case arg :: Nil => arg
122
+ case Nil => Literal (Constant (())).withSpan(tree.span)
123
+ Some ((id.symbol, arg))
124
+ case _ => None
125
+ case _ => None
126
+
102
127
/** The LabelUsage data associated with `lbl` in the current context */
103
128
private def labelUsage (lbl : Symbol )(using Context ): Option [LabelUsage ] =
104
129
for
@@ -117,18 +142,34 @@ class DropBreaks extends MiniPhase:
117
142
case _ =>
118
143
ctx
119
144
120
- /** If `tree` is not a LabeledTry, include all enclosing labels in the
121
- * `LabelsShadowedByTry` context property. This means that breaks to these
122
- * labels will not be translated to labeled returns in the body of the try .
145
+ /** Include all enclosing labels in the `ShadowedLabels` context property.
146
+ * This means that breaks to these labels will not be translated to labeled
147
+ * returns while this context is valid .
123
148
*/
124
- override def prepareForTry (tree : Try )(using Context ): Context = tree match
125
- case LabelTry (_, _) => ctx
126
- case _ => ctx.property(LabelUsages ) match
149
+ private def shadowLabels (using Context ): Context =
150
+ ctx.property(LabelUsages ) match
127
151
case Some (usesMap) =>
128
- val setSoFar = ctx.property(LabelsShadowedByTry ).getOrElse(Set .empty)
129
- ctx.fresh.setProperty(LabelsShadowedByTry , setSoFar ++ usesMap.keysIterator)
152
+ val setSoFar = ctx.property(ShadowedLabels ).getOrElse(Set .empty)
153
+ ctx.fresh.setProperty(ShadowedLabels , setSoFar ++ usesMap.keysIterator)
130
154
case _ => ctx
131
155
156
+ /** Need to suppress labeled returns if the stack can change between
157
+ * source and target of the jump
158
+ */
159
+ protected def stackChange (using Context ) = shadowLabels
160
+
161
+ /** Need to suppress labeled returns if there is an intervening try
162
+ */
163
+ override def prepareForTry (tree : Try )(using Context ): Context = tree match
164
+ case LabelTry (_, _) => ctx
165
+ case _ => shadowLabels
166
+
167
+ override def prepareForApply (tree : Apply )(using Context ): Context = tree match
168
+ case Break (_, _) => ctx
169
+ case _ => stackChange
170
+
171
+ // other stack changing operations are handled in RecordStackChange
172
+
132
173
/** If `tree` is a BreakBoundary, transform it as follows:
133
174
* - Wrap it in a labeled block if its label has local uses
134
175
* - Drop the try/catch if its label has no other uses
@@ -147,29 +188,9 @@ class DropBreaks extends MiniPhase:
147
188
case _ =>
148
189
tree
149
190
150
- private def isBreak (sym : Symbol )(using Context ): Boolean =
151
- sym.name == nme.apply && sym.owner == defn.breakModule.moduleClass
152
-
153
- private def transformBreak (tree : Tree , arg : Tree , lbl : Symbol )(using Context ): Tree =
154
- report.log(i " transform break $tree/ $arg/ $lbl" )
155
- labelUsage(lbl) match
156
- case Some (uses : LabelUsage )
157
- if uses.enclMeth == ctx.owner.enclosingMethod
158
- && ! ctx.property(LabelsShadowedByTry ).getOrElse(Set .empty).contains(lbl)
159
- =>
160
- uses.otherRefs -= 1
161
- uses.returnRefs += 1
162
- Return (arg, ref(uses.goto)).withSpan(arg.span)
163
- case _ =>
164
- tree
165
-
166
-
167
- /** Rewrite a break call
168
- *
169
- * break.apply[...](value)(using lbl)
170
- *
191
+ /** Rewrite a break with argument `arg` and label `lbl`
171
192
* where `lbl` is a label defined in the current method and is not included in
172
- * LabelsShadowedByTry to
193
+ * ShadowedLabels to
173
194
*
174
195
* return[target] arg
175
196
*
@@ -179,22 +200,29 @@ class DropBreaks extends MiniPhase:
179
200
* binding containing `local` is dropped.
180
201
*/
181
202
override def transformApply (tree : Apply )(using Context ): Tree = tree match
182
- case Apply (Apply (fn, args), (id : Ident ) :: Nil ) if isBreak(fn.symbol) =>
183
- val arg = (args : @ unchecked) match
184
- case arg :: Nil => arg
185
- case Nil => Literal (Constant (())).withSpan(tree.span)
186
- transformBreak(tree, arg, id.symbol)
187
- case _ =>
188
- tree
203
+ case Break (lbl, arg) =>
204
+ labelUsage(lbl) match
205
+ case Some (uses : LabelUsage )
206
+ if uses.enclMeth == ctx.owner.enclosingMethod
207
+ && ! ctx.property(ShadowedLabels ).getOrElse(Set .empty).contains(lbl)
208
+ =>
209
+ uses.otherRefs -= 1
210
+ uses.returnRefs += 1
211
+ Return (arg, ref(uses.goto)).withSpan(arg.span)
212
+ case _ => tree
213
+ case _ => tree
189
214
190
215
/** If `tree` refers to an enclosing label, increase its non local recount.
191
216
* This increase is corrected in `transformInlined` if the reference turns
192
217
* out to be part of a BreakThrow to a local, non-shadowed label.
193
218
*/
194
219
override def transformIdent (tree : Ident )(using Context ): Tree =
195
- if tree.symbol.name == nme.local then
196
- for uses <- labelUsage(tree.symbol) do
197
- uses.otherRefs += 1
220
+ for uses <- labelUsage(tree.symbol) do
221
+ uses.otherRefs += 1
198
222
tree
199
223
224
+ // override def transformReturn(tree: Return)(using Context): Tree =
225
+ // if !tree.from.isEmpty && tree.expr.tpe.isExactlyNothing then tree.expr
226
+ // else tree
227
+
200
228
end DropBreaks
0 commit comments