Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'next' undefines variables in 'continue' block #1050

Closed
p5pRT opened this issue Jan 19, 2000 · 9 comments
Closed

'next' undefines variables in 'continue' block #1050

p5pRT opened this issue Jan 19, 2000 · 9 comments

Comments

@p5pRT
Copy link

p5pRT commented Jan 19, 2000

Migrated from rt.perl.org#2001 (status was 'resolved')

Searchable as RT2001$

@p5pRT
Copy link
Author

p5pRT commented Jan 19, 2000

From oracle@tauceti.pcr.com

A friend of mine ran into this problem, but I never saw it come up
on perl-porters, so I'm submitting it.

When 'next' exits a while block and enters a following continue
block, it undefines any 'my' variable that was declared and
initialized within the control expression. 'perlsub' is pretty
clear that the scope extends to the continue block. A sample​:

File test_next​:
$^W = 1;
my $x = 3;
while(my $i = $x--) {
  print "while block​: \$i = $i\n";
  next if $i == 2;
} continue {
  print "cont. block​: \$i = $i\n";
}
Output​:

while block​: $i = 3
cont. block​: $i = 3
while block​: $i = 2
Use of uninitialized value at test_next line 5.
cont. block​: $i =
while block​: $i = 1
cont. block​: $i = 1

Also, when 'next' is used to jump to a labelled outer loop with a
continue block, the same thing happens for variables declared in
the outer block's control expression.

After digging around in the perl source code, it seems that a
compiled while statement has an unstack op at the end. Without
a continue block, a next op jumps directly to the beginning of
the while block, skipping the unstack op, and so has to do the
unstacking itself. With a continue block, the next op jumps right
into the continue block, but still does the unstacking.

Perl Info


Site configuration information for perl 5.00502:

Configured by oracle at Thu Feb 11 16:37:09 EST 1999.

Summary of my perl5 (5.0 patchlevel 5 subversion 2) configuration:
  Platform:
    osname=solaris, osvers=2.6, archname=sun4-solaris
    uname='sunos tauceti 5.6 generic_105181-03 sun4u sparc sunw,ultra-4 '
    hint=previous, useposix=true, d_sigaction=define
    usethreads=undef useperlio=undef d_sfio=undef
  Compiler:
    cc='cc', optimize='-O', gccversion=
    cppflags='-I/usr/local/include'
    ccflags ='-I/usr/local/include'
    stdchar='unsigned char', d_stdstdio=define, usevfork=false
    intsize=4, longsize=4, ptrsize=4, doublesize=8
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
    alignbytes=8, usemymalloc=n, prototype=define
  Linker and Libraries:
    ld='cc', ldflags =' -L/usr/local/lib'
    libpth=/usr/local/lib /lib /usr/lib /usr/ccs/lib
    libs=-lsocket -lnsl -ldl -lm -lc -lcrypt
    libc=/lib/libc.so, so=so, useshrplib=true, libperl=libperl.a
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='  -R /usr/local/lib/perl5/5.005/sun4-solaris/CORE'
    cccdlflags='-KPIC', lddlflags='-G -L/usr/local/lib'

Locally applied patches:
    


@INC for perl 5.00502:
    /usr/local/lib/perl5/5.005/sun4-solaris
    /usr/local/lib/perl5/5.005
    /usr/local/lib/perl5/site_perl/5.005/sun4-solaris
    /usr/local/lib/perl5/site_perl/5.005
    .


Environment for perl 5.00502:
    HOME=/u/oracle
    LANG (unset)
    LD_LIBRARY_PATH=/usr/dt/lib:/opt/app/oracle/product/8.0.5/lib:/opt/SUNWmotif/lib:/usr/openwin/lib:.:
    LOGDIR (unset)
    PATH=.:/opt/app/oracle/product/8.0.5/bin:/u/oracle/local/bin:/u/oracle/bin:/usr/local/bin:/usr/bin:/etc:/opt/SUNWspro/bin:/usr/ucb:/opt/bin:/usr/ccs/bin:/usr/openwin/bin:/u/Pcr/Bin
    PERL_BADLANG (unset)
    SHELL=/bin/ksh

@p5pRT
Copy link
Author

p5pRT commented Jan 23, 2000

