In perl.git, the branch blead has been updated <http://perl5.git.perl.org/perl.git/commitdiff/43dbb3c1592f7f9ccbe0aba64ef2684f39b673ed?hp=f34228d67754c62c57c3533a692e29f905d8f15a>
- Log ----------------------------------------------------------------- commit 43dbb3c1592f7f9ccbe0aba64ef2684f39b673ed Author: David Mitchell <da...@iabyn.com> Date: Tue Feb 7 15:45:14 2017 +0000 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. ----------------------------------------------------------------------- Summary of changes: op.c | 15 +++++++++++++++ t/op/multideref.t | 14 +++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) 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))); diff --git a/t/op/multideref.t b/t/op/multideref.t index daf147d898..87c57e8ddd 100644 --- a/t/op/multideref.t +++ b/t/op/multideref.t @@ -18,7 +18,7 @@ BEGIN { use warnings; use strict; -plan 58; +plan 60; # check that strict refs hint is handled @@ -205,3 +205,15 @@ sub defer {} 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"; +} -- Perl5 Master Repository