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