From @gsar

On Wed, 19 Jan 2000 09​:48​:19 MST, oracle@​tauceti.pcr.com wrote​:

File test_next​:
$^W = 1;
my $x = 3;
while(my $i = $x--) {
print "while block​: \$i = $i\n";
next if $i == 2;
} continue {
print "cont. block​: \$i = $i\n";
}
Output​:

while block​: $i = 3
cont. block​: $i = 3
while block​: $i = 2
Use of uninitialized value at test_next line 5.
cont. block​: $i =
while block​: $i = 1
cont. block​: $i = 1

Also, when 'next' is used to jump to a labelled outer loop with a
continue block, the same thing happens for variables declared in
the outer block's control expression.

After digging around in the perl source code, it seems that a
compiled while statement has an unstack op at the end. Without
a continue block, a next op jumps directly to the beginning of
the while block, skipping the unstack op, and so has to do the
unstacking itself. With a continue block, the next op jumps right
into the continue block, but still does the unstacking.

Thanks for that accurate diagnosis of the problem.

I noticed another longstanding bug​: local-ized values inside a while block
are visible in the continue block!

These two changes fix both problems.

There is one remaining problem with local-ized values still being visible
in the continue block after a next, but that will have to wait for another
day.

Sarathy
gsar@​ActiveState.com

Inline Patch
-----------------------------------8<-----------------------------------
Change 4848 by gsar@auger on 2000/01/23 06:43:51

	fix scope cleanup when next jumps to a continue block; this is rather
	in the nature of a kludge; it doesn't fix the longstanding bug that
	makes C<while (!$x++) { local $x = 7 } continue { print $x }> print "7"
	instead of "1")

Affected files ...

... //depot/perl/pp_ctl.c#174 edit
... //depot/perl/t/cmd/while.t#8 edit

Differences ...

==== //depot/perl/pp_ctl.c#174 (text) ====
Index: perl/pp_ctl.c
--- perl/pp_ctl.c.~1~	Sat Jan 22 22:43:56 2000
+++ perl/pp_ctl.c	Sat Jan 22 22:43:56 2000
@@ -1960,9 +1960,15 @@
 	dounwind(cxix);
 
     TOPBLOCK(cx);
-    oldsave = PL_scopestack[PL_scopestack_ix - 1];
-    LEAVE_SCOPE(oldsave);
-    return cx->blk_loop.next_op;
+    {
+	OP *nextop = cx->blk_loop.next_op;
+	/* clean scope, but only if there's no continue block */
+	if (nextop == cUNOPx(cx->blk_loop.last_op)->op_first->op_next) {
+	    oldsave = PL_scopestack[PL_scopestack_ix - 1];
+	    LEAVE_SCOPE(oldsave);
+	}
+	return nextop;
+    }
 }
 
 PP(pp_redo)

==== //depot/perl/t/cmd/while.t#8 (xtext) ====
Index: perl/t/cmd/while.t
--- perl/t/cmd/while.t.~1~	Sat Jan 22 22:43:56 2000
+++ perl/t/cmd/while.t	Sat Jan 22 22:43:56 2000
@@ -2,7 +2,7 @@
 
 # $RCSfile: while.t,v $$Revision: 4.1 $$Date: 92/08/07 18:27:15 $
 
-print "1..15\n";
+print "1..17\n";
 
 open (tmp,'>Cmd_while.tmp') || die "Can't create Cmd_while.tmp.";
 print tmp "tvi925\n";
