http://www.perlmonks.org?node_id=1073753

djerius has asked for the wisdom of the Perl Monks concerning the following question:

Yes 5.10.1 is ancient. Still, I have in the past attempted to make sure code runs on it, if only because it's the standard Perl shipped with CentOS 6, which is one of the platforms I support.

Perhaps no more.

I have always believed that require acts only during the execution phase of a program, not during compilation.

Not under 5.10.1...

Here's the test code. Note that the require FileHandle line is in a block which is never run and commented out

use File::Temp qw( tempfile ); if ( 0 ) { print STDERR "required\n"; # require FileHandle; } my ($fh, $file) = tempfile( UNLINK => 1 ); $fh->getline;
Here's what I get if I run it:
$ perl ttrap.t
Now, uncomment that require FileHandle line, so that it's visible to the compiler. It's still in a block which will never be run.
$ perl ttrap.t Can't locate object method "getline" via package "FileHandle" at ttrap +.t line 12.
Huh? The require FileHandle is never executed, so why should it make a difference if the compiler sees the code? (And it's not actually loading FileHandle.pm; I've checked for that).

Now, change that if ( 0 ) to if ( 1 ) so that the block is executed, and rerun:

$ perl ttrap.t required
Here it actually is loading FileHandle.pm (I checked). So, it looks like the compiler does something special if it detects there's a possibility that FileHandle is being required, but that messes things up if it isn't actually required. Replacing
require FileHandle;
with
eval 'require FileHandle';
doesn't trigger the error, which bolsters the argument.

This makes me wonder if there are any other "optimizations" like this lurking in the mists.

(I've tried this code with earlier and later versions of Perl, and this discordant behavior is absent. I checked perldelta's and didn't see any relevant comment. Perhaps I missed it; that happens).

Update:

It seems the compiler is doing a pattern match for require Filehandle, as require FileHandle::WhoCaresAnyway; has the same effect.

Updated Update:

Ignore the conspiracy theories above. See tobyink's comment below.

tldr; If you're running on 5.10.1 and you conditionally require a module which is in the FileHandle hierarchy (e.g. FileHandle, FileHandle::Fmode), or you use something in that heirarchy below FileHandle (such as FileHandle::Fmode) you may run into problems using methods on file handles. To that end, I've put the following in code with those characteristics:

use if $^V ge v5.10.0 && $^V lt v5.11.0, 'FileHandle';

Replies are listed 'Best First'.
Re: Compile time action of require in 5.10.1??
by tobyink (Canon) on Feb 06, 2014 at 21:43 UTC

    This will give you part of the answer:

    $ perl -E'if (0) { require Foo::Bar } say for keys %Foo::' Bar::

    A require PackageName statement creates an empty stash for the package at compile time, even if the statement never gets executed.

    The next part of the answer is in gv.c, the Perl_newIO function. Part of what this function does is to check whether the FileHandle package exists (and it does this by checking whether the stash exists!) and if not, aliases it to IO::Handle.

    It's all pretty dumb. The situation was improved in 5.12, and as of 5.14 works sanely.

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
      Thanks for the explanation!

      I'd seen the code in Perl_newIO but didn't understand how it got triggered. Now it all makes sense.

      Well, that was 5 hours of my life gone trying to figure out why my tests using Test::Trap in systemsafe mode were silently dieing on 5.10.1. All because there was a use FileHandle::Fmode in a module far, far away.