This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Handle chop(@a =~ tr///)
authorDavid Mitchell <davem@iabyn.com>
Mon, 2 Jan 2017 16:37:27 +0000 (16:37 +0000)
committerDavid Mitchell <davem@iabyn.com>
Mon, 2 Jan 2017 16:52:34 +0000 (16:52 +0000)
commit2108cbcf2fd75bfcc7b9c01563db7063a67549cf
tree0f3918332820d329b207c64b35a6f700c0fc775b
parent700779a8627cf5e47eedfe20b1c2eb3c865afb11
Handle chop(@a =~ tr///)

RT #130198

'chop(@x =~ tr/1/1/)' crashed with an assertion failure. Ditto for chomp.

There are two quirks which together cause this. First, the op tree for
a tr// is different from other bind ops:

    $ perl -MO=Concise -e'$x =~ m/a/'
    5  <@> leave[1 ref] vKP/REFC ->(end)
    1     <0> enter ->2
    2     <;> nextstate(main 1 -e:1) v:{ ->3
    4     </> match(/"a"/) vKS ->5
    -        <1> ex-rv2sv sK/1 ->4
    3           <#> gvsv[*x] s ->4

    $ perl -MO=Concise -e'$x =~ tr/a/b/'
    5  <@> leave[1 ref] vKP/REFC ->(end)
    1     <0> enter ->2
    2     <;> nextstate(main 1 -e:1) v:{ ->3
    -     <1> null vKS/2 ->5
    -        <1> ex-rv2sv sKRM/1 ->4
    3           <#> gvsv[*x] s ->4
    4        <"> trans sS ->5

Note that the argument for the match is a child of the match, while the
arg of the trans is an (earlier) sibing of the trans (linked by a common
null parent).

The normal code path that croaks when e.g. a match is seen in an lvalue
context,

    $ perl -e'chop(@a =~ /a/)'
    Can't modify pattern match (m//) in chop at -e line 1, near "/a/)

is skipped, since lvalue() is only called for the first child of a null op.

Fixing this is as simple as calling lvalue() on the RHS too if the RHS is
a trans op.

The second issue is that chop and chomp are special-cased not to flatten
an array; so

    @b = 10..99;
    chop $a, @b, $c;

pushes 3 items on the stack to pass to pp_chop, rather than 102. pp_chop()
itself then iterates over any array args.

The compiler was seeing the rv2av op in chop(@a =~ tr///) and was setting
the OPf_REF (don't flatten) flag on it. Which then caused pp_trans to
panic when its arg was an AV rather than a string.

This second issue is now moot, since after the fix suggested above, we
will have croaked before we reach the place where OPf_REF would be set.

This commit adds lots of tests, since tr/a/a/ and tr/a/b/r are
special-cased in terms of whether they are regarded as modifying the
var they are bound to.
op.c
t/op/tr.t