@@ -128,3 +128,16 @@
 $i++;
 print "not " unless $` . $& . $' eq "abc";
 print "ok $i\n";
+
+# check that scope cleanup happens right when there's a continue block
+{
+    my $var = 16;
+    while (my $i = ++$var) {
+	next if $i == 17;
+	last if $i > 17;
+	my $i = 0;
+    }
+    continue {
+        print "ok ", $var-1, "\nok $i\n";
+    }
+}
End of Patch.

Change 4849 by gsar@auger on 2000/01/23 08:17:30

	fix localization in while BLOCK when there is a continue BLOCK
	by introducing an explicit scope (c.f. change#4848)

Affected files ...

... //depot/perl/op.c#242 edit
... //depot/perl/pp_ctl.c#175 edit
... //depot/perl/t/cmd/while.t#9 edit

Differences ...

==== //depot/perl/op.c#242 (text) ====
Index: perl/op.c
--- perl/op.c.~1~	Sun Jan 23 00:17:34 2000
+++ perl/op.c	Sun Jan 23 00:17:34 2000
@@ -3753,6 +3753,9 @@
 
     if (!block)
 	block = newOP(OP_NULL, 0);
+    else if (cont) {
+	block = scope(block);
+    }
 
     if (cont)
 	next = LINKLIST(cont);

==== //depot/perl/pp_ctl.c#175 (text) ====
Index: perl/pp_ctl.c
--- perl/pp_ctl.c.~1~	Sun Jan 23 00:17:34 2000
+++ perl/pp_ctl.c	Sun Jan 23 00:17:34 2000
@@ -1959,11 +1959,12 @@
     if (cxix < cxstack_ix)
 	dounwind(cxix);
 
-    TOPBLOCK(cx);
+    cx = &cxstack[cxstack_ix];
     {
 	OP *nextop = cx->blk_loop.next_op;
 	/* clean scope, but only if there's no continue block */
 	if (nextop == cUNOPx(cx->blk_loop.last_op)->op_first->op_next) {
+	    TOPBLOCK(cx);
 	    oldsave = PL_scopestack[PL_scopestack_ix - 1];
 	    LEAVE_SCOPE(oldsave);
 	}

==== //depot/perl/t/cmd/while.t#9 (xtext) ====
Index: perl/t/cmd/while.t
--- perl/t/cmd/while.t.~1~	Sun Jan 23 00:17:34 2000
+++ perl/t/cmd/while.t	Sun Jan 23 00:17:34 2000
@@ -1,8 +1,6 @@
 #!./perl
 
-# $RCSfile: while.t,v $$Revision: 4.1 $$Date: 92/08/07 18:27:15 $
-
-print "1..17\n";
+print "1..19\n";
 
 open (tmp,'>Cmd_while.tmp') || die "Can't create Cmd_while.tmp.";
 print tmp "tvi925\n";
@@ -141,3 +139,24 @@
         print "ok ", $var-1, "\nok $i\n";
     }
 }
+
+{
+    local $l = 18;
+    {
+        local $l = 0
+    }
+    continue {
+        print "ok $l\n"
+    }
+}
+
+{
+    local $l = 19;
+    my $x = 0;
+    while (!$x++) {
+        local $l = 0
+    }
+    continue {
+        print "ok $l\n"
+    }
+}
End of Patch.

@p5pRT
Copy link
Author

p5pRT commented Jan 23, 2000

From @timbunce

On Sun, Jan 23, 2000 at 12​:28​:33AM -0800, Gurusamy Sarathy wrote​:

On Wed, 19 Jan 2000 09​:48​:19 MST, oracle@​tauceti.pcr.com wrote​:

There is one remaining problem with local-ized values still being visible
in the continue block after a next, but that will have to wait for another
day.

That sounds like a borderline feature. Some people may be using it.

Tim.

@p5pRT
Copy link
Author

p5pRT commented Jan 23, 2000

From [Unknown Contact. See original ticket]

Hi,

Tim Bunce​:

There is one remaining problem with local-ized values still being visible
in the continue block after a next, but that will have to wait for another
day.

That sounds like a borderline feature. Some people may be using it.

Either a block is a block, no matter how you get there, or the continue
part is embedded in the block it is continuing -- conceptually, that is.

At the moment, we have some sort of pseudo block around a while(){} anyway,
i.e. the variables declared inside the parentheses are visible within the
block but not outside -- even though they're not declared within the braces
-- so an argument may be made for either case.

Personally, I'd favor the second solution.

--
Matthias Urlichs | noris network GmbH | smurf@​noris.de | ICQ​: 20193661
The quote was selected randomly. Really. | http​://www.noris.de/~smurf/
--
Newman's Discovery​:
  Your best dreams may not come true;
  fortunately, neither will your worst dreams.

@p5pRT
Copy link
Author

p5pRT commented Jan 23, 2000

From @gsar

On Sun, 23 Jan 2000 14​:17​:25 +0100, "Matthias Urlichs" wrote​:

Tim Bunce​:

There is one remaining problem with local-ized values still being visible
in the continue block after a next, but that will have to wait for another
day.

That sounds like a borderline feature. Some people may be using it.

Either a block is a block, no matter how you get there, or the continue
part is embedded in the block it is continuing -- conceptually, that is.

At the moment, we have some sort of pseudo block around a while(){} anyway,
i.e. the variables declared inside the parentheses are visible within the
block but not outside -- even though they're not declared within the braces
-- so an argument may be made for either case.

Looks like there's some misunderstanding here--the case I was referring
to doesn't have any variables localized in the conditional, only in the
body. This​:

  while (!$x++) {
  local $x = 7;
  next;
  }
  continue {
  print $x;
  }

will print "7" instead of "1". After the patches I showed, removing the
C<next;> makes it print "1", but the above still prints "7".

Sarathy
gsar@​ActiveState.com

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2000

From @TimToady

Gurusamy Sarathy writes​:
: Change 4849 by gsar@​auger on 2000/01/23 08​:17​:30
:
: fix localization in while BLOCK when there is a continue BLOCK
: by introducing an explicit scope (c.f. change#4848)

Tim is worried about backward compatibility. I'm worried about performance.

Larry

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2000

From @gsar

On Tue, 25 Jan 2000 09​:00​:36 PST, Larry Wall wrote​:

Gurusamy Sarathy writes​:
​: Change 4849 by gsar@​auger on 2000/01/23 08​:17​:30
​:
​: fix localization in while BLOCK when there is a continue BLOCK
​: by introducing an explicit scope (c.f. change#4848)

Tim is worried about backward compatibility. I'm worried about performance.

We ought to do some tests to see how bad it is, but I should point out
that loops without a continue block are unchanged by the patch.

Sarathy
gsar@​ActiveState.com

@p5pRT
Copy link
Author

p5pRT commented Mar 29, 2000

From [Unknown Contact. See original ticket]

"GSar" == Gurusamy Sarathy <gsar@​ActiveState.com> writes​:

GSar> On Wed, 19 Jan 2000 09​:48​:19 MST, oracle@​tauceti.pcr.com wrote​:
O> Also, when 'next' is used to jump to a labelled outer loop with a
O> continue block, the same thing happens for variables declared in
O> the outer block's control expression.

O> After digging around in the perl source code, it seems that a
O> compiled while statement has an unstack op at the end. Without a
O> continue block, a next op jumps directly to the beginning of the
O> while block, skipping the unstack op, and so has to do the
O> unstacking itself. With a continue block, the next op jumps right
O> into the continue block, but still does the unstacking.

GSar> Thanks for that accurate diagnosis of the problem.

GSar> I noticed another longstanding bug​: local-ized values inside a
GSar> while block are visible in the continue block!

GSar> These two changes fix both problems.

GSar> There is one remaining problem with local-ized values still
GSar> being visible in the continue block after a next, but that will
GSar> have to wait for another day.

GSar> Change 4848 by gsar@​auger on 2000/01/23 06​:43​:51

GSar> fix scope cleanup when next jumps to a continue block; this is rather
GSar> in the nature of a kludge; it doesn't fix the longstanding bug that
GSar> makes C<while (!$x++) { local $x = 7 } continue { print $x }> print "7"
GSar> instead of "1")

GSar> Affected files ...

GSar> ... //depot/perl/pp_ctl.c#174 edit
GSar> ... //depot/perl/t/cmd/while.t#8 edit

GSar> Differences ...

GSar> ==== //depot/perl/pp_ctl.c#174 (text) ====
GSar> Index​: perl/pp_ctl.c
GSar> --- perl/pp_ctl.c.1 Sat Jan 22 22​:43​:56 2000
GSar> +++ perl/pp_ctl.c Sat Jan 22 22​:43​:56 2000
GSar> @​@​ -1960,9 +1960,15 @​@​
GSar> dounwind(cxix);

GSar> TOPBLOCK(cx);
GSar> - oldsave = PL_scopestack[PL_scopestack_ix - 1];
GSar> - LEAVE_SCOPE(oldsave);
GSar> - return cx->blk_loop.next_op;
GSar> + {
GSar> + OP *nextop = cx->blk_loop.next_op;
GSar> + /* clean scope, but only if there's no continue block */
GSar> + if (nextop == cUNOPx(cx->blk_loop.last_op)->op_first->op_next) {
GSar> + oldsave = PL_scopestack[PL_scopestack_ix - 1];
GSar> + LEAVE_SCOPE(oldsave);
GSar> + }
GSar> + return nextop;
GSar> + }
GSar> }

Um, I think you still need to do some save-stack popping even if there
is a continue block, since dounwind() doesn't do any LEAVE_SCOPing,
but it may pass by any number of subs and foreach()es. For instance,
in 5.6.0​:

sub f { my $a = 1; my $b = 2; my $c = 3; my $d = 4; next }
my $x = "foo";
{ f } continue { print $x, "\n";}

Will print `1', and then if you add more my declarations before `my
$x' it will in turn print 2, 3, 4 and then undef a few times, and then
segfault, because $x is being looked up using f's value of PL_curpad,
which should have been un-saved when we next-ed out of f.

