summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2010-12-08 16:52:08 -0800
committerFather Chrysostomos <sprout@cpan.org>2010-12-08 16:52:32 -0800
commita1fba7eb664f6fea65549e94672040d8e47c905e (patch)
treee4796dcd38c011b1967e5c74c5d95d0d21f5de68
parent9f0564c6a634ece822d85183eb4065e894294d7f (diff)
downloadperl-a1fba7eb664f6fea65549e94672040d8e47c905e.tar.gz
[perl #68658] attributes turn "state" into "my"
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.
-rw-r--r--op.c11
-rw-r--r--t/op/attrs.t12
2 files changed, 21 insertions, 2 deletions
diff --git a/op.c b/op.c
index 13462d107b..09b1bbc1bd 100644
--- 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;
diff --git a/t/op/attrs.t b/t/op/attrs.t
index 36d6beeee3..a8bcd6f455 100644
--- a/t/op/attrs.t
+++ b/t/op/attrs.t
@@ -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();