[perl #109264] ->method(my(...)) forcing lvalue cx
authorFather Chrysostomos <sprout@cpan.org>
Sun, 29 Jan 2012 03:14:39 +0000 (19:14 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Sun, 29 Jan 2012 04:43:06 +0000 (20:43 -0800)
commit0865059d9cec0d198515152182d4283ab634748e
tree1b680fe427fe875c15229e0a912817916e167588
parent422d6414d26e413a69e401207deaf53fb640b369
[perl #109264] ->method(my(...)) forcing lvalue cx

A simple my($foo,$bar) list is flagged as an lvalue:

$ ./perl -Ilib -MO=Concise -e 'my($foo,$bar)'
7  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
6     <@> list vKPM/128 ->7
3        <0> pushmark vM/128 ->4
4        <0> padsv[$foo:1,2] vM/LVINTRO ->5
5        <0> padsv[$bar:1,2] vM/LVINTRO ->6
-e syntax OK

That 128 that the list op is the same flag as LVINTRO.

When a method call is compiled, the list op for the argument list is
itself converted into an entersub op.  That LVINTRO flag is never
turned off.  So foo->bar(my($foo,$bar)) becomes this:

$ ./perl -Ilib -MO=Concise -e 'foo->bar(my($foo,$bar))'
9  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
8     <1> entersub[t4] vKMS/LVINTRO,TARG ->9
3        <0> pushmark sM/128 ->4
4        <$> const[PV "foo"] sM/BARE ->5
5        <0> padsv[$foo:1,2] lM/LVINTRO ->6
6        <0> padsv[$bar:1,2] lM/LVINTRO ->7
7        <$> method_named[PV "bar"] ->8
-e syntax OK

This was rarely a problem until commit da1dff948 added lvalue check-
ing for method calls (a fifth bug fix in that commit not mentioned in
the commit message).

Calling the method will now result in ‘Can't modify non-lvalue subrou-
tine call’ unless the method has the :lvalue attribute.

Before that, this would only cause problems with lvalue methods:

$ perl -le '
    sub clear_queue:lvalue { warn "called"; undef }
    3==main->clear_queue(my ($id, $name))
'
called at -e line 2.
Can't return undef from lvalue subroutine at -e line 3.

Calling it with ($id, $name) was fine, and allowed undef to
be returned.

Perl_localize in op.c (which is called for my, our and local)
calls my() (aka Perl_my_attrs) on the list itself for my or our.
Perl_my_attrs was setting flags on the list, not just on its children.

So this commit modifies my_attrs not to set any flags on the list
op itself.

local() was not affected, as it goes through op_lvalue_flags instead
of my_attrs, and op_lvalue_flags doesn’t set flags on list ops (I
mean ops of type OP_LIST, not listops in general).  I added tests for
it anyway.
op.c
t/op/method.t