When I wrote about exception handling in Perl I mentioned briefly that DESTROY() methods should localize $? too.

Try this script:


package Foo;

sub new {
    my $class = shift;
    open my $fh, "-|", "cat /dev/zero";
    return bless { fh => $fh }, $class;

    my $self = shift;
    close $self->{fh}

package main;

my $foo = Foo->new();
exit 42;

You would expect it to return with status code 42, but when I run it the status code is 13. Localizing $? in the DESTROY() method gives the expected 42 status code.

The most common usage of $? is explained in the perlvar manual page:

The status returned by the last pipe close, backtick ("``") command, successful call to wait() or waitpid(), or from the system() operator.

And the above destructor makes a 'pipe close' changing the value of $?. But the perlvar manual also mentions an secondary usage of $?:

Inside an "END" subroutine $? contains the value that is going to be given to "exit()". You can modify $? in an "END" subroutine to change the exit status of your program.

But this doesn't just holds for END block, it is true for any code run after the exit() invokation - including destructors.

And then to a mystery. Given the above Foo package it isn't very surprising that this script have the status code 13:

use Foo;
my $foo = Foo->new();

But why does the following change make the script have the status code 0?

use Foo;
my $foo = Foo->new();
$? = 42;