-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce a flexible scheme for declaring and checking which exceptions can be thrown. It relies on the effects as implicit capabilities pattern. The scheme is not 100% safe yet since it does not track and prevent capability capture. Nevertheless, it's already useful for declaring thrown exceptions and finding mismatches between provided and required capabilities.
- Loading branch information
Showing
12 changed files
with
180 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package scala | ||
import language.experimental.erasedDefinitions | ||
import annotation.implicitNotFound | ||
|
||
/** An ability class that allows to throw exception `E`. When used with the | ||
* experimental.saferExceptions feature, a `throw Ex()` expression will require | ||
* a given of class `CanThrow[Ex]` to be available. | ||
*/ | ||
@implicitNotFound("The ability to throw exception ${E} is missing.\nThe ability can be provided by one of the following:\n - A using clause `(using CanThrow[${E}])`\n - A `canThrow` clause in a result type such as `X canThrow ${E}`\n - an enclosing `try` that catches ${E}") | ||
erased class CanThrow[-E <: Exception] | ||
|
||
/** A helper type to allow syntax like | ||
* | ||
* def f(): T canThrow Ex | ||
*/ | ||
infix type canThrow[R, +E <: Exception] = CanThrow[E] ?=> R | ||
|
||
object unsafeExceptions: | ||
given canThrowAny: CanThrow[Exception] = ??? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
-- Error: tests/neg/saferExceptions.scala:14:16 ------------------------------------------------------------------------ | ||
14 | case 4 => throw Exception() // error | ||
| ^^^^^^^^^^^^^^^^^ | ||
| The ability to throw exception Exception is missing. | ||
| The ability can be provided by one of the following: | ||
| - A using clause `(using CanThrow[Exception])` | ||
| - A `canThrow` clause in a result type such as `X canThrow Exception` | ||
| - an enclosing `try` that catches Exception | ||
| | ||
| The following import might fix the problem: | ||
| | ||
| import unsafeExceptions.canThrowAny | ||
| | ||
-- Error: tests/neg/saferExceptions.scala:19:48 ------------------------------------------------------------------------ | ||
19 | def baz(x: Int): Int canThrow Failure = bar(x) // error | ||
| ^ | ||
| The ability to throw exception java.io.IOException is missing. | ||
| The ability can be provided by one of the following: | ||
| - A using clause `(using CanThrow[java.io.IOException])` | ||
| - A `canThrow` clause in a result type such as `X canThrow java.io.IOException` | ||
| - an enclosing `try` that catches java.io.IOException | ||
| | ||
| The following import might fix the problem: | ||
| | ||
| import unsafeExceptions.canThrowAny | ||
| |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
object test: | ||
import language.experimental.saferExceptions | ||
import java.io.IOException | ||
|
||
class Failure extends Exception | ||
|
||
def bar(x: Int): Int | ||
`canThrow` Failure | ||
`canThrow` IOException = | ||
x match | ||
case 1 => throw AssertionError() | ||
case 2 => throw Failure() // ok | ||
case 3 => throw java.io.IOException() // ok | ||
case 4 => throw Exception() // error | ||
case 5 => throw Throwable() // ok: Throwable is treated as unchecked | ||
case _ => 0 | ||
|
||
def foo(x: Int): Int canThrow Exception = bar(x) | ||
def baz(x: Int): Int canThrow Failure = bar(x) // error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import language.experimental.saferExceptions | ||
|
||
|
||
class LimitExceeded extends Exception | ||
|
||
val limit = 10e9 | ||
|
||
def f(x: Double): Double canThrow LimitExceeded = | ||
if x < limit then x * x else throw LimitExceeded() | ||
|
||
@main def test(xs: Double*) = | ||
try println(xs.map(f).sum) | ||
catch case ex: LimitExceeded => println("too large") | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import language.experimental.saferExceptions | ||
|
||
class Fail extends Exception | ||
|
||
def foo(x: Int) = | ||
try x match | ||
case 1 => throw AssertionError() | ||
case 2 => throw Fail() | ||
case 3 => throw java.io.IOException() | ||
case 4 => throw Exception() | ||
case 5 => throw Throwable() | ||
case _ => 0 | ||
catch | ||
case ex: AssertionError => 1 | ||
case ex: Fail => 2 | ||
case ex: java.io.IOException => 3 | ||
case ex: Exception => 4 | ||
case ex: Throwable => 5 | ||
|
||
def bar(x: Int): Int canThrow Exception = | ||
x match | ||
case 1 => throw AssertionError() | ||
case 2 => throw Fail() | ||
case 3 => throw java.io.IOException() | ||
case 4 => throw Exception() | ||
case _ => 0 | ||
|
||
@main def Test = | ||
assert(foo(1) + foo(2) + foo(3) + foo(4) + foo(5) + foo(6) == 15) | ||
import unsafeExceptions.canThrowAny | ||
val x = | ||
try bar(2) | ||
catch case ex: Fail => 3 // OK | ||
assert(x == 3) |