Clear all variable pad slots after multi-variable foreach loop

We need to remember to clear all the pad slots related to iteration
variables, which means some number of additional ones after the main
"itervar" when doing multi-variable iteration. This ensures we don't
retain references or large scalar values longer than necessary.
This commit is contained in:
Paul "LeoNerd" Evans 2025-12-24 14:12:33 +00:00 committed by Paul Evans
parent 47a5d1f4d9
commit 4430717932
2 changed files with 34 additions and 0 deletions

View File

@ -4152,6 +4152,25 @@ Perl_cx_poploop(pTHX_ PERL_CONTEXT *cx)
cx->blk_loop.itersave = NULL;
SvREFCNT_dec(cursv);
}
if (CxPADLOOP(cx)) {
/* for my ... might be multivariable. my_op->op_next will point at
* the OP_ITER whose op_targ contains the count of how many more
* variables
*/
OP *iterop = cx->blk_loop.my_op->op_next;
assert(iterop->op_type == OP_ITER);
PADOFFSET how_many = iterop->op_targ;
/* op_targ actually stores count - 1, so it's the count of additional
* vars after itervar itself */
for (SV **svp = cx->blk_loop.itervar_u.svp + 1; how_many; svp++, how_many--) {
/* we didn't store an itersave for these, but we know they are all
* scoped to the loop we have just left. It's therefore safe to store
* &PL_sv_undef there since they weren't live before or afterwards.
*/
SvREFCNT_dec(*svp);
*svp = &PL_sv_undef;
}
}
if (cx->cx_type & CXp_FOR_LVREF) {
SV *itervar = (SV *)(cx)->blk_loop.itervar_u.gv;
SV *origval = (cx)->blk_loop.itersave;

View File

@ -511,4 +511,19 @@ is($continue, 'xx', 'continue reached twice');
pass '2-var for does not crash on lexical sub calls';
}
# all foreach iteration vars are cleared in a timely manner
{
my @arrx;
my @arry;
for my ($x, $y) (\@arrx, \@arry) {
# list items are aliased; add 1 to account for \ ref in the following
refcount_is \@arrx, 2+1, 'arrx refcount 2 inside loop';
refcount_is \@arry, 2+1, 'arry refcount 2 inside loop';
}
refcount_is \@arrx, 1+1, 'arrx refcount 1 after loop';
refcount_is \@arry, 1+1, 'arry refcount 1 after loop';
}
done_testing();