zsh-workers
 help / color / mirror / code / Atom feed
* "break" and functions
@ 2024-03-29  1:54 Bart Schaefer
  2024-03-29  9:30 ` Mikael Magnusson
  2024-03-29 17:38 ` Oliver Kiddle
  0 siblings, 2 replies; 8+ messages in thread
From: Bart Schaefer @ 2024-03-29  1:54 UTC (permalink / raw)
  To: Zsh hackers list

On Wed, Mar 27, 2024 at 3:22 PM Oliver Kiddle <opk@zsh.org> wrote:
>
> Perhaps worth including in a test case is the following which does
> break after running the echo.
>   while :; do; echo ${|REPLY=x;break}; done

When adding this, I ran into a puzzling effect:

% while :; do; echo ${|REPLY=x;break}; done
x
% while :; do; () { echo "$@" } ${|REPLY=x;break}; done
%

That is, breaking the loop skips the function call but does not skip
the builtin.

I think something like this has been mentioned before in different
context, but it's not easy to search for.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "break" and functions
  2024-03-29  1:54 "break" and functions Bart Schaefer
@ 2024-03-29  9:30 ` Mikael Magnusson
  2024-03-29 16:28   ` Bart Schaefer
  2024-03-29 17:38 ` Oliver Kiddle
  1 sibling, 1 reply; 8+ messages in thread
From: Mikael Magnusson @ 2024-03-29  9:30 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

On Fri, Mar 29, 2024 at 2:55 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> On Wed, Mar 27, 2024 at 3:22 PM Oliver Kiddle <opk@zsh.org> wrote:
> >
> > Perhaps worth including in a test case is the following which does
> > break after running the echo.
> >   while :; do; echo ${|REPLY=x;break}; done
>
> When adding this, I ran into a puzzling effect:
>
> % while :; do; echo ${|REPLY=x;break}; done
> x
> % while :; do; () { echo "$@" } ${|REPLY=x;break}; done
> %
>
> That is, breaking the loop skips the function call but does not skip
> the builtin.
>
> I think something like this has been mentioned before in different
> context, but it's not easy to search for.

51608

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "break" and functions
  2024-03-29  9:30 ` Mikael Magnusson
@ 2024-03-29 16:28   ` Bart Schaefer
  2024-03-29 16:56     ` Bart Schaefer
  0 siblings, 1 reply; 8+ messages in thread
From: Bart Schaefer @ 2024-03-29 16:28 UTC (permalink / raw)
  To: Zsh hackers list

On Fri, Mar 29, 2024 at 2:30 AM Mikael Magnusson <mikachu@gmail.com> wrote:
>
> On Fri, Mar 29, 2024 at 2:55 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
> >
> > That is, breaking the loop skips the function call but does not skip
> > the builtin.
>
> 51608

Hmm, sort of, but I meant specifically the difference in behavior
between builtin and function, and 51608 is a fix whereas this behavior
remains.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "break" and functions
  2024-03-29 16:28   ` Bart Schaefer
@ 2024-03-29 16:56     ` Bart Schaefer
  0 siblings, 0 replies; 8+ messages in thread
From: Bart Schaefer @ 2024-03-29 16:56 UTC (permalink / raw)
  To: Zsh hackers list

On Fri, Mar 29, 2024 at 9:28 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> I meant specifically the difference in behavior
> between builtin and function

Found it:  52151, Bug: function calls with break in preexec

In exactly this same context, in fact:  Adding nofork tests.  No one
responded, that time.

On Sat, Sep 16, 2023 at 8:08 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> [...] "set -x" shows the function call being made, but it
> immediately stops without executing any commands.
>
> One of these cases has to be wrong:  Either the loop should break
> immediately, so neither the builtin nor the function runs, or the
> function should run and then the loop should break.  Or more
> radically, a break occurring in the argument expansions should just be
> discarded, but that seems wrong too.  I think it's the former:  The
> builtin shouldn't run in this case.

I suspect this comes down to "breaks" being tested at the top of
execpline2(), which is called when entering a function but not when
already committed to running a builtin.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "break" and functions
  2024-03-29  1:54 "break" and functions Bart Schaefer
  2024-03-29  9:30 ` Mikael Magnusson
@ 2024-03-29 17:38 ` Oliver Kiddle
  2024-03-29 18:51   ` Bart Schaefer
  1 sibling, 1 reply; 8+ messages in thread
From: Oliver Kiddle @ 2024-03-29 17:38 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

Bart Schaefer wrote:
> When adding this, I ran into a puzzling effect:
>
> % while :; do; echo ${|REPLY=x;break}; done
> x
> % while :; do; () { echo "$@" } ${|REPLY=x;break}; done
> %
>
> That is, breaking the loop skips the function call but does not skip
> the builtin.

My expectation would have been for either an error message saying
  break: not in while, until, select, or repeat loop`
or that because the break is run before the builtin that the builtin
would never get to run. But the implementation of break is just putting
a value in the breaks integer variable so I guess that variable isn't
checked until after the builtin is run. The anonymous function is
perhaps a two-stage process for defining and then running. Note:
  while :; do; () { echo hello } > ${|REPLY=x;break}; done
creates an empty file named x.

When suggesting the test, the exact behaviour was not something I cared
strongly about provided it wasn't seg fault, infinite loop or similar.

