Skip to content

Commit 315e47f

Browse files
committed
Add capture checking to util.boundary
1 parent 2b4d157 commit 315e47f

File tree

1 file changed

+15
-4
lines changed

1 file changed

+15
-4
lines changed

library/src/scala/util/boundary.scala

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
package scala.util
2+
3+
import language.experimental.captureChecking
24
import scala.annotation.implicitNotFound
35

46
/** A boundary that can be exited by `break` calls.
@@ -27,18 +29,27 @@ import scala.annotation.implicitNotFound
2729
* ```
2830
*/
2931
object boundary:
32+
import caps.unsafe.unsafeAssumePure
3033

3134
/** User code should call `break.apply` instead of throwing this exception
3235
* directly.
3336
*/
34-
final class Break[T] private[boundary](val label: Label[T], val value: T)
37+
final class Break[T] private[boundary] (private[boundary] val label: Label[T]^{}, val value: T)
3538
extends RuntimeException(
36-
/*message*/ null, /*cause*/ null, /*enableSuppression=*/ false, /*writableStackTrace*/ false)
39+
/*message*/ null, /*cause*/ null, /*enableSuppression=*/ false, /*writableStackTrace*/ false):
40+
41+
/** Compare the given [[Label]] to the one this [[Break]] was constructed with. */
42+
inline def isSameLabelAs(other: Label[T]) = label eq other
43+
44+
object Break:
45+
def apply[T](label: Label[T], value: T) =
46+
// SAFETY: labels cannot leak from [[Break]], and is only used for equality comparison.
47+
new Break(label.unsafeAssumePure, value)
3748

3849
/** Labels are targets indicating which boundary will be exited by a `break`.
3950
*/
4051
@implicitNotFound("explain=A Label is generated from an enclosing `scala.util.boundary` call.\nMaybe that boundary is missing?")
41-
final class Label[-T]
52+
final class Label[-T] extends caps.Capability
4253

4354
/** Abort current computation and instead return `value` as the value of
4455
* the enclosing `boundary` call that created `label`.
@@ -60,7 +71,7 @@ object boundary:
6071
val local = Label[T]()
6172
try body(using local)
6273
catch case ex: Break[T] @unchecked =>
63-
if ex.label eq local then ex.value
74+
if ex.isSameLabelAs(local) then ex.value
6475
else throw ex
6576

6677
end boundary

0 commit comments

Comments
 (0)