Given
my Int ($x, ($y, $z), $t) = ("x", "y");
In 5.20.1 it parses as:
6 <;> nextstate(main 2 -e:1) v:{ ->7
e <2> aassign[t5] vKS ->f
- <1> ex-list lKP ->a
7 <0> pushmark s ->8
8 <$> const(PV "x") s ->9
9 <$> const(PV "y") s ->a
- <1> ex-list lKPRM* ->e
a <0> padrange[$x:2,3] lRM/LVINTRO,1 ->b
- <0> padsv[$x:2,3] lRM*/LVINTRO ->b
c <@> list lKPRM* ->d
b <0> padrange[$y:2,3; $z:2,3] lRM/LVINTRO,2 ->c
- <0> padsv[$y:2,3] lRM*/LVINTRO ->-
- <0> padsv[$z:2,3] lRM*/LVINTRO ->c
d <0> padsv[$t:2,3] lRM*/LVINTRO ->e
In 5.21.6-to-be:
6 <;> nextstate(main 3 -e:1) v:{ ->7
b <2> aassign[t5] vKS ->c
- <1> ex-list lKP ->a
7 <0> pushmark s ->8
8 <$> const[PV "x"] s ->9
9 <$> const[PV "y"] s ->a
- <1> ex-list lKPRM* ->b
a <0> padrange[$x:3,4; $y:3,4; $z:3,4; $t:3,4] lRM/LVINTRO,4 ->b
- <0> padsv[$x:3,4] lRM*/LVINTRO ->-
- <1> ex-list lKPRM* ->-
- <0> ex-pushmark sRM*/LVINTRO ->-
- <0> padsv[$y:3,4] lRM*/LVINTRO ->-
- <0> padsv[$z:3,4] lRM*/LVINTRO ->-
- <0> padsv[$t:3,4] lRM*/LVINTRO ->-
The inner list is optimised away, but still visible in the op tree.
Also, I noticed that this comment in Types.xs is wrong:
/* In a padrange sequence, either all lexicals are typed, or none are.
* Thus we can stop at the first padsv op. However, note that these
* lexicals can need to call different methods in different packages. */
$ perl5.20.1 -MO=Concise -e '(my main $a, my $b, my main $c) = ()'
6 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 1 -e:1) v:{ ->3
5 <2> aassign[t4] vKS ->6
- <1> ex-list lK ->4
3 <0> pushmark s ->4
- <0> stub lP ->-
- <1> ex-list lKPRM* ->5
4 <0> padrange[$a:1,2; $b:1,2; $c:1,2] lRM/LVINTRO,3 ->5
- <0> padsv[$a:1,2] lRM*/LVINTRO ->-
- <0> padsv[$b:1,2] lRM*/LVINTRO ->-
- <0> padsv[$c:1,2] lRM*/LVINTRO ->-
-e syntax OK
I don’t know whether that has any bearing on the correctness of the code,
but I suspect there is a bug.
The attached patch gets all the tests passing, but I don’t know whether I
caught everything. Note that there are cases where the sibling of the
padrange is actually a null op :
$ ./perl -ILib -MO=Concise -e '@a[my $x, my $y, my $z]'
7 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 1 -e:1) v:{ ->3
6 <@> aslice vK ->7
3 <0> padrange[$x:1,2; $y:1,2; $z:1,2] l/LVINTRO,3 ->4
- <1> ex-list lK ->4
- <0> ex-pushmark s ->-
- <0> padsv[$x:1,2] lM/LVINTRO ->-
- <0> padsv[$y:1,2] lM/LVINTRO ->-
- <0> padsv[$z:1,2] lM/LVINTRO ->-
5 <1> rv2av[t2] sKR/1 ->6
4 <#> gv[*a] s ->5
-e syntax OK
This fixes RT #100202.
base = PL_op->op_targ;
count = PL_op->op_private & OPpPADRANGE_COUNTMASK;
- for (i = 0, p = roi.padxv_start; i < count && p; ++i, p = OP_SIBLING(p)) {
+ for (i = 0, p = roi.padxv_start; i < count && p; ++i, p = p->op_next) {
+ while (p->op_type == OP_NULL)
+ p = p->op_next;
lt_op_padxv_info oi;
if (p->op_type == OP_PADSV && lt_padxv_map_fetch(p, &oi))
lt_op_padxv_info_call(&oi, PAD_SV(base + i));
count = o->op_private & OPpPADRANGE_COUNTMASK;
- for (i = 0, p = start; i < count && p; ++i, p = OP_SIBLING(p)) {
+ for (i = 0, p = start; i < count && p; ++i, p = p->op_next) {
if (p->op_type == OP_PADSV) {
/* In a padrange sequence, either all lexicals are typed, or none are.
* Thus we can stop at the first padsv op. However, note that these
&& !(o->op_flags & OPf_SPECIAL)) {
/* A padrange op is guaranteed to have previously been a pushmark.
* Moreover, for non-special padrange ops (i.e. that aren't for
- * my (...) = @_), the original padxv ops are its siblings. */
- lt_maybe_padrange_setup(o, OP_SIBLING(o));
+ * my (...) = @_), the first original padxv is its sibling or nephew.
+ */
+ OP *kid = OP_SIBLING(o);
+ if (kid->op_type == OP_NULL && kid->op_flags & OPf_KIDS) {
+ kid = kUNOP->op_first;
+ if (kid->op_type == OP_NULL)
+ kid = OP_SIBLING(kid);
+ }
+ lt_maybe_padrange_setup(o, kid);
}
break;
case OP_AASSIGN: {