Skip to content

Commit 337f9b5

Browse files
committed
Make break non-inlined functions
`DropBreaks` now detects the calls to `break` instead of their inline expansion. Also: Make `Break`'s constructor private.
1 parent dcb2e62 commit 337f9b5

File tree

4 files changed

+33
-45
lines changed

4 files changed

+33
-45
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ class Definitions {
970970

971971
@tu lazy val LabelClass: Symbol = requiredClass("scala.util.boundary.Label")
972972
@tu lazy val BreakClass: Symbol = requiredClass("scala.util.boundary.Break")
973+
@tu lazy val breakModule: Symbol = requiredModule("scala.util.break")
973974

974975
@tu lazy val CapsModule: Symbol = requiredModule("scala.caps")
975976
@tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("*")

compiler/src/dotty/tools/dotc/transform/DropBreaks.scala

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import MegaPhase._
1010
import Types._, Contexts._, Flags._, DenotTransformers._
1111
import Symbols._, StdNames._, Trees._
1212
import util.Property
13+
import Constants.Constant
1314
import Flags.MethodOrLazy
1415

1516
object DropBreaks:
@@ -98,26 +99,6 @@ class DropBreaks extends MiniPhase:
9899
None
99100
end BreakBoundary
100101

101-
private object BreakThrow:
102-
103-
/** `(local, arg)` provided `tree` matches inlined
104-
*
105-
* val Label_this: ... = local
106-
* throw new Break[...](Label_this, arg)
107-
*/
108-
def unapply(tree: Tree)(using Context): Option[(Symbol, Tree)] = tree match
109-
case Inlined(_,
110-
(vd @ ValDef(label_this1, _, id: Ident)):: Nil,
111-
Apply(throww, Apply(constr, Inlined(_, _, Ident(label_this2)) :: arg :: Nil) :: Nil))
112-
if throww.symbol == defn.throwMethod
113-
&& label_this1 == nme.Label_this && label_this2 == nme.Label_this
114-
&& id.symbol.name == nme.local
115-
&& constr.symbol.isClassConstructor && constr.symbol.owner == defn.BreakClass =>
116-
Some((id.symbol, arg))
117-
case _ =>
118-
None
119-
end BreakThrow
120-
121102
/** The LabelUsage data associated with `lbl` in the current context */
122103
private def labelUsage(lbl: Symbol)(using Context): Option[LabelUsage] =
123104
for
@@ -166,13 +147,29 @@ class DropBreaks extends MiniPhase:
166147
case _ =>
167148
tree
168149

169-
/** Rewrite a BreakThrow
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
170168
*
171-
* val Label_this: ... = local
172-
* throw new Break[...](Label_this, arg)
169+
* break.apply[...](value)(using lbl)
173170
*
174-
* where `local` is defined in the current method and is not included in
175-
* LabeldShowedByTry to
171+
* where `lbl` is a label defined in the current method and is not included in
172+
* LabelsShadowedByTry to
176173
*
177174
* return[target] arg
178175
*
@@ -181,22 +178,12 @@ class DropBreaks extends MiniPhase:
181178
* and the non-local refcount is decreased, since `local` the `Label_this`
182179
* binding containing `local` is dropped.
183180
*/
184-
override def transformInlined(tree: Inlined)(using Context): Tree = tree match
185-
case BreakThrow(lbl, arg) =>
186-
report.log(i"trans inlined $arg, ${arg.source}, ${ctx.outer.source}, ${tree.source}")
187-
labelUsage(lbl) match
188-
case Some(uses: LabelUsage)
189-
if uses.enclMeth == ctx.owner.enclosingMethod
190-
&& !ctx.property(LabelsShadowedByTry).getOrElse(Set.empty).contains(lbl)
191-
=>
192-
uses.otherRefs -= 1
193-
uses.returnRefs += 1
194-
cpy.Inlined(tree)(tree.call, Nil,
195-
inContext(ctx.withSource(tree.expansion.source)) {
196-
Return(arg, ref(uses.goto)).withSpan(arg.span)
197-
})
198-
case _ =>
199-
tree
181+
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)
200187
case _ =>
201188
tree
202189

library/src/scala/util/boundary.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ object boundary:
1919
/** User code should call `break.apply` instead of throwing this exception
2020
* directly.
2121
*/
22-
class Break[T](val label: Label[T], val value: T) extends ControlException
22+
class Break[T] private[boundary](val label: Label[T], val value: T) extends ControlException
2323

2424
/** Labels are targets indicating which boundary will be exited by a `break`.
2525
*/
2626
class Label[T]:
27-
transparent inline def break(value: T): Nothing = throw Break(this, value)
27+
def break(value: T): Nothing = throw Break(this, value)
2828

2929
/** Run `body` with freshly generated label as implicit argument. Catch any
3030
* breaks associated with that label and return their results instead of

library/src/scala/util/break.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ object break:
88
/** Abort current computation and instead return `value` as the value of
99
* the enclosing `boundary` call that created `label`.
1010
*/
11-
transparent inline def apply[T](value: T)(using l: boundary.Label[T]): Nothing =
11+
def apply[T](value: T)(using l: boundary.Label[T]): Nothing =
1212
l.break(value)
1313

1414
/** Abort current computation and instead continue after the `boundary` call that
1515
* created `label`.
1616
*/
17-
transparent inline def apply()(using l: boundary.Label[Unit]): Nothing =
17+
def apply()(using l: boundary.Label[Unit]): Nothing =
1818
apply(())
1919

2020
end break

0 commit comments

Comments
 (0)