In the case of
  repeat 3 print c*(oe:'break':)
it might be a useful feature if break applied to the internal loop over
globbed files causing print to run but only with those files that were
matched so far. false works as a continue in that case.

If you're choosing which of the two cases in 52151 is wrong,
I'd say that with the purr() function is right because the break is run
before the print (or purr).
And possibly, it should be an error message for "not in a while, ..."

It also rather surprises me that the break here even works:

  br() { break }
  while : ; do
    br
  done

Oliver


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "break" and functions
  2024-03-29 17:38 ` Oliver Kiddle
@ 2024-03-29 18:51   ` Bart Schaefer
  2024-03-29 21:14     ` Mikael Magnusson
  0 siblings, 1 reply; 8+ messages in thread
From: Bart Schaefer @ 2024-03-29 18:51 UTC (permalink / raw)
  To: Zsh hackers list

On Fri, Mar 29, 2024 at 10:38 AM Oliver Kiddle <opk@zsh.org> wrote:
>
> My expectation would have been for either an error message saying
>   break: not in while, until, select, or repeat loop`
> or that because the break is run before the builtin that the builtin
> would never get to run.

The latter was my expectation; I think the break is inside the loop.

Skipping ahead:
> It also rather surprises me that the break here even works:
>
>   br() { break }
>   while : ; do
>     br
>   done

% setopt localloops
% br() { break }
% repeat 3 br
zsh: `break' active at end of function scope
zsh: `break' active at end of function scope
zsh: `break' active at end of function scope
% unsetopt localloops
% repeat 3 br
%

> But the implementation of break is just putting
> a value in the breaks integer variable so I guess that variable isn't
> checked until after the builtin is run.

Right; since the break occurs in prefork() we're already deeper in the
execXXX() call stack than anywhere the variable is checked.  The
function call re-enters that stack closer to the top, and finds the
setting.

> The anonymous function is
> perhaps a two-stage process for defining and then running. Note:
>   while :; do; () { echo hello } > ${|REPLY=x;break}; done
> creates an empty file named x.

Again, prefork() ... the redirections also occur before breaks is
checked.  Although this:
  echo xx > ${|REPLY=x;exit}
does bail out before completing the redirect, whereas this:
  echo xx > ${|REPLY=x;return}
does not.

> When suggesting the test, the exact behaviour was not something I cared
> strongly about provided it wasn't seg fault, infinite loop or similar.

Yes, I understand.  I considered testing this way for future-proofing:

repeat 3 V=${|REPLY=x; break}
print $V

I ended up deciding that it was reasonable to leave the "echo ${|...}"
as an ad-hoc regression test in the event builtins do start
"break"-ing.

> In the case of
>   repeat 3 print c*(oe:'break':)
> it might be a useful feature if break applied to the internal loop over
> globbed files causing print to run but only with those files that were
> matched so far.

IIRC the entire glob loop finishes and then any (e) actions are
applied to the resulting list.  So "matched so far" is not well
defined.  I believe the only way to stop the internal loop "early" is
the (Y) qualifier.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "break" and functions
  2024-03-29 18:51   ` Bart Schaefer
@ 2024-03-29 21:14     ` Mikael Magnusson
  2024-03-29 22:02       ` Bart Schaefer
  0 siblings, 1 reply; 8+ messages in thread
From: Mikael Magnusson @ 2024-03-29 21:14 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

On Fri, Mar 29, 2024 at 7:51 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
[only replying to this last part]
> > In the case of
> >   repeat 3 print c*(oe:'break':)
> > it might be a useful feature if break applied to the internal loop over
> > globbed files causing print to run but only with those files that were
> > matched so far.
>
> IIRC the entire glob loop finishes and then any (e) actions are
> applied to the resulting list.  So "matched so far" is not well
> defined.  I believe the only way to stop the internal loop "early" is
> the (Y) qualifier.

This is not the case, eg
% ls /**/*(e:exit:)
returns instantly, while this doesn't
% ls /**/*(e:true:)

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "break" and functions
  2024-03-29 21:14     ` Mikael Magnusson
@ 2024-03-29 22:02       ` Bart Schaefer
  0 siblings, 0 replies; 8+ messages in thread
From: Bart Schaefer @ 2024-03-29 22:02 UTC (permalink / raw)
  To: Zsh hackers list

On Fri, Mar 29, 2024 at 2:15 PM Mikael Magnusson <mikachu@gmail.com> wrote:
>
> On Fri, Mar 29, 2024 at 7:51 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
> > IIRC the entire glob loop finishes and then any (e) actions are
> > applied to the resulting list.
>
> This is not the case

Ah, OK.  I was thinking of order of operations.  E.g. this:

print -l **/*(@e:'x+=($REPLY)':)

is not the same as this:

print -l **/*(e:'x+=($REPLY)':@)


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2024-03-29 22:03 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-29  1:54 "break" and functions Bart Schaefer
2024-03-29  9:30 ` Mikael Magnusson
2024-03-29 16:28   ` Bart Schaefer
2024-03-29 16:56     ` Bart Schaefer
2024-03-29 17:38 ` Oliver Kiddle
2024-03-29 18:51   ` Bart Schaefer
2024-03-29 21:14     ` Mikael Magnusson
2024-03-29 22:02       ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).