summaryrefslogtreecommitdiff
path: root/op.c
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2017-02-07 15:45:14 +0000
committerDavid Mitchell <davem@iabyn.com>2017-02-07 15:46:57 +0000
commit43dbb3c1592f7f9ccbe0aba64ef2684f39b673ed (patch)
tree34b7347a24582292be93cb74c15dd562a1e0a731 /op.c
parentf34228d67754c62c57c3533a692e29f905d8f15a (diff)
downloadperl-43dbb3c1592f7f9ccbe0aba64ef2684f39b673ed.tar.gz
multideref: handle both OPpLVAL_INTRO,OPpDEREF
RT #130727 In a nested dereference like $a[0]{b}[1], all but the last aelem/helem will normally have a OPpDEREF_AV/HV flag, while the last won't have a deref but may well have OPpLVAL_INTRO, e.g. local $a[0]{b}[1] = 1; The code in S_maybe_multideref() which converts a chain of aelem/helem's into a single mltideref op assumes this - in particular that an op can't have both OPpLVAL_INTRO and OPpDEREF* at the same time. However, the following code violates that assumption: @{ local $a[0]{b}[1] } = 1; In @{expr} = 1, the array is in lvalue context, which makes expr be done in ref (autovivify) context. So the final aelem in the above expression gets both OPpLVAL_INTRO and OPpDEREF_AV flags. In the old days, pp_aelem (probably more by luck than design) would action OPpLVAL_INTRO and ignore OPpDEREF_AV. This commit makes pp_multideref behave in the same way. In particular, there's no point in autovivifying $a[0]{b}[1] as an array ref since the local() will be undone before it gets a change to be used. The easiest way to achieve this is to tun off the OPpDEREF flag on the aelem/helem op if the OPpLVAL_INTRO flag is set.
Diffstat (limited to 'op.c')
-rw-r--r--op.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/op.c b/op.c
index c9e2078589..def6aca846 100644
--- a/op.c
+++ b/op.c
@@ -13178,6 +13178,21 @@ S_maybe_multideref(pTHX_ OP *start, OP *orig_o, UV orig_action, U8 hints)
&& ( (o->op_private & OPpDEREF) == OPpDEREF_AV
|| (o->op_private & OPpDEREF) == OPpDEREF_HV);
+ /* This doesn't make much sense but is legal:
+ * @{ local $x[0][0] } = 1
+ * Since scope exit will undo the autovivification,
+ * don't bother in the first place. The OP_LEAVE
+ * assertion is in case there are other cases of both
+ * OPpLVAL_INTRO and OPpDEREF which don't include a scope
+ * exit that would undo the local - in which case this
+ * block of code would need rethinking.
+ */
+ if (is_deref && (o->op_private & OPpLVAL_INTRO)) {
+ assert(o->op_next->op_type == OP_LEAVE);
+ o->op_private &= ~OPpDEREF;
+ is_deref = FALSE;
+ }
+
if (is_deref) {
ASSUME(!(o->op_flags &
~(OPf_WANT|OPf_KIDS|OPf_MOD|OPf_PARENS)));