Sometimes, it really IS a bug in Perl

Sometimes, you find a bug so bizarre that you start to think it must be a bug in Perl itself. Nine times out of ten or more, it's not, and some more determined digging will turn up whatever you did wrong.

It's such a common tendency that I usually bias the other way – the moment I start to think Perl might have a bug, I dismiss it and look harder for the bug in my code.

But sometimes, it really IS a bug in Perl.

Here's what happened to me last week:

  • I tried installing a module from CPAN and had a test failure in the boolean.pm module dependency.
  • The error messages were coming from the latest release candidate of Test::More, which I had installed to help flush out issues
  • Downgrading to the stable Test::More made the problem go away
  • Investigating what broke in the release candidate got strange; something in an eval seemed to be having an impossible result

Graham Knop did some incredibly diligent digging and was able to produce a simplified reproduction independent of Test::More. Here's what was necessary:

  • Perl 5.20.0 or 5.20.1 with threads
  • Load boolean.pm using the '-truth' option
  • Construct an anonymous function with a closure
  • Watch literal strings ("Hello world") in the closure DISAPPEAR and become undefined

I must explain that what the '-truth' option does is incredibly unwise – perhaps even "evil". When loaded with '-truth', boolean.pm unlocks the read-only status of Perl's internal variables representing the values for true and false, replaces them with boolean.pm objects, and relocks the variable. That means that 1 == 1 returns a boolean.pm object rather than the usual "1" for truth.

Here's an example program that demonstrates the bug.

use strict;
use warnings;
use boolean -truth;

my $name = 'welp';

my $not_broken = sub {
  print "what\n";
};

my $broken = sub {
  print "what\n";
  my $str = $name;
};

print "calling not_broken:\n";
$not_broken->();
print "calling broken:\n";
$broken->();

On Perl 5.18.2-threaded and Perl 5.20.0-not-threaded the output is:

calling not_broken:
what
calling broken:
what

But on Perl 5.20.0-threaded and 5.20.1-threaded, the output is:

calling not_broken:
what
calling broken:
Use of uninitialized value0 in print at boolean-broken.pl line 12.

Note that line 12 is print "what\n" and value0 is the first argument to print. The literal string has become undefined!

So, yes, this is actually a bug in Perl. It's triggered by a very dubious feature of boolean.pm, but it doesn't occur in 5.18.2 with threads or in any 5.20 without threads, so this is very specific to 5.20.x with threads. That's a bug.

Fortunately, there's some good news:

  • Perl 5.21.5 already disallowed unlocking internal read-only variables (which broke boolean.pm tests, too)
  • boolean.pm has deprecated the '-truth' option in light of this bug and the change in 5.21.5
  • Father Chrysostomos jury-rigged the latest bleadperl to allow unlocking the read only true/false variables and the problem did not occur under a threaded build, so the the bug appears fixed even if the internal unlocking were still allowed

The moral of the story is that while you shouldn't jump to the conclusion that some strange behavior is a bug in Perl, it's worth keeping in mind that sometimes, it really is.

This entry was posted in p5p, perl programming and tagged , , . Bookmark the permalink. Both comments and trackbacks are currently closed.

© 2009-2017 David Golden All Rights Reserved