1
+ import scala .util .control .NonFatal
2
+ import scala .util .control .NonLocalReturns ._
3
+
1
4
object lib {
2
5
inline def (op : => T ) rescue[T ] (fallback : => T ) =
3
6
try op
4
7
catch {
5
- case _ : Throwable => fallback
8
+ case NonFatal (_) => fallback // ReturnThrowable is fatal error, thus ignored
6
9
}
7
10
8
11
inline def (op : => T ) rescue[T , E <: Throwable ] (fallback : PartialFunction [E , T ]) =
9
12
try op
10
13
catch {
14
+ // case ex: ReturnThrowable[_] => throw ex // bug #7041
11
15
case ex : E =>
12
- if (fallback.isDefinedAt(ex)) fallback(ex) else throw ex
16
+ // user should never match `ReturnThrowable`, which breaks semantics of non-local return
17
+ if (fallback.isDefinedAt(ex) && ! ex.isInstanceOf [ReturnThrowable [_]]) fallback(ex) else throw ex
13
18
}
14
19
}
15
20
@@ -30,6 +35,8 @@ import lib._
30
35
} == 3
31
36
)
32
37
38
+ (9 / 0 ) rescue { case ex : ArithmeticException => 4 }
39
+
33
40
assert(
34
41
{
35
42
{
@@ -42,4 +49,30 @@ import lib._
42
49
}
43
50
} == 3
44
51
)
52
+
53
+ assert(foo(10 ) == 40 )
54
+ assert(bar(10 ) == 40 )
55
+
56
+ // should not catch fatal errors
57
+ assert(
58
+ try { { throw new OutOfMemoryError (); true } rescue false }
59
+ catch { case _ : OutOfMemoryError => true }
60
+ )
61
+
62
+ // should catch any errors specified, including fatal errors
63
+ assert(
64
+ try { { throw new OutOfMemoryError (); true } rescue { case _ : OutOfMemoryError => true } }
65
+ catch { case _ : OutOfMemoryError => false }
66
+ )
67
+
68
+ // should not catch NonLocalReturns
69
+ def foo (x : Int ): Int = returning[Int ] {
70
+ { throwReturn[Int ](4 * x) : Int } rescue 10
71
+ }
72
+
73
+ // should catch specified exceptions, but not NonLocalReturn
74
+ def bar (x : Int ): Int = returning[Int ] {
75
+ { throwReturn[Int ](4 * x) : Int } rescue { case _ => 10 }
76
+ }
77
+
45
78
}
0 commit comments