perlevaldie

Perl die() call mysteriously not dying


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?


Solution

  • 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).