Skip to content

Commit 6741bab

Browse files
cknittclaude
andauthored
Don't warn about unreachable code for a finally block that always throws (#9204)
Give a finally block that always throws no unreachable-code warning Why: The finally block is typed as a statement, and a statement of type 'a reports warning 21, although no code follows a throw that ends the finally block. Code that follows it inside the block still warns. Diagnostic removed (warning 21 for a throwing finally block): Example: exception B let k = () => try { 1 } finally { throw(B) } Before (diagnostic): Warning number 21 2:35-42 This statement does not continue execution; following code is unreachable. After (diagnostic): no warning Review: Does `{ throw(B); Console.log(2) }` in a finally block still warn? Yes, the sequence types `throw(B)` as an ordinary statement. Claude-Session: https://claude.ai/code/session_01Kbf3gGhHjzBTXqSo8Dckpb Signed-off-by: Christoph Knittel <ck@cca.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 59d2dd5 commit 6741bab

3 files changed

Lines changed: 67 additions & 4 deletions

File tree

‎compiler/ml/typecore.ml‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2914,12 +2914,15 @@ and type_expect_ ?deprecated_context ~context ?(recarg = Rejected)
29142914
in
29152915
(* JavaScript runs the finally block after the body or catch has already
29162916
decided how the try is left, so a break or continue there would cancel
2917-
a pending exception or result. *)
2917+
a pending exception or result. A finally block that always throws ends
2918+
the try with no code after it, so it gets no nonreturning-statement
2919+
warning. *)
29182920
let finally =
29192921
Option.map
29202922
(fun sfinally ->
29212923
with_control_flow_boundary ~in_finally:true (fun () ->
2922-
type_statement ~context:None env sfinally))
2924+
type_statement ~nonreturning_warning:false ~context:None env
2925+
sfinally))
29232926
sfinally
29242927
in
29252928
re
@@ -4767,13 +4770,13 @@ and type_construct ~context env loc lid sargs ty_expected attrs =
47674770
47684771
(* Typing of statements (expressions whose values are discarded) *)
47694772
4770-
and type_statement ~context env sexp =
4773+
and type_statement ?(nonreturning_warning = true) ~context env sexp =
47714774
let loc = (final_subexpression sexp).pexp_loc in
47724775
begin_def ();
47734776
let exp = type_exp ~context env sexp in
47744777
end_def ();
47754778
let ty = expand_head env exp.exp_type and tv = newvar () in
4776-
if is_Tvar ty && ty.level > tv.level then
4779+
if nonreturning_warning && is_Tvar ty && ty.level > tv.level then
47774780
Location.prerr_warning loc Warnings.Nonreturning_statement;
47784781
let expected_ty = instance_def (Predef.type_unit ()) in
47794782
let context = type_clash_context_in_statement sexp in
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
Warning number 21
3+
/.../fixtures/finally_nonreturning_statement.res:47:5-12
4+
5+
45 ┆ 1
6+
46 ┆ } finally {
7+
47 ┆ throw(B)
8+
48 ┆ Console.log(2)
9+
49 ┆ }
10+
11+
This statement does not continue execution; following code is unreachable.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
exception B
2+
3+
let rec loop = () => loop()
4+
5+
let throwInFinally = () =>
6+
try {
7+
1
8+
} finally {
9+
throw(B)
10+
}
11+
12+
let throwAfterStatementInFinally = () =>
13+
try {
14+
1
15+
} finally {
16+
Console.log(1)
17+
throw(B)
18+
}
19+
20+
let throwInFinallyAfterCatch = () =>
21+
try {
22+
1
23+
} catch {
24+
| B => 2
25+
} finally {
26+
throw(B)
27+
}
28+
29+
let assertFalseInFinally = () =>
30+
try {
31+
1
32+
} finally {
33+
assert(false)
34+
}
35+
36+
let nonreturningCallInFinally = () =>
37+
try {
38+
1
39+
} finally {
40+
loop()
41+
}
42+
43+
let unreachableCodeInFinally = () =>
44+
try {
45+
1
46+
} finally {
47+
throw(B)
48+
Console.log(2)
49+
}

0 commit comments

Comments
 (0)