Note that just adding braces is enough to trigger the bug:

     $ zsh -e -c 'true && false; echo NOT REACHED'     # no output; correct
     $ zsh -e -c 'true && {false; echo NOT REACHED}'  # incorrect output
     NOT REACHED

and

     $ zsh -c 'trap "echo Trapped!" ERR; true && false'     # correct output
     Trapped!
     $ zsh -c 'trap "echo Trapped!" ERR; true && {false}'  # no output; bug

I think that the correct fix is the following:

    if (isandor || isnot)
        noerrexit = oldnoerrexit | NOERREXIT_EXIT | NOERREXIT_RETURN;
    else
        noerrexit = oldnoerrexit;

For reminder, here is the current code:

    if (isandor || isnot)
        noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;


In other words, every sub-command in a list should only depend on the noerrexit that was in effect at the start of the evaluation of the list. It should in no way depend on the noerrexit that results from the evaluation of proceeding sub-commands as is currently the case. The current code only works for lists where the last sub-command is a simple command, thanks to the "noerrexit = oldnoerrexit;" performed after the last sub-command just before checking whether an ERREXIT should be triggered.

Here are two other examples fixed by this patch:

    zsh -c 'trap "echo Trapped!" ERR; true && if true; then false; fi' 
    zsh -c 'trap "echo Trapped!" ERR; true && {false} always {true}' 

Philippe




On Tue, Jun 25, 2024 at 1:03 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
On Fri, Jun 21, 2024 at 10:50 PM Lawrence Velázquez <larryv@zsh.org> wrote:
>
> >    set -e; true && { false; echo one; } || echo two
> >
> > Is "false" there considered to be "any command of an AND-OR list" ?
>
> This ought to work the same as the subshell version: early exit is
> suppressed for the commands "true" and "{ false; echo one; }", so
> the overall output is still "one".

The following seems to fix all six of the mentioned cases from this
thread, then.