Skip to content

Commit 1eef69b

Browse files
committed
Better handling of multiple exceptions for saferExceptions - reworks
1 parent 05114db commit 1eef69b

File tree

7 files changed

+53
-40
lines changed

7 files changed

+53
-40
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,18 +1753,16 @@ class Typer extends Namer
17531753
if Feature.enabled(Feature.saferExceptions) then
17541754
for
17551755
CaseDef(pat, guard, _) <- cases
1756-
tpe = pat.tpe.widen
1757-
if tpe.isCheckedException
1756+
if pat.tpe.widen.isCheckedException
17581757
yield
17591758
checkCatch(pat, guard)
1760-
tpe
1759+
pat.tpe.widen
17611760
else Seq.empty
17621761

1763-
caughtExceptions match
1764-
case Nil => expr
1765-
case head :: tail =>
1766-
val capabilityProof = tail.foldLeft(head: Type)(OrType(_, _, true))
1767-
untpd.Block(makeCanThrow(capabilityProof), expr)
1762+
if caughtExceptions.isEmpty then expr
1763+
else
1764+
val capabilityProof = caughtExceptions.reduce(OrType(_, _, true))
1765+
untpd.Block(makeCanThrow(capabilityProof), expr)
17681766

17691767
def typedTry(tree: untpd.Try, pt: Type)(using Context): Try = {
17701768
val expr2 :: cases2x = harmonic(harmonize, pt) {

docs/docs/reference/experimental/canthrow.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ You'll get this error message:
162162
^^^^^^^^^^^^^^^^^^^^^
163163
The capability to throw exception LimitExceeded is missing.
164164
The capability can be provided by one of the following:
165-
- A using clause `(using CanThrow[LimitExceeded])`
166-
- A `throws` clause in a result type such as `X throws LimitExceeded`
167-
- an enclosing `try` that catches LimitExceeded
165+
- Adding a using clause `(using CanThrow[LimitExceeded])` to the definition of the enclosing method
166+
- Adding `throws LimitExceeded` clause after the result type of the enclosing method
167+
- Wrapping this piece of code with a `try` block that catches LimitExceeded
168168
169169
The following import might fix the problem:
170170

library/src/scala/CanThrow.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import annotation.{implicitNotFound, experimental}
77
* a given of class `CanThrow[Ex]` to be available.
88
*/
99
@experimental
10-
@implicitNotFound("The capability to throw exception ${E} is missing.\nThe capability can be provided by one of the following:\n - A using clause `(using CanThrow[${E}])`\n - A `throws` clause in a result type such as `X throws ${E}`\n - an enclosing `try` that catches ${E}")
10+
@implicitNotFound("The capability to throw exception ${E} is missing.\nThe capability can be provided by one of the following:\n - Adding a using clause `(using CanThrow[${E}])` to the definition of the enclosing method\n - Adding `throws ${E}` clause after the result type of the enclosing method\n - Wrapping this piece of code with a `try` block that catches ${E}")
1111
erased class CanThrow[-E <: Exception]
1212

1313
@experimental

tests/neg/i13846.check

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
-- Error: tests/neg/i13846.scala:7:9 -----------------------------------------------------------------------------------
66
7 | foo() // error
77
| ^
8-
| The capability to throw exception ArithmeticException is missing.
9-
| The capability can be provided by one of the following:
10-
| - A using clause `(using CanThrow[ArithmeticException])`
11-
| - A `throws` clause in a result type such as `X throws ArithmeticException`
12-
| - an enclosing `try` that catches ArithmeticException
8+
| The capability to throw exception ArithmeticException is missing.
9+
| The capability can be provided by one of the following:
10+
| - Adding a using clause `(using CanThrow[ArithmeticException])` to the definition of the enclosing method
11+
| - Adding `throws ArithmeticException` clause after the result type of the enclosing method
12+
| - Wrapping this piece of code with a `try` block that catches ArithmeticException
1313
|
14-
| The following import might fix the problem:
14+
| The following import might fix the problem:
1515
|
16-
| import unsafeExceptions.canThrowAny
16+
| import unsafeExceptions.canThrowAny
1717
|

tests/neg/i13864.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
| ^
99
| The capability to throw exception Ex[Int] is missing.
1010
| The capability can be provided by one of the following:
11-
| - A using clause `(using CanThrow[Ex[Int]])`
12-
| - A `throws` clause in a result type such as `X throws Ex[Int]`
13-
| - an enclosing `try` that catches Ex[Int]
11+
| - Adding a using clause `(using CanThrow[Ex[Int]])` to the definition of the enclosing method
12+
| - Adding `throws Ex[Int]` clause after the result type of the enclosing method
13+
| - Wrapping this piece of code with a `try` block that catches Ex[Int]
1414
|
1515
| The following import might fix the problem:
1616
|

tests/neg/saferExceptions.check

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
| ^^^^^^^^^^^^^^^^^
44
| The capability to throw exception Exception is missing.
55
| The capability can be provided by one of the following:
6-
| - A using clause `(using CanThrow[Exception])`
7-
| - A `throws` clause in a result type such as `X throws Exception`
8-
| - an enclosing `try` that catches Exception
6+
| - Adding a using clause `(using CanThrow[Exception])` to the definition of the enclosing method
7+
| - Adding `throws Exception` clause after the result type of the enclosing method
8+
| - Wrapping this piece of code with a `try` block that catches Exception
99
|
1010
| The following import might fix the problem:
1111
|
@@ -14,13 +14,13 @@
1414
-- Error: tests/neg/saferExceptions.scala:17:46 ------------------------------------------------------------------------
1515
17 | def baz(x: Int): Int throws Failure = bar(x) // error
1616
| ^
17-
| The capability to throw exception java.io.IOException is missing.
18-
| The capability can be provided by one of the following:
19-
| - A using clause `(using CanThrow[java.io.IOException])`
20-
| - A `throws` clause in a result type such as `X throws java.io.IOException`
21-
| - an enclosing `try` that catches java.io.IOException
17+
| The capability to throw exception java.io.IOException is missing.
18+
| The capability can be provided by one of the following:
19+
| - Adding a using clause `(using CanThrow[java.io.IOException])` to the definition of the enclosing method
20+
| - Adding `throws java.io.IOException` clause after the result type of the enclosing method
21+
| - Wrapping this piece of code with a `try` block that catches java.io.IOException
2222
|
23-
| The following import might fix the problem:
23+
| The following import might fix the problem:
2424
|
25-
| import unsafeExceptions.canThrowAny
25+
| import unsafeExceptions.canThrowAny
2626
|

tests/pos/i13816.scala

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@ class Ex1 extends Exception("Ex1")
44
class Ex2 extends Exception("Ex2")
55

66
def foo1(i: Int): Unit throws Ex1 throws Ex2 =
7-
if i > 0 then throw new Ex1 else throw new Ex2
7+
if i > 0 then throw new Ex1 else throw new Ex2
88

99
def foo2(i: Int): Unit throws Ex1 | Ex2 =
10-
if i > 0 then throw new Ex1 else throw new Ex2
10+
if i > 0 then throw new Ex1 else throw new Ex2
1111

1212
def foo3(i: Int): Unit throws (Ex1 | Ex2) =
13-
if i > 0 then throw new Ex1 else throw new Ex2
13+
if i > 0 then throw new Ex1 else throw new Ex2
1414

1515
def foo4(i: Int)(using CanThrow[Ex1], CanThrow[Ex2]): Unit =
16-
if i > 0 then throw new Ex1 else throw new Ex2
16+
if i > 0 then throw new Ex1 else throw new Ex2
1717

1818
def foo5(i: Int)(using CanThrow[Ex1])(using CanThrow[Ex2]): Unit =
19-
if i > 0 then throw new Ex1 else throw new Ex2
19+
if i > 0 then throw new Ex1 else throw new Ex2
2020

2121
def foo6(i: Int)(using CanThrow[Ex1 | Ex2]): Unit =
22-
if i > 0 then throw new Ex1 else throw new Ex2
22+
if i > 0 then throw new Ex1 else throw new Ex2
2323

2424
def foo7(i: Int)(using CanThrow[Ex1]): Unit throws Ex2 =
25-
if i > 0 then throw new Ex1 else throw new Ex2
25+
if i > 0 then throw new Ex1 else throw new Ex2
2626

2727
def foo8(i: Int)(using CanThrow[Ex2]): Unit throws Ex1 =
28-
if i > 0 then throw new Ex1 else throw new Ex2
28+
if i > 0 then throw new Ex1 else throw new Ex2
2929

3030
def test(): Unit =
3131
try
@@ -40,3 +40,18 @@ def test(): Unit =
4040
catch
4141
case _: Ex1 =>
4242
case _: Ex2 =>
43+
44+
try
45+
try
46+
foo1(1)
47+
foo2(1)
48+
foo3(1)
49+
foo4(1)
50+
foo5(1)
51+
// foo6(1) // As explained in the docs this won't work until we find a way to aggregate capabilities
52+
foo7(1)
53+
foo8(1)
54+
catch
55+
case _: Ex1 =>
56+
catch
57+
case _: Ex2 =>

0 commit comments

Comments
 (0)