(The reason I just noticed this is that I think it's the proximate
cause of the problem with B​::Deparse that Tom reported yesterday
(which also involves something I consider a bug in B​::Deparse -- some
`next's that I should have turned into `return's when I switched from
an iteration using a for loop to one using a helper function)).

No, I don't have a patch, but I found the following message in my
archives which seems relevant; it includes an old patch that addresses
roughly the same problem (and which was never applied, AFAICT).

From​: Stephen McCamant <alias@​mcs.com>
Sender​: owner-perl5-porters@​perl.org
To​: perl5-porters@​perl.org
Cc​: "M.J.T. Guy" <mjtg@​cus.cam.ac.uk>
Subject​: Re​: while continue problem?
Date​: Wed, 29 Jul 1998 15​:36​:13 -0500 (CDT)

"MJTG" == M J T Guy <mjtg@​cus.cam.ac.uk> writes​:

MJTG> joshua.pritikin@​db.com wrote
JP> The $l lexical is not set in the continue block if the
JP> while loop executes 'next'. This is wrong, no? (5.005)

MJTG> I'd call it a bug. Note that it's been like that ever since the
MJTG> new lexical scopes were introduced in perl5.004.

MJTG> If it's not a bug, it's certainly anomalous and needs
MJTG> documenting.

I agree it's a bug, though it's a pretty deep one. If you don't mind,
I'm in an ASCII art mood today​:

