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

Reply via email to