1
1
package scala .util
2
+
3
+ import language .experimental .captureChecking
2
4
import scala .annotation .implicitNotFound
3
5
4
6
/** A boundary that can be exited by `break` calls.
@@ -27,30 +29,39 @@ import scala.annotation.implicitNotFound
27
29
* ```
28
30
*/
29
31
object boundary :
32
+ import caps .unsafe .unsafeAssumePure
30
33
31
34
/** User code should call `break.apply` instead of throwing this exception
32
35
* directly.
33
36
*/
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 )
35
38
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)
37
48
38
49
/** Labels are targets indicating which boundary will be exited by a `break`.
39
50
*/
40
51
@ implicitNotFound(" explain=A Label is generated from an enclosing `scala.util.boundary` call.\n Maybe that boundary is missing?" )
41
- final class Label [- T ]
52
+ final class Label [- T ] extends caps. Capability
42
53
43
54
/** Abort current computation and instead return `value` as the value of
44
55
* the enclosing `boundary` call that created `label`.
45
56
*/
46
57
def break [T ](value : T )(using label : Label [T ]): Nothing =
47
- throw Break (label, value)
58
+ throw Break (label.unsafeAssumePure , value)
48
59
49
60
/** Abort current computation and instead continue after the `boundary` call that
50
61
* created `label`.
51
62
*/
52
63
def break ()(using label : Label [Unit ]): Nothing =
53
- throw Break (label, ())
64
+ throw Break (label.unsafeAssumePure , ())
54
65
55
66
/** Run `body` with freshly generated label as implicit argument. Catch any
56
67
* breaks associated with that label and return their results instead of
@@ -60,7 +71,7 @@ object boundary:
60
71
val local = Label [T ]()
61
72
try body(using local)
62
73
catch case ex : Break [T ] @ unchecked =>
63
- if ex.label eq local then ex.value
74
+ if ex.isSameLabelAs( local) then ex.value
64
75
else throw ex
65
76
66
77
end boundary
0 commit comments