+-----------+
| NEXTSTATE |
+-----------+ nextop(1a)
  | |
  V V
+-----------+ +-----------+ +-+ +-----+ +-----------+
| ENTERLOOP |->| condition | ... | | ... | AND |--next-> | LEAVELOOP |
+-----------+ +-----------+ +-+ +-----+ +-----------+
  ^ | ^
  | other |
  | | lastop
  +-----------+ |
  | NEXTSTATE | V
  +-----------+ +------+
  ^ | body |<-- redoop
  | +------+
  | :
  +---------+ +-+
  nextop(1b)->| UNSTACK | | |
  +---------+ +-+
  ^ ...................​: ...................
  | : +-+ +-+ +----------+ :
  +-----| |..| |...| continue |<-- nextop(2) :
  : +-+ +-+ | block | :
  : +----------+ :
  :......................................​:
  (optional)

  Fig. 1 A loop.

The above picture shows what a `while(){}' or `until(){}' loop looks
like to perl at runtime. The AND is a general purpose branch, the
ENTERLOOP and LEAVELOOP handle various bookkeeping things, the
NEXTSTATE keeps track of (among other things) perl's notion the
current line for error messages, and the UNSTACK deletes various kinds
of temporaries. (The dotted lines connect sections that can consist of
any number of OPs, like the body of the loop).

In theory, the body of a loop is its own block, so perl should
generate OPs corresponding to entering and leaving the block on each
iteration. Since (except at the very beginning and end) perl knows
it's leaving and re-entering the same block, however, it can avoid a
lot of popping and re-pushing. All that really has to be done is to
pop the savestack, restoring the old values of variables local()ized
in the block and clearing/destroying the values of my() variables
going out of scope, and this is what the UNSTACK does.

