@@ -28,7 +28,7 @@ object DropBreaks:
2828 var otherRefs : Int = 0
2929
3030 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 ]]
3232
3333/** Rewrites local Break throws to labeled returns.
3434 * Drops `try` statements on breaks if no other uses of its label remain.
@@ -38,7 +38,7 @@ object DropBreaks:
3838 * - the throw and the boundary are in the same method, and
3939 * - there is no try expression inside the boundary that encloses the throw.
4040 */
41- class DropBreaks extends MiniPhase :
41+ class DropBreaks extends MiniPhase , RecordStackChange :
4242 import DropBreaks .*
4343
4444 import tpd ._
@@ -99,6 +99,31 @@ class DropBreaks extends MiniPhase:
9999 None
100100 end BreakBoundary
101101
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+
102127 /** The LabelUsage data associated with `lbl` in the current context */
103128 private def labelUsage (lbl : Symbol )(using Context ): Option [LabelUsage ] =
104129 for
@@ -117,18 +142,34 @@ class DropBreaks extends MiniPhase:
117142 case _ =>
118143 ctx
119144
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 .
123148 */
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
127151 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)
130154 case _ => ctx
131155
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+
132173 /** If `tree` is a BreakBoundary, transform it as follows:
133174 * - Wrap it in a labeled block if its label has local uses
134175 * - Drop the try/catch if its label has no other uses
@@ -147,29 +188,9 @@ class DropBreaks extends MiniPhase:
147188 case _ =>
148189 tree
149190
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`
171192 * where `lbl` is a label defined in the current method and is not included in
172- * LabelsShadowedByTry to
193+ * ShadowedLabels to
173194 *
174195 * return[target] arg
175196 *
@@ -179,22 +200,29 @@ class DropBreaks extends MiniPhase:
179200 * binding containing `local` is dropped.
180201 */
181202 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
189214
190215 /** If `tree` refers to an enclosing label, increase its non local recount.
191216 * This increase is corrected in `transformInlined` if the reference turns
192217 * out to be part of a BreakThrow to a local, non-shadowed label.
193218 */
194219 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
198222 tree
199223
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+
200228end DropBreaks
0 commit comments