[perl #68658] attributes turn "state" into "my"
authorFather Chrysostomos <sprout@cpan.org>
Thu, 9 Dec 2010 00:52:08 +0000 (16:52 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Thu, 9 Dec 2010 00:52:32 +0000 (16:52 -0800)
This is for two reasons:

• In S_my_kid, the attribute-handling code comes before the code that
  marks the padop as being a state instead of a my, which it knows to
  do based on the value of PL_parser->in_my. The attribute-handling
  code begins by setting PL_parser->in_my to FALSE, preventing the
  code that follows from doing its job.

  So now PL_parser->in_my is read at the top of S_my_kid, before the
  attribute code, with the statehood recorded in a boolean. Then the
  code that marks the padop as being state checks that boolean
  instead of in_my.

• A lexical variable declaration that has an attribute and is assigned
  to in the same expression compiles to something similar to:

    (attributes->import(... \$x ...), my $x) = 3;

  where the list is actually in scalar context, returning the my $x
  which is then assigned to (something that cannot be expressed
  directly in Perl syntax). So Perl_ck_sassign needs to take that list
  op into account when creating the ‘once’ op that actually makes
  state assignment work. Up till now it was just looking for a padsv
  on its LHS. This commit makes it check also for a list op whose last
  item is a padsv.

op.c
t/op/attrs.t

diff --git a/op.c b/op.c
index 13462d1..09b1bbc 100644 (file)
--- a/op.c
+++ b/op.c
@@ -2143,6 +2143,7 @@ S_my_kid(pTHX_ OP *o, OP *attrs, OP **imopsp)
 {
     dVAR;
     I32 type;
+    const bool stately = PL_parser && PL_parser->in_my == KEY_state;
 
     PERL_ARGS_ASSERT_MY_KID;
 
@@ -2213,7 +2214,7 @@ S_my_kid(pTHX_ OP *o, OP *attrs, OP **imopsp)
     }
     o->op_flags |= OPf_MOD;
     o->op_private |= OPpLVAL_INTRO;
-    if (PL_parser->in_my == KEY_state)
+    if (stately)
        o->op_private |= OPpPAD_STATE;
     return o;
 }
@@ -7937,7 +7938,13 @@ Perl_ck_sassign(pTHX_ OP *o)
     }
     if (kid->op_sibling) {
        OP *kkid = kid->op_sibling;
-       if (kkid->op_type == OP_PADSV
+       /* For state variable assignment, kkid is a list op whose op_last
+          is a padsv. */
+       if ((kkid->op_type == OP_PADSV ||
+            (kkid->op_type == OP_LIST &&
+             (kkid = cLISTOPx(kkid)->op_last)->op_type == OP_PADSV
+            )
+           )
                && (kkid->op_private & OPpLVAL_INTRO)
                && SvPAD_STATE(*av_fetch(PL_comppad_name, kkid->op_targ, FALSE))) {
            const PADOFFSET target = kkid->op_targ;
index 36d6bee..a8bcd6f 100644 (file)
@@ -313,4 +313,16 @@ foreach my $test (@tests) {
      'Calling closure proto with no @_ that returns a lexical';
 }
 
+# [perl #68658] Attributes on stately variables
+{
+  package thwext;
+  sub MODIFY_SCALAR_ATTRIBUTES { () }
+  my $i = 0;
+  my $x_values = '';
+  eval 'sub foo { use 5.01; state $x :A0 = $i++; $x_values .= $x }';
+  foo(); foo();
+  package main;
+  is $x_values, '00', 'state with attributes';
+}
+
 done_testing();