Skip to content

Commit 0f0d601

Browse files
committed
Do the required JMPENV dance when invoking a nested runloop for defer {} block in order to correctly handle exceptions thrown and caught entirely within it
1 parent b39e463 commit 0f0d601

File tree

4 files changed

+77
-3
lines changed

4 files changed

+77
-3
lines changed

pod/perldelta.pod

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,19 @@ manager will later use a regex to expand these into links.
368368

369369
=item *
370370

371-
XXX
371+
Exceptions thrown and caught entirely within a C<defer {}> or C<finally {}>
372+
block no longer stop the outer run-loop.
373+
374+
Code such as the following would stop running the contents of the C<defer>
375+
block once the inner exception in the inner C<try>/C<catch> block was caught.
376+
This has now been fixed, and runs as expected. ([GH #23064]).
377+
378+
defer {
379+
try { die "It breaks\n"; }
380+
catch ($e) { warn $e }
381+
382+
say "This line would never run";
383+
}
372384

373385
=back
374386

pp_ctl.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6475,9 +6475,37 @@ _invoke_defer_block(pTHX_ U8 type, void *_arg)
64756475
SAVETMPS;
64766476

64776477
SAVEOP();
6478+
OP *was_PL_op = PL_op;
64786479
PL_op = start;
64796480

6480-
CALLRUNOPS(aTHX);
6481+
dJMPENV;
6482+
int ret;
6483+
JMPENV_PUSH(ret);
6484+
switch (ret) {
6485+
case 0: /* normal start */
6486+
redo_body:
6487+
CALLRUNOPS(aTHX);
6488+
break;
6489+
6490+
case 3: /* exception happened */
6491+
if (PL_restartjmpenv == PL_top_env) {
6492+
if (!PL_restartop)
6493+
break;
6494+
PL_restartjmpenv = NULL;
6495+
PL_op = PL_restartop;
6496+
PL_restartop = NULL;
6497+
goto redo_body;
6498+
}
6499+
6500+
/* FALLTHROUGH */
6501+
default:
6502+
JMPENV_POP;
6503+
PL_op = was_PL_op;
6504+
JMPENV_JUMP(ret);
6505+
NOT_REACHED;
6506+
}
6507+
6508+
JMPENV_POP;
64816509

64826510
FREETMPS;
64836511
LEAVE;

t/op/defer.t

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ BEGIN {
66
set_up_inc('../lib');
77
}
88

9-
plan 28;
9+
plan 29;
1010

1111
use feature 'defer';
1212
no warnings 'experimental::defer';
@@ -285,3 +285,18 @@ no warnings 'experimental::defer';
285285
like($e, qr/^Bareword "foo" not allowed while "strict subs" in use at /,
286286
'Error from finalization');
287287
}
288+
289+
# GH#23604
290+
{
291+
my $ok;
292+
{
293+
defer {
294+
eval { die "Ignore this error\n" };
295+
$ok .= "k";
296+
}
297+
298+
$ok .= "o";
299+
}
300+
301+
is($ok, "ok", 'eval{die} inside defer does not stop runloop');
302+
}

t/op/try.t

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,25 @@ no warnings 'experimental::try';
330330
ok($finally_invoked, 'finally block still invoked for side-effects');
331331
}
332332

333+
# Variant of GH#23604
334+
{
335+
my $ok;
336+
try {
337+
# nothing
338+
}
339+
catch ($e) {}
340+
finally {
341+
try {
342+
die "Ignore this error\n"
343+
}
344+
catch ($e) {}
345+
346+
$ok = "ok";
347+
}
348+
349+
is($ok, "ok", 'try{die} inside try/finally does not stop runloop');
350+
}
351+
333352
# Nicer compiletime errors
334353
{
335354
my $e;

0 commit comments

Comments
 (0)