This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Make lv keys distinguish scalar/list cx properly
authorFather Chrysostomos <sprout@cpan.org>
Fri, 10 Jun 2016 15:49:59 +0000 (08:49 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Sat, 11 Jun 2016 13:17:01 +0000 (06:17 -0700)
keys(%h) was special in that it did not use the same code path as
other ops that distinguish between scalar and list lvalue context.
Consequently, some scalar lvalue contexts worked:

    keys %h = 3;
    ${\scalar keys %h} = 3;
    sub { $_[0] = 3 }->(scalar keys %h);
    foreach(scalar keys %h) { $_ = 3 }
    grep { $_ = 3 } scalar keys %h;
    substr keys %h, 0, = 3;

while others did not:

    keys %h .= 0;
    read FH, keys %h, 0;

Fixing other bugs in the same code paths without breaking keys (or
adding *more* exceptions) is harder to do if keys is not consistent.
So this commit allows .= and read to assign to keys, by using the same
internal code (scalar_mod_type) that determines whether %h assignment
is allowed.  The logic is reversed (since %h is list-only and keys %h
is scalar-only), so where %h is a valid lvalue keys %h is not, and
vice versa.

op.c
t/comp/parser.t

diff --git a/op.c b/op.c
index 6986610..a25471f 100644 (file)
--- a/op.c
+++ b/op.c
@@ -3042,7 +3042,7 @@ Perl_op_lvalue_flags(pTHX_ OP *o, I32 type, U32 flags)
        break;
 
     case OP_KEYS:
-       if (type != OP_SASSIGN && type != OP_LEAVESUBLV)
+       if (type != OP_LEAVESUBLV && !scalar_mod_type(NULL, type))
            goto nomod;
        goto lvalue_func;
     case OP_SUBSTR:
index efd3a8d..9652c42 100644 (file)
@@ -8,7 +8,7 @@ BEGIN {
     chdir 't' if -d 't';
 }
 
-print "1..182\n";
+print "1..185\n";
 
 sub failed {
     my ($got, $expected, $name) = @_;
@@ -565,6 +565,13 @@ for my $marker (qw(
     like $@, qr/^Version control conflict marker at \(eval \d+\) line 3, near "$marker"/, "VCS marker '$marker' after operator";
 }
 
+# keys assignments in weird contexts (mentioned in perl #128260)
+eval 'keys(%h) .= "00"';
+is $@, "", 'keys .=';
+eval 'sub { read $fh, keys %h, 0 }';
+is $@, "", 'read into keys';
+eval 'substr keys(%h),0,=3';
+is $@, "", 'substr keys assignment';
 
 # Add new tests HERE (above this line)