develooper Front page | perl.perl5.porters | Postings from February 2015

Re: [perl #123738] eval {} still clobbers $@ in a DESTROY

Thread Previous | Thread Next
Aristotle Pagaltzis
February 6, 2015 09:57
Re: [perl #123738] eval {} still clobbers $@ in a DESTROY
Message ID:
* Zefram <> [2015-02-05 12:45]:
> This is specific to the "for(Destroyer->new)" formulation; if that's
> replaced with "my $x = Destroyer->new;" then $@ comes out correctly.
> The inner eval{} does not need to be an eval: "$@ = 'wibble'" works
> just as well to clobber $@.
> The output suggests that the localised $_ is being destroyed after the
> outer eval thinks it's finished unwinding. I don't immediately see how
> this happens, but then I wouldn't, because the outer eval's idea of
> "finished unwinding" is the embodiment of mine. It was certainly my
> intent, in coding the 5.14 change, that code like this should see the
> "haha" exception in $@ immediately after the outer eval.


    #!/usr/bin/env perl
    package Destroyer {
        sub new { bless {} }
        sub DESTROY { warn 'in destroy'; eval {} }
    warn eval {
        for ( Destroyer->new() ) {
            die 'haha';
    warn 'after eval: ', $@;

I get this output:

    $ perl
    haha at line 8.
        ...caught at line 6.
    in destroy at line 4.
    after eval:  at line 11.

Note how the `warn` around the `eval` actually picks up the inside exception.

You can change this to the statement modifier form of `for` and you can change
it to an explicitly loop iterator variable, both package global (`for our $x`)
and lexical (`for my $x`) and reproduce the bug anyway.

So I would guess the bug is due to `foreach` aliasing – specifically that the
per-iteration aliasing is normally undone at the end of the iteration but that
that is preempted by the `die` here, such that the unaliasing winds up delayed
until all the way after the statement. So the `warn` *inside* that statement
picks up the right exception (and would do so even without the anti-clobber fix
if my guess is right), but then the unaliasing catches up so that by the *next*
statement the exception is gone.

This readily suggests a workaround for running under existing perls: if you
change the `warn eval` line to

    eval { ... }, my $e = $@;

and then add a line

    warn 'from same statement: ', $e;

then this extra line appears in the output:

    from same statement: haha at line 8.

So something like Try::Tiny can work around this issue already, at least.

Aristotle Pagaltzis // <>

Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About