The `lastop', `redoop', and `nextop' show where the corresponding loop
exit operators jump to. `last' is pretty straightforward (actually, it
duplicates the work of the LEAVELOOP then jumps to the OP after it).
`redo' is also fairly simple -- it has to do the equivalent of an
UNSTACK, though, or else a loop that keeps redoing itself will leak a
bit of savestack on each time around.

`next' is the most complicated. In the current implementation, it
jumps to 1a, or 2 if there's a continue block, and does the same
effective-UNSTACK as redo. This is why the lexical comes up as undef
in the continue block -- it's been cleared by the effective-UNSTACK.

Note that the visibility and the lifetime of lexicals are subtly
different here​:

  Compiletime visibility
  +---------------------------------------------+
  | +------+ +---------------+
  | | | | |

while (condition) { body } continue { another block }

  | |
  +---------------------------------------------+
  Runtime lifetime

  Fig. 2 Scopes in a while

To the compiler, the loop has three different scopes (one each for the
blocks, and one for the whole loop), but the creation and destruction
of values at runtime only happens at one scope -- if you declare a
my() variable in the body, it won't be DESTROYed until the end of the
continue block, but you won't be able to get at it from there, because
any $a in the continue block would refer to the global $a (or some
other lexical $a).

So, how should this be fixed? I think the best thing to do would be to
make `next' not do an effective-UNSTACK, and move the destination of
`next' in the no-continue-block case to 1b in the above diagram, so
that the UNSTACK always gets executed. This has the additional benefit
of making errors in the condition of a while loop on an iteration
entered by `next' give the correct line number, since the NEXTSTATE is
now also in the path of execution.

Here's a patch (against 4_76, sorry) that does just that​:

