Skip to content

Commit cfe85d2

Browse files
committed
Add tests for capture checking, per lampepfl#67
1 parent 401bb6d commit cfe85d2

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import language.experimental.captureChecking
2+
3+
import gears.async.AsyncOperations.*
4+
import gears.async.default.given
5+
import gears.async.{Async, AsyncSupport, Future, uninterruptible}
6+
7+
import java.util.concurrent.CancellationException
8+
import scala.annotation.capability
9+
import scala.concurrent.duration.{Duration, DurationInt}
10+
import scala.util.Success
11+
import scala.util.boundary
12+
13+
type Result[+T, +E] = Either[E, T]
14+
object Result:
15+
@capability opaque type Label[-T, -E] = boundary.Label[Result[T, E]]
16+
// ^ doesn't work?
17+
18+
def apply[T, E](body: Label[T, E] ?=> T): Result[T, E] =
19+
boundary(Right(body))
20+
21+
extension [U, E](r: Result[U, E]^)(using Label[Nothing, E]^)
22+
def ok: U = r match
23+
case Left(value) => boundary.break(Left(value))
24+
case Right(value) => value
25+
26+
class CaptureCheckingBehavior extends munit.FunSuite:
27+
import Result.*
28+
29+
test("good") {
30+
// don't do this in real code! capturing Async.blocking's Async context across functions is hard to track
31+
Async.blocking: async ?=>
32+
def good1[T, E](frs: List[Future[Result[T, E]]^]): Future[Result[List[T], E]]^{async} =
33+
Future:
34+
Result:
35+
frs.map(_.await.ok)
36+
37+
def good2[T, E](rf: Result[Future[T]^, E]): Future[Result[T, E]]^{async} =
38+
Future:
39+
Result:
40+
rf.ok.await // OK, Future argument has type Result[T]
41+
42+
def useless4[T, E](fr: Future[Result[T, E]]^) =
43+
fr.await.map(Future(_))
44+
}
45+
46+
test("very bad") {
47+
Async.blocking: async ?=>
48+
def fail3[T, E](fr: Future[Result[T, E]]^) =
49+
Result: label ?=>
50+
Future: fut ?=>
51+
fr.await.ok // error, escaping label from Result
52+
53+
val fut = Future(Left(5))
54+
val res = fail3(fut)
55+
println(res.right.get.asInstanceOf[Future[Any]].awaitResult)
56+
}
57+
58+
// test("bad") {
59+
// Async.blocking: async ?=>
60+
// def fail3[T, E](fr: Future[Result[T, E]]^): Result[Future[T]^{async}, E] =
61+
// Result: label ?=>
62+
// Future: fut ?=>
63+
// fr.await.ok // error, escaping label from Result
64+
// }

0 commit comments

Comments
 (0)