I was able to get to this short code after doing some serious debugging at work with a very obscure bug in the project. A die call that was not dying.
The issue happens only when calling script.pl
. If you call Class_A
directly, then the die
call will be successful.
We need three files:
File 1: script.pl
use strict;
use warnings;
use lib '.';
use Class_A;
# This should not execute. Class_A should die at loading time
print "We shouldn't get here. Class_A shoud not load and die.\n";
File 2: Class_A.pm
package Class_A;
use strict;
use warnings;
use Class_B;
# This code SHOULD die:
my $p = Class_B->new;
$p->do_something->die_now;
1;
File 3: Class_B.pm
package Class_B;
use strict;
use warnings;
sub new {
my $class = shift;
bless {}, $class;
}
sub do_something {
my $self = shift;
}
sub die_now {
die "No soup for you!";
}
sub DESTROY {
eval {
1;
};
}
1;
Notice the chained call at Class_A.pm line 8
? Well, if you unchain it, then the code dies successfully. :-|
# This works. There should be no difference.
$p->do_something;
$p->die_now;
And, the final surprise was to find out that just by removing the eval call at Class_B.pm line 19
, then things work as expected and the script dies.
I had the opportunity to test this in Perl 5.22.2
, Perl 5.26.1
and Perl 5.32.0
. For another wonderful surprise, this issue doesn't happen on 5.32.0
only.
Seriously, WT*? Any thoughts on what is going on here?
If LIST was empty or made an empty string, and $@ is also empty, then the string "Died" is used. You can also call die with a reference argument, and if this is trapped within an eval, $@ contains that reference.
If the exception is outside of all enclosing eval s, then the uncaught exception is printed to STDERR and perl exits with an exit code indicating failure. If you need to exit the process with a specific exit code, see exit. Most of the time, die is called with a string to use as the exception.
If LIST was empty or made an empty string, and $@ is also empty, then the string "Died" is used. You can also call die with a reference argument, and if this is trapped within an eval, $@ contains that reference. This permits more elaborate exception handling using objects that maintain arbitrary state about the exception.
The code you posted doesn't exhibit the problem since 5.28.
The problem relates to $@
getting clobbered (using eval { }
) during the unwind that occurs due to an exception. Apparently, unsetting $@
fools Perl into thinking no exception occurred.
According to perl528delta, $@
is now set after everything is destroyed, which prevents the destructor from clobbering $@
. You can also prevent the destructor from clobbering $@
by adding local $@;
(e.g. to support older versions of Perl).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With