This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Ensure that the C<exists &Errno::EFOO> idiom continues to work as documented.
authorNicholas Clark <nick@ccl4.org>
Mon, 7 Mar 2011 10:27:17 +0000 (10:27 +0000)
committerNicholas Clark <nick@ccl4.org>
Mon, 7 Mar 2011 10:32:52 +0000 (10:32 +0000)
A change post-5.12 (probably 42607a60df6df19b) caused the documented idiom not
to work if Errno was loaded after the C<exists> code had been compiled, as
the compiler implicitly creates typeglobs in the Errno symbol table when it
builds the optree for the C<exists code>.

ext/Errno/Errno_pm.PL
ext/Errno/t/Errno.t

index 5725de8..c38f309 100644 (file)
@@ -356,14 +356,23 @@ EDQ
 print <<'ESQ';
     );
     # Generate proxy constant subroutines for all the values.
-    # We assume at this point that our symbol table is empty.
+    # Well, almost all the values. Unfortunately we can't assume that at this
+    # point that our symbol table is empty, as code such as if the parser has
+    # seen code such as C<exists &Errno::EINVAL>, it will have created the
+    # typeglob.
     # Doing this before defining @EXPORT_OK etc means that even if a platform is
     # crazy enough to define EXPORT_OK as an error constant, everything will
     # still work, because the parser will upgrade the PCS to a real typeglob.
     # We rely on the subroutine definitions below to update the internal caches.
     # Don't use %each, as we don't want a copy of the value.
     foreach my $name (keys %err) {
-        $Errno::{$name} = \$err{$name};
+        if ($Errno::{$name}) {
+            # We expect this to be reached fairly rarely, so take an approach
+            # which uses the least compile time effort in the common case:
+            eval "sub $name() { $err{$name} }; 1" or die $@;
+        } else {
+            $Errno::{$name} = \$err{$name};
+        }
     }
 }
 
index 302bd8d..3baaf60 100644 (file)
@@ -1,6 +1,9 @@
 #!./perl -w
 
-use Test::More tests => 10;
+use Test::More tests => 12;
+
+# Keep this before the use Errno.
+my $has_einval = exists &Errno::EINVAL;
 
 BEGIN {
     use_ok("Errno");
@@ -34,3 +37,17 @@ like($@, qr/^ERRNO hash is read only!/);
 # through Acme::MetaSyntactic::batman
 is($!{EFLRBBB}, "");
 ok(! exists($!{EFLRBBB}));
+
+SKIP: {
+    skip("Errno does not have EINVAL", 1)
+       unless grep {$_ eq 'EINVAL'} @Errno::EXPORT_OK;
+    is($has_einval, 1,
+       'exists &Errno::EINVAL compiled before Errno is loaded works fine');
+}
+
+SKIP: {
+    skip("Errno does not have EBADF", 1)
+       unless grep {$_ eq 'EBADF'} @Errno::EXPORT_OK;
+    is(exists &Errno::EBADF, 1,
+       'exists &Errno::EBADF compiled after Errno is loaded works fine');
+}