Fix try/catch catches more than it should #1859

Close #1885, #2140, #2011, #2220, #2485, #2073

Rename the FORK_OPT opcode to TRY_BEGIN, add a TRY_END opcode, and wrap
errors when raising through a TRY_END so that they will not be caught by
the matching TRY_BEGIN.

Now a `try exp catch handler` expression generates code like:

    TRY_BEGIN handler
    <exp>
    TRY_END
    JUMP past_handler
    handler: <handler>
    past_handler:
    ...

On backtrack through TRY_BEGIN it just backtracks.

If anything past the whole thing raises when <exp> produced a value,
then the TRY_END will catch the error, wrap it in another, and
backtrack.  The TRY_BEGIN will see a wrapped error and then it will
unwrap and re-raise the error.

If <exp> raises, then TRY_BEGIN will catch the error and jump to the
handler, but the TRY_BEGIN will not stack_save() in that case, so on
raise/backtrack the TRY_BEGIN will not execute again (nor will the
TRY_END).
This commit is contained in:
Nico Williams authored and GitHub committed 2023-07-24 13:20:21 +02:00
1 parent 98f709d0c1
commit 1cf6515c06
7 files changed
+468 -368

No files matched your search

+1 -3
View File
@@ -379,11 +379,9 @@ Term "as" Patterns '|' Exp {
} |
"try" Exp "catch" Exp {
//$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, $4);
$$ = gen_try($2, gen_try_handler($4));
$$ = gen_try($2, $4);
} |
"try" Exp {
//$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, gen_op_simple(BACKTRACK));
$$ = gen_try($2, gen_op_simple(BACKTRACK));
} |
"try" Exp "catch" error {