Subject: | action-at-a-distance failure mode |
Because base.pm will not require a module if a stash entry for the module's
$VERSION variable already exists, code which generates such a stash entry can
cause seemingly bizarre failure modes in far-away, unrelated places.
Consider the following program, which we will call 'basebug.pl'. It's sole
purpose is to print "Hello world.\n".
use Baz;
Baz->hello;
Here's Baz.pm...
package Baz;
use Foo::MoreFoo;
my $more_foo = Foo::MoreFoo->new;
sub hello { print "Hello, world.\n" }
1;
... here's Foo/MoreFoo.pm, used by Baz...
package Foo::MoreFoo;
use base qw( Foo );
1;
... and here's Foo::MoreFoo's parent, Foo.pm:
package Foo;
sub new { bless {}, shift }
1;
Nothing out of the ordinary and so far, everything works. However, if we
modify basebug.pl to use the Bar module...
use Bar;
use Baz;
Baz->hello;
... which looks like this...
package Bar;
sub print_foo_version {
require Foo;
print $Foo::VERSION;
}
1;
... we get compile-time failure in Baz.pm:
/home/creamyg/perltest/ $ /usr/local/bleadperl/bin/perl5.9.5 basebug.pl
Can't locate object method "new" via package "Foo::MoreFoo" at Baz.pm line 4.
Compilation failed in require at basebug.pl line 2.
BEGIN failed--compilation aborted at basebug.pl line 2.
/home/creamyg/perltest/ $
The ordinary debugging path suggested by the error message -- examining first
Baz.pm, then Foo/MoreFoo.pm, and finally Foo.pm -- will not reveal any obvious
culprit. That's because the problem ultimately lies in base::has_version:
sub has_version {
my($base) = shift;
my $vglob = ${$base.'::'}{VERSION};
return( ($vglob && *$vglob{SCALAR}) ? 1 : 0 );
}
Once Bar.pm is loaded, has_version() returns true for 'Foo' -- even though
$Foo::VERSION has neither been assigned to nor even accessed. As a result
base.pm decides that it is not necessary to require Foo.pm at the request of
Foo::MoreFoo.
While this error does not occur frequently in the wild, when it does, the cost
to the user is high because the debug path is obscure. I personally
encountered it after failing to wrap a "use_ok" test in a BEGIN block;
isolating it took me... longer than I would have liked. ;)
Unfortunately, no fix seems to be possible without breaking backwards
compatibility, so the only potential remedy is deprecation and replacement.
Tested with blead perl v5.9.5 DEVEL31701.