Subject: | Bleadperl v5.21.3-644-ge52eb89 breaks Lexical::Failure |
See <https://rt.perl.org/rt3/Ticket/Display.html?id=123172>.
In short:
$ perl5.8.9 -le 'sub { my $f; BEGIN { $ref = \$f; $f = 7; $$ref = 8; print $f } }'
8
$ perl5.10 -le 'sub { my $f; BEGIN { $ref = \$f; $f = 7; $$ref = 8; print $f } }'
7
$ perl5.20.1 -le 'sub { my $f; BEGIN { $ref = \$f; $f = 7; $$ref = 8; print $f } }'
7
$ perl5.21.5 -le 'sub { my $f; BEGIN { $ref = \$f; $f = 7; $$ref = 8; print $f } }'
8
You can see that \$stale_variable *copies* it, which results in buggy behaviour.
You probably didn’t realise that your
my $errmsg;
use TestModule errors => \$errmsg;
in t/inner_scalar.t was depending on this buggy behaviour. A reference to $errmsg was not being passed to TestModule::import, but, rather, a reference to a copy of $errmsg.
As of 5.21.4, a reference to $errmsg is indeed passed to TestModule.
Attached is an unfinished patch attempting to fix this another way. The only dependable way to see whether a variable is ‘unavailable’ is to look at the pad name. And to get to that, we need a sub ref and B.pm.
I notice that t/inner_scalar.t has a comment saying:
# Note: ideally the following would also warn when inner array used, but
# there doesn't seem to be any way to actually detect the problem. :-(
This fixes that, causing the test to fail, due to an incorrect test count (note the line 65).
The line 81 mentioned in the test output is for
my $outer_var;
{
subtest 'fail --> my outer scalar', sub {
plan tests => 2;
use TestModule errors => \$outer_var;
...
which probably should *not* warn, right?
I want your feedback before I finish this patch. Do you really want to depend on Devel::Caller as well? Do you think this B rummaging is worth it, or should the warning be skipped? If the warning should stay, do I need to comment the code copiously, so you can maintain it? :-)
$ perl5.21.5 -Mblib t/inner_scalar.t
1..13
ok 1 - Warned as expected at line
ok 2 - Warned as expected at line 26
ok 3 - Warning given
ok 4 - Warned as expected at line
ok 5 - Warned as expected at line 43
ok 6 - Warning given
ok 7 - Warned as expected at line
ok 8 - Warned as expected at line 65
ok 9 - Warned as expected at line 81
# Subtest: fail --> my inner scalar
1..2
ok 1 - Correct effects
ok 2 - Failed to bind, as expected
ok 10 - fail --> my inner scalar
# Subtest: fail --> my inner hash
1..2
ok 1 - Correct effects
ok 2 - Failed to bind, as expected
ok 11 - fail --> my inner hash
# Subtest: fail --> my inner array
1..2
ok 1 - Correct effects
ok 2 - Failed to bind, as expected
ok 12 - fail --> my inner array
# Subtest: fail --> my outer scalar
1..2
ok 1 - Correct effects
ok 2 - Successfully bound, as expected
ok 13 - fail --> my outer scalar
# Subtest: fail --> our package scalar
1..2
ok 1 - Correct effects
ok 2 - Successfully bound, as expected
ok 14 - fail --> our package scalar
# Subtest: fail --> qualified package scalar
1..2
ok 1 - Correct effects
ok 2 - Successfully bound, as expected
ok 15 - fail --> qualified package scalar
# Looks like you planned 13 tests but ran 15.
Subject: | open_9sI9iRDm.txt |
diff -rup Lexical-Failure-0.000006-G3OJHV/lib/Lexical/Failure.pm Lexical-Failure-0.000006-X3uPaH/lib/Lexical/Failure.pm
--- Lexical-Failure-0.000006-G3OJHV/lib/Lexical/Failure.pm 2014-05-02 17:59:58.000000000 -0700
+++ Lexical-Failure-0.000006-X3uPaH/lib/Lexical/Failure.pm 2014-11-11 22:17:43.000000000 -0800
@@ -225,7 +225,29 @@ sub _check_scoping_of {
my %vars = ( %{peek_our(3)}, %{peek_my(3)} );
# If it isn't any of them, warn us...
- if (!grep { $vars{$_} == $target_var } keys %vars) {
+ my $found = grep { $vars{$_} == $target_var } keys %vars;
+ if ($found) {
+ # Unfind it if it closes over an anonymous sub's stale var.
+ use B;
+ use Devel::Caller 'caller_cv';
+ my $cv = B::svref_2object(caller_cv(3));
+ my $outer = $cv->OUTSIDE;
+ if (ref $outer ne 'B::SPECIAL' && $outer->CvFLAGS & B::CVf_ANON) {
+ my @pad = $cv->PADLIST->ARRAYelt($cv->DEPTH)->ARRAY;
+ for (0..$#pad) {
+ if (ref $pad[$_] ne 'B::SPECIAL'
+ && $pad[$_]->object_2svref == $target_var)
+ {
+ if ($cv->PADLIST->ARRAYelt(0)->ARRAYelt($_)->FLAGS
+ & B::SVf_FAKE) {
+ $found = 0;
+ last;
+ }
+ }
+ }
+ }
+ }
+ if (!$found) {
return if _is_package_var($target_var);
cluck 'Lexical ' . lc($var_type) . ' used as failure handler may not stay shared at runtime';
diff -rup Lexical-Failure-0.000006-G3OJHV/t/inner_scalar.t Lexical-Failure-0.000006-X3uPaH/t/inner_scalar.t
--- Lexical-Failure-0.000006-G3OJHV/t/inner_scalar.t 2014-05-02 15:58:09.000000000 -0700
+++ Lexical-Failure-0.000006-X3uPaH/t/inner_scalar.t 2014-11-11 22:27:05.000000000 -0800
@@ -52,7 +52,7 @@ BEGIN { $SIG{__WARN__} = \&_check_warnin
};
}
-BEGIN { $SIG{__WARN__} = sub { _check_warning(@_) } }
+#BEGIN { $SIG{__WARN__} = sub { _check_warning(@_) } }
# Note: ideally the following would also warn when inner array used, but
# there doesn't seem to be any way to actually detect the problem. :-(