Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Bug (?) with "return" from inside "while"



On Nov 5,  2:00pm, Bart Schaefer wrote:
}
} The change for [execwhile()] is less obvious than with execif(),

As best I can tell, the exit value of a "while" loop is supposed to
be the exit value of the last executed statement in the loop-body,
but the value of $? on entry to the loop body is supposed to be the
value of the last executed statement in the condition.

Thus

    i=0
    while (( i++ < 1 )); do echo $?; (return 27); done
    echo $?

should echo

    0
    27

And

    i=0
    until (( i++ == 1 )); do echo $?; (return 27); done
    echo $?

should echo

    1
    27

The question is what the exit status should be if the conditional
includes an explicit "return N".  dash and zsh up to this point
return the exit status of the loop body, or zero if the loop body
has never executed.  Bash returns N, which seems more correct.

A related question is what happens if the loop is interrupted with
^C.  The most recent changes to execwhile() are all about handling
that (plus making the interrupt possible if the condition or body
or both are empty).  Is an interrupted condition the same as one
that returned false (or true in the case of until)?  This somewhat
obscures what is intended in the case where there is no interrupt.

There's also the question of what $? should be upon entry to the
condition.  On the first loop it's unchanged from the previous
command, but on successive loops it's the value from the loop body.
On the other hand the value of $? on entry to the loop body is the
value from the condition (always 0 for while, nonzero for until).
(Patch below doesn't address this at all.)

I think the following is the minimum sensible change but there may
be corner cases that it's not covering.

diff --git a/Src/loop.c b/Src/loop.c
index f65c72b..367c0df 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -439,13 +439,12 @@ execwhile(Estate state, UNUSED(int do_exec))
             if (!((lastval == 0) ^ isuntil)) {
                 if (breaks)
                     breaks--;
-                lastval = oldval;
+		if (!retflag)
+		    lastval = oldval;
                 break;
             }
-            if (retflag) {
-                lastval = oldval;
+            if (retflag)
                 break;
-            }
 
 	    /* In case the loop body is also a functional no-op,
 	     * make sure signal handlers recognize ^C as above. */



Messages sorted by: Reverse Date, Date, Thread, Author