This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
OP_MULTIDEREF: remove ghost entry in op_aux list
authorDavid Mitchell <davem@iabyn.com>
Fri, 22 Jul 2016 12:12:17 +0000 (13:12 +0100)
committerDavid Mitchell <davem@iabyn.com>
Wed, 3 Aug 2016 19:54:41 +0000 (20:54 +0100)
In the case where a multideref expression ends with an index that initially
looks simple (e.g. const or var) but turns out to be complex, the code
which optimises a sequence of aelem etc into a single multideref backs off
from including that last index expression in the multideref, but still
allocates a slot in the op_aux array for that ghost index.

For example in

    $expr->{foo}[0][1+$x]

it allocates a slot for the "1" constant but then doesn't use it.

This isn't an issue for multideref itself, but when running Deparse or
otherwise inspecting the op tree, the $op->aux_list() method returns a
list of the elements of the op_aux array with an extra garbage element at
the end, which can cause Deparse to crash.

lib/B/Deparse.t
op.c

index 24db3de..2cc415e 100644 (file)
@@ -2458,12 +2458,17 @@ my $e = delete $h{'foo'}[$i];
 ####
 # multideref with leading expression
 my $r;
-my $x = ($r // [])->{'foo'}[0];
+my $x = +($r // [])->{'foo'}[0];
 ####
 # multideref with complex middle index
 my(%h, $i, $j, $k);
 my $x = $h{'foo'}[$i + $j]{$k};
 ####
+# multideref with trailing non-simple index that initially looks simple
+# (i.e. the constant "3")
+my($r, $i, $j, $k);
+my $x = +($r || {})->{'foo'}[$i + $j]{3 + $k};
+####
 # chdir
 chdir 'file';
 chdir FH;
diff --git a/op.c b/op.c
index ec13eaa..6b5ef81 100644 (file)
--- a/op.c
+++ b/op.c
@@ -13044,6 +13044,8 @@ S_maybe_multideref(pTHX_ OP *start, OP *orig_o, UV orig_action, U8 hints)
                 is_last = TRUE;
                 index_skip = action_count;
                 action |= MDEREF_FLAG_last;
+                if (index_type != MDEREF_INDEX_none)
+                    arg--;
             }
 
             if (pass)