Inline Patch
--- op.c.old	Sun Jul 26 20:16:44 1998
+++ op.c	Mon Jul 27 14:30:55 1998
@@ -2972,7 +2972,10 @@
     if (cont)
 	next = LINKLIST(cont);
     if (expr) {
-	cont = append_elem(OP_LINESEQ, cont, newOP(OP_UNSTACK, 0));
+	OP *unstack = newOP(OP_UNSTACK, 0);
+	if (!next)
+	    next = unstack;
+	cont = append_elem(OP_LINESEQ, cont, unstack);
 	if ((line_t)whileline != NOLINE) {
 	    PL_copline = whileline;
 	    cont = append_elem(OP_LINESEQ, cont,
@@ -2995,8 +2998,6 @@
 	if (listop)
 	    ((LISTOP*)listop)->op_last->op_next = condop =
 		(o == listop ? redo : LINKLIST(o));
-	if (!next)
-	    next = condop;
     }
     else
 	o = listop;
--- pp_ctl.c.old	Mon Jul 27 12:28:01 1998
+++ pp_ctl.c	Wed Jul 29 15:10:05 1998
@@ -1663,7 +1663,7 @@
 {
     I32 cxix;
     register PERL_CONTEXT *cx;
-    I32 oldsave;
+    I32 inner;
 
     if (PL_op->op_flags & OPf_SPECIAL) {
 	cxix = dopoptoloop(cxstack_ix);
@@ -1678,9 +1678,12 @@
     if (cxix < cxstack_ix)
 	dounwind(cxix);
 
+    /* Clear off anything above the scope we're re-entering, but
+       save the rest until after a possible continue block */ 
+    inner = PL_scopestack_ix;
     TOPBLOCK(cx);
-    oldsave = PL_scopestack[PL_scopestack_ix - 1];
-    LEAVE_SCOPE(oldsave);
+    if (PL_scopestack_ix < inner)
+	leave_scope(PL_scopestack[PL_scopestack_ix]);
     return cx->blk_loop.next_op;
 }
 
@@ -1703,6 +1706,7 @@
     if (cxix < cxstack_ix)
 	dounwind(cxix);
 
+    /* Restart the loop scope with a clean slate */
     TOPBLOCK(cx);
     oldsave = PL_scopestack[PL_scopestack_ix - 1];
     LEAVE_SCOPE(oldsave);

(Note that getting rid of the `effective-UNSTACK' is complicated by the way LEAVE\_SCOPE\(\) was also used for popping nested scopes\, but I think I've figured out the right way to do it\)\.

[end of archive message]

--
_____________________________________________________________________
Stephen McCamant ======================== smccam@​uclink4.berkeley.edu

@p5pRT
Copy link
Author

p5pRT commented Mar 29, 2000

From @gsar

On Tue, 28 Mar 2000 21​:06​:27 CST, Stephen McCamant wrote​:

Um, I think you still need to do some save-stack popping even if there
is a continue block, since dounwind() doesn't do any LEAVE_SCOPing,

I probably didn't realize it until well after change#4848, but this
issue did force me to add a kludge close to 5.6.0​: the one where
blk_loop keeps the correct PL_curpad around in order to look up the
right itervar (under ithreads).

And come to think of it, POPSUB() could be diddling the wrong PL_curpad
too because of this.

I think the most robust fix here would involve making dounwind() restore
at least PL_curpad.

but it may pass by any number of subs and foreach()es. For instance,
in 5.6.0​:

sub f { my $a = 1; my $b = 2; my $c = 3; my $d = 4; next }
my $x = "foo";
{ f } continue { print $x, "\n";}

Will print `1', and then if you add more my declarations before `my
$x' it will in turn print 2, 3, 4 and then undef a few times, and then
segfault, because $x is being looked up using f's value of PL_curpad,
which should have been un-saved when we next-ed out of f.

Indeed, thanks for the catch.

No, I don't have a patch, but I found the following message in my
archives which seems relevant; it includes an old patch that addresses
roughly the same problem (and which was never applied, AFAICT).

Don't know how that one slipped through the cracks, but this adaptation
of it to 5.6.0 seems to run the testsuite fine. I haven't tested it in
other ways, so that's probably not saying much.

Sarathy
gsar@​ActiveState.com

Inline Patch
-----------------------------------8<-----------------------------------
Index: perl/op.c
--- perl/op.c.~1~	Tue Mar 28 20:17:04 2000
+++ perl/op.c	Tue Mar 28 20:17:04 2000
@@ -3848,7 +3848,10 @@
 	loopflags |= OPpLOOP_CONTINUE;
     }
     if (expr) {
-	cont = append_elem(OP_LINESEQ, cont, newOP(OP_UNSTACK, 0));
+	OP *unstack = newOP(OP_UNSTACK, 0);
+	if (!next)
+	    next = unstack;
+	cont = append_elem(OP_LINESEQ, cont, unstack);
 	if ((line_t)whileline != NOLINE) {
 	    PL_copline = whileline;
 	    cont = append_elem(OP_LINESEQ, cont,
@@ -3871,8 +3874,6 @@
 	if (listop)
 	    ((LISTOP*)listop)->op_last->op_next = condop =
 		(o == listop ? redo : LINKLIST(o));
-	if (!next)
-	    next = condop;
     }
     else
 	o = listop;
Index: perl/pp_ctl.c
--- perl/pp_ctl.c.~1~	Tue Mar 28 20:17:04 2000
+++ perl/pp_ctl.c	Tue Mar 28 20:17:04 2000
@@ -1979,7 +1979,7 @@
 {
     I32 cxix;
     register PERL_CONTEXT *cx;
-    I32 oldsave;
+    I32 inner;
 
     if (PL_op->op_flags & OPf_SPECIAL) {
 	cxix = dopoptoloop(cxstack_ix);
@@ -1994,13 +1994,12 @@
     if (cxix < cxstack_ix)
 	dounwind(cxix);
 
+    /* clear off anything above the scope we're re-entering, but
+     * save the rest until after a possible continue block */
+    inner = PL_scopestack_ix;
     TOPBLOCK(cx);
-
-    /* clean scope, but only if there's no continue block */
-    if (!(cx->blk_loop.last_op->op_private & OPpLOOP_CONTINUE)) {
-	oldsave = PL_scopestack[PL_scopestack_ix - 1];
-	LEAVE_SCOPE(oldsave);
-    }
+    if (PL_scopestack_ix < inner)
+	leave_scope(PL_scopestack[PL_scopestack_ix]);
     return cx->blk_loop.next_op;
 }
 
End of Patch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant