tags:

views:

146

answers:

1

This bit of code behaves differently under Perl 5.8 than it does under Perl 5.12:

my $badcode = sub { 1 / 0 };
print "Made it past the bad code.\n";

When I run it under 5.8, I get an error even though I never execute the division:

[brock@chase tmp]$ /usr/bin/perl -v  

This is perl, v5.8.8 built for i486-linux-gnu-thread-multi

[brock@chase tmp]$ /usr/bin/perl badcode.pl  
Illegal division by zero at badcode.pl line 1.

[brock@chase tmp]$ /usr/local/bin/perl -v  

This is perl 5, version 12, subversion 0 (v5.12.0) built for i686-linux  

[brock@chase tmp]$ /usr/local/bin/perl badcode.pl  
Made it past the bad code.

Under perl 5.10.1, it behaves as it does under 5.12:

brock@laptop:/var/tmp$ perl -v

This is perl, v5.10.1 (*) built for i486-linux-gnu-thread-multi

brock@laptop:/var/tmp$ perl badcode.pl  
Made it past the bad code.

I get the same results with a named subroutine, e.g.

sub badcode { 1 / 0 }

I don't see anything about this in the perl5100delta pod. Is this an undocumented change? A unintended side effect of some other change? (For the record, I think 5.10 and 5.12 are doing the Right Thing.)

+17  A: 

I believe this was planned, and I do see this mentioned in perl5100delta.pod:

Exceptions in constant folding

The constant folding routine is now wrapped in an exception handler, and if folding throws an exception (such as attempting to evaluate 0/0), perl now retains the current optree, rather than aborting the whole program. Without this change, programs would not compile if they had expressions that happened to generate exceptions, even though those expressions were in code that could never be reached at runtime. (Nicholas Clark, Dave Mitchell)

It just has do with the divided-by-zero exception not resulting in a compilation-stage abort.

Evan Carroll
Thanks, Evan. Now I need to figure out how to work around this in testing under perl 5.8, as I was using this code to test an edge case. *grumble*
Brock
@Brock : Not sure whether it'd work as I've never had to do this, but have you considered trapping the error using an `eval` wrapper?
Zaid
@Zaid no because constant folding happens prior to the eval during op-tree generation. eval just caches an op-tree. ex, `eval { 1/1 }`, is effectively no different in runtime than `eval { 1 }`.
Evan Carroll
Right, but you *can* do `$sub = eval 'sub { 1 / 0 }'` or `eval 'require "DefinesCrashySub.pm"'`
hobbs
The `eval` trick won't work, since what I'm testing is a bit of code where I `eval { $coderef->() }`, and trap the error to deal with elsewhere.However, this works, even in perl 5.8:`my $zero = 0; my $badcode = sub { 1 / $zero }; print "Made it past the bad code.\n";`
Brock
@Brock => that's because the variable in the expression prevents the constant folder from reducing it during compilation. If you just need to throw an exception, why not `my $badsub = sub {die "badsub died\n"};`
Eric Strom
@Eric: Yeah, that's what I should have done. Since the original code demonstrating the edge case was a division by zero error, I wrote it into the test code, only to discover the test code died under 5.8.
Brock
@Brock why not just write the `die` message yourself. `my $badcode = sub { die 'Illegal division by zero' };`
Brad Gilbert