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.
&& ( (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)));
use warnings;
use strict;
-plan 58;
+plan 60;
# check that strict refs hint is handled
or diag("eval gave: $@");
is($warn, "", "#123609: warn");
}
+
+# RT #130727
+# a [ah]elem op can be both OPpLVAL_INTRO and OPpDEREF. It may not make
+# much sense, but it shouldn't fail an assert.
+
+{
+ my @x;
+ eval { @{local $x[0][0]} = 1; };
+ like $@, qr/Can't use an undefined value as an ARRAY reference/,
+ "RT #130727 error";
+ ok !defined $x[0][0],"RT #130727 array not autovivified";
+}