zsh-workers
 help / color / mirror / code / Atom feed
* Oddball output from zerrmsg()
@ 2014-06-04  2:12 Bart Schaefer
  2014-06-05  5:37 ` [PATCH] " Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-04  2:12 UTC (permalink / raw)
  To: zsh-workers

There's a discussion on the POSIX mailing list about "break"/"continue" RE
whether they should affect only loops "visible" to the statement (can be
thought of as "local" scope) or also those in calling scope.  Shells differ
on this point.

That is, currently, given:

    foo() { print FOO; break 2 }
    bar() { repeat 2 foo; print BAR }
    baz() { repeat 2 bar }

The zsh behavior is:

    % foo
    FOO
    foo:break: not in while, until, select, or repeat loop
    % bar
    FOO
    BAR
    % baz
    FOO
    % 

Note that "break 2" has interupted the loops in both bar and baz, even
though neither of them was in the scope of foo, and has thereby short-
circuited the "print" in bar.

With the proposed semantics:

    % baz
    FOO
    foo:break: not in while, until, select, or repeat loop
    % 

If we edit foo just a little:

    foo() { repeat 2; do print FOO; break 3; done }

Then (with the proposed "break" semantics):

    % baz
    FOO
    FOO
    BAR
    FOO
    FOO
    BAR
    % 

Thus "break 3" has interrupted only the innermost loop, the other two
in bar and baz are considered out of scope and proceed normally.

I don't have an opinion on which of these is preferable, this is all
just background material.

Now for the strange part.  I applied a small patch to doshfunc() to
implement the proposed behavior and ran "make check".  I got exactly
one failure, and it's a false positive:

============
./A07control.ztst: starting.
*** /tmp/zsh.ztst.err.64460     Tue Jun  3 18:57:24 2014
--- /tmp/zsh.ztst.terr.64460    Tue Jun  3 18:57:24 2014
***************
*** 1 ****
! fn:continue:1 not in while, until, select, or repeat loop
--- 1 ----
! fn:continue:1: not in while, until, select, or repeat loop
Test ./A07control.ztst failed: error output differs from expected as shown
above for:
  fn() {
    continue
  }
  fn
Was testing: continue outside loop
./A07control.ztst: test failed.
============

What?  Where did the colon after the line number in the error message go?
Why has it not been there all along?  I don't see the code path by which
that colon would ever have been omitted, yet A07control.ztst clearly was
not prepared for it to be there.  Any ideas?


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

* [PATCH] Re: Oddball output from zerrmsg()
  2014-06-04  2:12 Oddball output from zerrmsg() Bart Schaefer
@ 2014-06-05  5:37 ` Bart Schaefer
  2014-06-05 15:53   ` break/continue vs. try-always Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-05  5:37 UTC (permalink / raw)
  To: zsh-workers

On Jun 3,  7:12pm, Bart Schaefer wrote:
}
} Now for the strange part.  I applied a small patch to doshfunc() to
} implement the proposed behavior and ran "make check".  I got exactly
} one failure

... which then caused additional tests that would have failed, never to
be attempted ...

} and it's a false positive:
} 
} ============
} ./A07control.ztst: starting.
} *** /tmp/zsh.ztst.err.64460     Tue Jun  3 18:57:24 2014
} --- /tmp/zsh.ztst.terr.64460    Tue Jun  3 18:57:24 2014
} ***************
} *** 1 ****
} ! fn:continue:1 not in while, until, select, or repeat loop
} --- 1 ----
} ! fn:continue:1: not in while, until, select, or repeat loop
} Test ./A07control.ztst failed: error output differs from expected as shown
} above for:
}   fn() {
}     continue
}   }
}   fn
} Was testing: continue outside loop
} ./A07control.ztst: test failed.
} ============
} 
} What?  Where did the colon after the line number in the error message go?
} Why has it not been there all along?  I don't see the code path by which
} that colon would ever have been omitted, yet A07control.ztst clearly was
} not prepared for it to be there.  Any ideas?

Tracked this down.

It turns out this test has never worked, because the "continue" propagates
up through the dynamic scopes and restarts the "while true;" loop way up
in ZTST_test itself, despite the "eval" in ZTST_execchunk; so nothing ever
has a chance to look at whether the actual stderr matches the expected.

Not even an "always" block can intercept this, which is probably a bug in
the handling of "always".

So it's not that the ":" is somehow missing from the output, it's that
the expected output has been wrong all along but is simply never seen.


diff --git a/Test/A07control.ztst b/Test/A07control.ztst
index b9b89b5..397a821 100644
--- a/Test/A07control.ztst
+++ b/Test/A07control.ztst
@@ -23,12 +23,12 @@
 >start 255
 >255
 
-  fn() {
+  $ZTST_testdir/../Src/zsh -fc 'fn() {
     continue
   }
-  fn
+  fn'
 1:continue outside loop
-?fn:continue:1 not in while, until, select, or repeat loop
+?fn:continue:1: not in while, until, select, or repeat loop
 
   for outer in 0 1 2 3; do
     print outer $outer


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

* break/continue vs. try-always
  2014-06-05  5:37 ` [PATCH] " Bart Schaefer
@ 2014-06-05 15:53   ` Bart Schaefer
  2014-06-06 20:58     ` Peter Stephenson
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-05 15:53 UTC (permalink / raw)
  To: zsh-workers

On Jun 4, 10:37pm, Bart Schaefer wrote:
} Subject: [PATCH] Re: Oddball output from zerrmsg()
}
} ... the "continue" propagates
} up through the dynamic scopes and restarts the "while true;" loop ...
} 
} Not even an "always" block can intercept this, which is probably a bug in
} the handling of "always".

OK, not a bug, exactly.  The always-block is in fact executed, but it has
no way to decrement the number of levels of "break" or "continue" that
have been set by the try-block.  It can *increase* the number of levels,
but not stop the break/continue from propagating upward.


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

* Re: break/continue vs. try-always
  2014-06-05 15:53   ` break/continue vs. try-always Bart Schaefer
@ 2014-06-06 20:58     ` Peter Stephenson
  2014-06-06 21:08       ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Stephenson @ 2014-06-06 20:58 UTC (permalink / raw)
  To: zsh-workers

On Thu, 05 Jun 2014 08:53:19 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Jun 4, 10:37pm, Bart Schaefer wrote:
> } Subject: [PATCH] Re: Oddball output from zerrmsg()
> }
> } ... the "continue" propagates
> } up through the dynamic scopes and restarts the "while true;" loop ...
> } 
> } Not even an "always" block can intercept this, which is probably a bug in
> } the handling of "always".
> 
> OK, not a bug, exactly.  The always-block is in fact executed, but it has
> no way to decrement the number of levels of "break" or "continue" that
> have been set by the try-block.  It can *increase* the number of levels,
> but not stop the break/continue from propagating upward.

We could do something like add "break -r" to reset.  It would only be
usable in always blocks (not sure about traps) because otherwise you
don't get the chance to execute it.

pws


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

* Re: break/continue vs. try-always
  2014-06-06 20:58     ` Peter Stephenson
@ 2014-06-06 21:08       ` Bart Schaefer
  2014-06-06 21:45         ` Peter Stephenson
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-06 21:08 UTC (permalink / raw)
  To: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 416 bytes --]

On Jun 6, 2014 2:04 PM, "Peter Stephenson" <p.w.stephenson@ntlworld.com>
wrote:
>
> We could do something like add "break -r" to reset.  It would only be
> usable in always blocks (not sure about traps) because otherwise you
> don't get the chance to execute it.

I was thinking perhaps "break 0" which is otherwise/currently an error.
Assuming the break handler can tell when it is in the scope of an always
block.

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

* Re: break/continue vs. try-always
  2014-06-06 21:08       ` Bart Schaefer
@ 2014-06-06 21:45         ` Peter Stephenson
  2014-06-07  6:22           ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Stephenson @ 2014-06-06 21:45 UTC (permalink / raw)
  To: zsh-workers

On Fri, 6 Jun 2014 14:08:53 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Jun 6, 2014 2:04 PM, "Peter Stephenson" <p.w.stephenson@ntlworld.com>
> wrote:
> >
> > We could do something like add "break -r" to reset.  It would only be
> > usable in always blocks (not sure about traps) because otherwise you
> > don't get the chance to execute it.
> 
> I was thinking perhaps "break 0" which is otherwise/currently an error.
> Assuming the break handler can tell when it is in the scope of an always
> block.

That's straightforward --- well, it isn't, but it's straightforward to
make it only work in always blocks and ignore it elsewhere which is
probably good enough since this is rather a specialised feature --- but
it occurred to me that for a complete sandbox it would be useful to have
"continue -r" and "return -r".  "continue 0" works but obviously "return
0" doesn't do that.  Actually, break -r (or break 0) would have a side
effect on continue because of the interaction between the "breaks" and
"contflag" variables, so arguably break -r and continue -r shouldn't be
independent however it's implemented.

pws


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

* Re: break/continue vs. try-always
  2014-06-06 21:45         ` Peter Stephenson
@ 2014-06-07  6:22           ` Bart Schaefer
  2014-06-08 17:54             ` Peter Stephenson
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-07  6:22 UTC (permalink / raw)
  To: zsh-workers

On Jun 6, 10:45pm, Peter Stephenson wrote:
}
} On Fri, 6 Jun 2014 14:08:53 -0700
} Bart Schaefer <schaefer@brasslantern.com> wrote:
} > On Jun 6, 2014 2:04 PM, "Peter Stephenson" <p.w.stephenson@ntlworld.com>
} > wrote:
} > 
} > I was thinking perhaps "break 0" which is otherwise/currently an error.
} 
} That's straightforward --- well, it isn't, but it's straightforward to
} make it only work in always blocks and ignore it elsewhere which is
} probably good enough since this is rather a specialised feature --- but
} it occurred to me that for a complete sandbox it would be useful to have
} "continue -r" and "return -r".

I don't understand what "return -r" would do.  Unlike "break 1000" for
example, and except for the weird case of 'trap return EXIT', "return"
can't affect anything beyond the local scope.  E.g., where one might
do

    outer() {
      while (($#)); do { inner $1; shift } always { break 0 }; done
    }

to prevent inner from stopping the loop in outer, I don't see any case
in which the always block would need to cancel a return.

} Actually, break -r (or break 0) would have a side
} effect on continue because of the interaction between the "breaks" and
} "contflag" variables, so arguably break -r and continue -r shouldn't be
} independent however it's implemented.

There may already be a conflict there, e.g.

  while print one; do
    while print two; do
      while print three; do
	  { break 2 } always { continue 3 }
      done
    done
  done

In fact if there are both a "break" and a "continue" in the try-always,
the effect is as if "continue" were called with the larger loop count,
no matter which of them appears in the try vs. the always blocks.

So we should probably define those semantics before we figure out what
a value of 0 (or whatever) means.


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

* Re: break/continue vs. try-always
  2014-06-07  6:22           ` Bart Schaefer
@ 2014-06-08 17:54             ` Peter Stephenson
  2014-06-08 18:41               ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Stephenson @ 2014-06-08 17:54 UTC (permalink / raw)
  To: zsh-workers

On Fri, 06 Jun 2014 23:22:50 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> I don't understand what "return -r" would do.

This would allow a sandbox inside a function; otherwise you couldn't
prevent the user skipping out early, and if you can't put a sandbox
inside a function the whole concept is degraded in a fairly major way.

I notice, however, that "return -r" is currently not an error.  That's
because it's "return $(( - $r ))", which is entirely valid for zsh (but
not other shells), even though a negative status isn't valid.  That
slightly gums up the normal option parsing in any case, but it looks
like it's long-standing behaviour:

% r=-1
% fn() { return -r; }
% fn
% print $?
1

So we need a syntax plan B.  Variables are possible --- we already do
stuff with TRY_BLOCK_ERROR, and the code I have working is likewise
specific to "always" blocks.

Anyway, actual functioning code with a change made before I noticed that:


% which fn
fn () {
        {
                print Before return.
                return
                print After return.
        } always {
                print In always block.
                return -r
        }
        print Evil plan by user to return early defeated.
}
% fn
Before return.
In always block.
Evil plan by user to return early defeated.


Hmm... along the lines of TRY_BLOCK_ERROR, and as negative numbers
aren't useful... how about... TRY_BLOCK_RETURN is by default negative,
but gets set to >= 0 if user signals a return; "always" block can set it
to some other value, one option would be to negative to prevent the return...?  This also means you can force a return from the end of the always block.


> } Actually, break -r (or break 0) would have a side
> } effect on continue because of the interaction between the "breaks" and
> } "contflag" variables, so arguably break -r and continue -r shouldn't be
> } independent however it's implemented.
> 
> There may already be a conflict there, e.g.
> 
>   while print one; do
>     while print two; do
>       while print three; do
> 	  { break 2 } always { continue 3 }
>       done
>     done
>   done
> 
> In fact if there are both a "break" and a "continue" in the try-always,
> the effect is as if "continue" were called with the larger loop count,
> no matter which of them appears in the try vs. the always blocks.
> 
> So we should probably define those semantics before we figure out what
> a value of 0 (or whatever) means.

Yes, indeed.  The minutiae of the standard may or may not be of some
help.

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


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

* Re: break/continue vs. try-always
  2014-06-08 17:54             ` Peter Stephenson
@ 2014-06-08 18:41               ` Bart Schaefer
  2014-06-08 19:43                 ` Peter Stephenson
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-08 18:41 UTC (permalink / raw)
  To: zsh-workers

On Jun 8,  6:54pm, Peter Stephenson wrote:
>
> On Fri, 06 Jun 2014 23:22:50 -0700
> Bart Schaefer <schaefer@brasslantern.com> wrote:
> > I don't understand what "return -r" would do.
> 
> This would allow a sandbox inside a function; otherwise you couldn't
> prevent the user skipping out early, and if you can't put a sandbox
> inside a function the whole concept is degraded in a fairly major way.

Sorry, I'm still not getting it.  The point of being able to reset a
break/continue is because you might have

 fn_that_I_wrote() {
    while some_test_of_mine;
    do {
          fn_somebody_else_wrote_that_calls_break_1000
       } always {
          print dammit there should be some way not to break here
       }
    done
  }

In the case of "return", there is no such situation, again unless you
are considering "trap return EXIT" in which case

    fn_that_I_wrote() {
       (){ douchebag_fn_that_calls_return_in_EXIT_trap } "$@"
    }

is already sufficient.  Which you can also use for a sandbox, no?  Under
what circumstances would I need to use try-always to cancel my own evil
return?

} So we need a syntax plan B.  Variables are possible --- we already do
} stuff with TRY_BLOCK_ERROR, and the code I have working is likewise
} specific to "always" blocks.

Yes, when I first encounterd this I was expecting TRY_BLOCK_ERROR to be
the solution, until I realized that continue was not an error.

} Hmm... along the lines of TRY_BLOCK_ERROR, and as negative numbers
} aren't useful... how about... TRY_BLOCK_RETURN is by default negative,
} but gets set to >= 0 if user signals a return; "always" block can set
} it to some other value, one option would be to negative to prevent the
} return...? This also means you can force a return from the end of the
} always block.

It would generally be useful to know how/why one arrived in the always
block, along the lines of emulating throw/catch ... but again you can
already force a return from the end of the always block with "return"
itself, so I'm not sure a return-specific variable is the right thing.

} > In fact if there are both a "break" and a "continue" in the try-always,
} > the effect is as if "continue" were called with the larger loop count,
} > no matter which of them appears in the try vs. the always blocks.
} 
} Yes, indeed.  The minutiae of the standard may or may not be of some
} help.

It might, but I sort of doubt it, as try-always is not standard; there
is no standard way to switch from breaking to continuing.

I suppose whichever of them is going to affect the larger number of
loops should win.  E.g. if you do "continue 4" while 6 levels deep
and then two levels up (so effectively at "continue 2") the always
block asserts "break 3" then break wins.  Ties ... go to whichever
was called most recently?


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

* Re: break/continue vs. try-always
  2014-06-08 18:41               ` Bart Schaefer
@ 2014-06-08 19:43                 ` Peter Stephenson
  2014-06-08 20:29                   ` Peter Stephenson
  2014-06-08 21:01                   ` Bart Schaefer
  0 siblings, 2 replies; 17+ messages in thread
From: Peter Stephenson @ 2014-06-08 19:43 UTC (permalink / raw)
  To: zsh-workers

On Sun, 08 Jun 2014 11:41:23 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> Sorry, I'm still not getting it.
>...
> Under what circumstances would I need to use try-always to cancel
> my own evil return?

I'm not really getting you're not getting it.  Why does it have to be
your own return?  Isn't the point of a sandbox to prevent something you
*don't* control?  That's the case in the test code:  what triggered this
thread was a "continue" that's causing a continue outside the scope of
the test.  I don't see why you should force people to create a new
function scope to do this when you don't do that for break and continue;
they should be consistent.

However, what *I'm* missing is that break and continue propagate outside
a function --- so you can't trap them that way anyway.  That seems to me
a straight bug, since it implies a fairly groteseque intermingling of
multiple levels, but it does happen in other shells, too, so evidently
we have to treat it as a feature.

If you like the idea of function scope as a sandbox, it seems me that a
neater way to handle this would be to add an option to force break and
continue to respect function scope.  Then no new syntax is required.
Provide you define that the new option (call it localloops for now) is
applied in the outer function on return from the inner, you can do
everything with


setopt localoptions localloops
() {
   # call user code
}
# localloops is restored on return here and used to cancel breaks /
# contflag before resuming user code at this point.


e basta.  That's also very simple to implement, with no new globals or
variables, and the only namespace affected is one we have complete
control over.  Furthermore you can set it once at the top of your code
and the affect propagates as far down as you need it: you could
explicitly unset it in the sandbox function if you wanted to provide a
100% pristine environment, but otherwise you only need to modify
existing code at one point.  I think to get this to be robust localloops
would have to be defined as one of the few options, like localoptions,
that always get restored on return from a function.

Of course the surrounding code doesn't actually have to be in a function
scope, though I set localoptions assuming it was; only the inner scope
is important.

pws


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

* Re: break/continue vs. try-always
  2014-06-08 19:43                 ` Peter Stephenson
@ 2014-06-08 20:29                   ` Peter Stephenson
  2014-06-08 21:01                   ` Bart Schaefer
  1 sibling, 0 replies; 17+ messages in thread
From: Peter Stephenson @ 2014-06-08 20:29 UTC (permalink / raw)
  To: zsh-workers

On Sun, 8 Jun 2014 20:43:58 +0100
Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> If you like the idea of function scope as a sandbox, it seems me that a
> neater way to handle this would be to add an option to force break and
> continue to respect function scope.  Then no new syntax is required.
> Provide you define that the new option (call it localloops for now) is
> applied in the outer function on return from the inner, you can do
> everything with
> 
> 
> setopt localoptions localloops
> () {
>    # call user code
> }
> # localloops is restored on return here and used to cancel breaks /
> # contflag before resuming user code at this point.

A further thought is we could even flag an error if there was a break or
continue pending at this point, since evidently the user tried to do
something they shouldn't.  Then we could get the continue-outside-a-loop
test to work, thought it wouldn't quite be testing the code it was meant
to test.

pws


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

* Re: break/continue vs. try-always
  2014-06-08 19:43                 ` Peter Stephenson
  2014-06-08 20:29                   ` Peter Stephenson
@ 2014-06-08 21:01                   ` Bart Schaefer
  2014-06-08 21:50                     ` Peter Stephenson
  1 sibling, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-08 21:01 UTC (permalink / raw)
  To: zsh-workers

On Jun 8,  8:43pm, Peter Stephenson wrote:
} Subject: Re: break/continue vs. try-always
}
} On Sun, 08 Jun 2014 11:41:23 -0700
} Bart Schaefer <schaefer@brasslantern.com> wrote:
} > Sorry, I'm still not getting it.
} >...
} > Under what circumstances would I need to use try-always to cancel
} > my own evil return?
} 
} I'm not really getting you're not getting it.  Why does it have to be
} your own return? Isn't the point of a sandbox to prevent something you
} *don't* control?

Because return respects function scopes, there are extremely limited
circumstances under which it would not be my own return.  I'm trying to
understand exactly when you feel a return would not be under the control
of the same person who is writing the function.

} However, what *I'm* missing is that break and continue propagate outside
} a function --- so you can't trap them that way anyway.

This and exactly this is the only problem that I'm trying to solve with
this thread, which is probably how we got out of sync.

} If you like the idea of function scope as a sandbox, it seems me that a
} neater way to handle this would be to add an option to force break and
} continue to respect function scope.

The problem with that solution is that it propagates downward -- it's
the inverse of break/continue propagating upward.  There may be layers
of function scope in between the caller and the eventual break/continue
that expect the current dynamic-scope behavior.

} setopt localoptions localloops
} () {
}    # call user code
} }
} # localloops is restored on return here and used to cancel breaks /
} # contflag before resuming user code at this point.
} 
} e basta.  That's also very simple to implement, with no new globals or
} variables, and the only namespace affected is one we have complete
} control over.

Yes, in fact I already implemented it (see the background discussion in
workers/32693) but I don't like it much, for the reason above.


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

* Re: break/continue vs. try-always
  2014-06-08 21:01                   ` Bart Schaefer
@ 2014-06-08 21:50                     ` Peter Stephenson
  2014-06-09  2:11                       ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Stephenson @ 2014-06-08 21:50 UTC (permalink / raw)
  To: zsh-workers

On Sun, 08 Jun 2014 14:01:46 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> } If you like the idea of function scope as a sandbox, it seems me that a
> } neater way to handle this would be to add an option to force break and
> } continue to respect function scope.
> 
> The problem with that solution is that it propagates downward -- it's
> the inverse of break/continue propagating upward.  There may be layers
> of function scope in between the caller and the eventual break/continue
> that expect the current dynamic-scope behavior.

This is why I pointed out you could do

 
setopt localoptions localloops
() {
   setopt nolocalloops # or emulation or whatever
   # call user code
}
# localloops is restored on return here and used to cancel breaks /
# contflag before resuming user code at this point.


because the restore behaviour is in the caller.  It doesn't seem to me
any different from the fact that any option at all could be misset for a
particular type of function, so the function needs to set them up with
an emulation.

I wouldn't expect anything more than a very exceptional wrapper to break
and continue to rely on that behaviour.

pws


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

* Re: break/continue vs. try-always
  2014-06-08 21:50                     ` Peter Stephenson
@ 2014-06-09  2:11                       ` Bart Schaefer
  2014-06-12 19:35                         ` Peter Stephenson
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-09  2:11 UTC (permalink / raw)
  To: zsh-workers

On Jun 8, 10:50pm, Peter Stephenson wrote:
} Subject: Re: break/continue vs. try-always
}
} On Sun, 08 Jun 2014 14:01:46 -0700
} Bart Schaefer <schaefer@brasslantern.com> wrote:
} > } neater way to handle this would be to add an option to force break and
} > } continue to respect function scope.
} > 
} > The problem with that solution is that it propagates downward
} 
} This is why I pointed out you could do
}  
} setopt localoptions localloops
} () {
}    setopt nolocalloops # or emulation or whatever
}    # call user code
} }
} # localloops is restored on return here and used to cancel breaks /
} # contflag before resuming user code at this point.

Ah, you didn't have the "setopt nolocalloops" in the anonymous scope
the last time.   OK, this is fine, and it sets up up well in the event
that the austin-group decides to impose this semantics for Issue 8
(though it looks like they're going to make it unspecified instead).

-- 
Barton E. Schaefer


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

* Re: break/continue vs. try-always
  2014-06-09  2:11                       ` Bart Schaefer
@ 2014-06-12 19:35                         ` Peter Stephenson
  2014-06-13  6:57                           ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Stephenson @ 2014-06-12 19:35 UTC (permalink / raw)
  To: zsh-workers

On Sun, 08 Jun 2014 19:11:15 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Jun 8, 10:50pm, Peter Stephenson wrote:
> } Subject: Re: break/continue vs. try-always
> }
> } On Sun, 08 Jun 2014 14:01:46 -0700
> } Bart Schaefer <schaefer@brasslantern.com> wrote:
> } > } neater way to handle this would be to add an option to force break and
> } > } continue to respect function scope.
> } > 
> } > The problem with that solution is that it propagates downward
> } 
> } This is why I pointed out you could do
> }  
> } setopt localoptions localloops
> } () {
> }    setopt nolocalloops # or emulation or whatever
> }    # call user code
> } }
> } # localloops is restored on return here and used to cancel breaks /
> } # contflag before resuming user code at this point.
> 
> Ah, you didn't have the "setopt nolocalloops" in the anonymous scope
> the last time.   OK, this is fine, and it sets up up well in the event
> that the austin-group decides to impose this semantics for Issue 8
> (though it looks like they're going to make it unspecified instead).

This makes a break or continue at the end of a function produce a
warning.  It didn't seem worth a hard error, but presumably a break or
continue is usually intended to do something so it should be reported if
it doesn't.

diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 7788cd7..31247f5 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -1631,6 +1631,19 @@ tt(FUNCTION_ARGZERO) from on to off (or off to on) does not change the
 current value of tt($0).  Only the state upon entry to the function or
 script has an effect.  Compare tt(POSIX_ARGZERO).
 )
+pindex(LOCAL_LOOPS)
+pindex(NO_LOCAL_LOOPS)
+pindex(LOCALLOOPS)
+pindex(NOLOCALLOOPS)
+cindex(break, inside function)
+cindex(continue, inside function)
+cinde(function, scope of break and continue)
+item(tt(LOCAL_LOOPS))(
+When this option is not set, the effect of tt(break) and tt(continue)
+commands may propagate outside function scope, affecting loops in
+calling functions.  When this option is not set, a tt(break) or
+a tt(continue) that is not caught within a function produces a warning.
+)
 pindex(LOCAL_OPTIONS)
 pindex(NO_LOCAL_OPTIONS)
 pindex(LOCALOPTIONS)
diff --git a/Src/exec.c b/Src/exec.c
index 8249def..a5982a7 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -4814,6 +4814,15 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	opts[XTRACE] = saveopts[XTRACE];
 	opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
 	opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
+	opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
+    }
+
+    if (opts[LOCALLOOPS]) {
+	if (contflag)
+	    zwarn("`continue' active at end of function scope");
+	if (breaks)
+	    zwarn("`break' active at end of function scope");
+	contflag = breaks = 0;
     }
 
     endtrapscope();
diff --git a/Src/options.c b/Src/options.c
index 2163bff..6e4e7b9 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -180,6 +180,7 @@ static struct optname optns[] = {
 {{NULL, "listrowsfirst",      0},			 LISTROWSFIRST},
 {{NULL, "listtypes",	      OPT_ALL},			 LISTTYPES},
 {{NULL, "localoptions",	      OPT_EMULATE|OPT_KSH},	 LOCALOPTIONS},
+{{NULL, "localloops",	      OPT_EMULATE},		 LOCALLOOPS},
 {{NULL, "localpatterns",      OPT_EMULATE},		 LOCALPATTERNS},
 {{NULL, "localtraps",	      OPT_EMULATE|OPT_KSH},	 LOCALTRAPS},
 {{NULL, "login",	      OPT_SPECIAL},		 LOGINSHELL},
diff --git a/Src/zsh.h b/Src/zsh.h
index 05d582c..fa73961 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2129,6 +2129,7 @@ enum {
     LISTPACKED,
     LISTROWSFIRST,
     LISTTYPES,
+    LOCALLOOPS,
     LOCALOPTIONS,
     LOCALPATTERNS,
     LOCALTRAPS,
diff --git a/Test/E01options.ztst b/Test/E01options.ztst
index d9f2191..46b1837 100644
--- a/Test/E01options.ztst
+++ b/Test/E01options.ztst
@@ -430,7 +430,7 @@
   foo
   unfunction foo
 0:FUNCTION_ARGZERO option
->My name is ZTST_execchunk
+>My name is (anon)
 >My name is foo
 
   setopt _NO_glob_
@@ -1114,3 +1114,45 @@
 >1
 >1
 >2
+
+  for (( i = 0; i < 10; i++ )); do
+     () {
+        print $i
+        break
+     }
+  done
+0:NO_LOCAL_LOOPS
+>0
+
+  () {
+      emulate -L zsh
+      setopt localloops
+      for (( i = 0; i < 10; i++ )); do
+	  () {
+              setopt nolocalloops # ignored in parent
+              print $i
+              break
+	  }
+      done
+  }
+0:LOCAL_LOOPS
+>0
+>1
+>2
+>3
+>4
+>5
+>6
+>7
+>8
+>9
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
diff --git a/Test/ztst.zsh b/Test/ztst.zsh
index 745a13c..74111f6 100755
--- a/Test/ztst.zsh
+++ b/Test/ztst.zsh
@@ -260,8 +260,12 @@ $ZTST_redir"
 # Execute an indented chunk.  Redirections will already have
 # been set up, but we need to handle the options.
 ZTST_execchunk() {
+  setopt localloops # don't let continue & break propagate out
   options=($ZTST_testopts)
-  eval "$ZTST_code"
+  () {
+      unsetopt localloops
+      eval "$ZTST_code"
+  }
   ZTST_status=$?
   # careful... ksh_arrays may be in effect.
   ZTST_testopts=(${(kv)options[*]})

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


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

* Re: break/continue vs. try-always
  2014-06-12 19:35                         ` Peter Stephenson
@ 2014-06-13  6:57                           ` Bart Schaefer
  2014-06-13  9:55                             ` Peter Stephenson
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2014-06-13  6:57 UTC (permalink / raw)
  To: zsh-workers

On Jun 12,  8:35pm, Peter Stephenson wrote:
}
} This makes a break or continue at the end of a function produce a
} warning.  It didn't seem worth a hard error, but presumably a break or
} continue is usually intended to do something so it should be reported if
} it doesn't.

Hrm.  That makes it sort of like WARN_CREATE_GLOBAL.  I can think of
cases where you want the warning, and cases where you don't ... the
situation that got us to this point is one of latter.

However ...
 
} +When this option is not set, the effect of tt(break) and tt(continue)
} +commands may propagate outside function scope, affecting loops in
} +calling functions.  When this option is not set, a tt(break) or
} +a tt(continue) that is not caught within a function produces a warning.

Typo, extra "not" in the last sentence.

} +	opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
} +    }
} +
} +    if (opts[LOCALLOOPS]) {
} +	if (contflag)
} +	    zwarn("`continue' active at end of function scope");
} +	if (breaks)
} +	    zwarn("`break' active at end of function scope");
} +	contflag = breaks = 0;

Since breaks is saved as obreaks on entry to doshfunc, shouldn't this be

	breaks = obreaks;

Also, probably need to save/restore contflag and loops?  In case the
function is called from a trap handler, for example?


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

* Re: break/continue vs. try-always
  2014-06-13  6:57                           ` Bart Schaefer
@ 2014-06-13  9:55                             ` Peter Stephenson
  0 siblings, 0 replies; 17+ messages in thread
From: Peter Stephenson @ 2014-06-13  9:55 UTC (permalink / raw)
  To: zsh-workers

On Thu, 12 Jun 2014 23:57:57 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:

> On Jun 12,  8:35pm, Peter Stephenson wrote:
> }
> } This makes a break or continue at the end of a function produce a
> } warning.  It didn't seem worth a hard error, but presumably a break or
> } continue is usually intended to do something so it should be reported if
> } it doesn't.
> 
> Hrm.  That makes it sort of like WARN_CREATE_GLOBAL.  I can think of
> cases where you want the warning, and cases where you don't ... the
> situation that got us to this point is one of latter.

I agree there's no obvious single right answer.  In the test code, we
can handle the warning when it appears, however: it's inside any
redirection of test output.

> } +When this option is not set, the effect of tt(break) and tt(continue)
> } +commands may propagate outside function scope, affecting loops in
> } +calling functions.  When this option is not set, a tt(break) or
> } +a tt(continue) that is not caught within a function produces a warning.
> 
> Typo, extra "not" in the last sentence.

Yes, and I improved the documentation after I'd sent the patch to make it
clearer what happens where.

> } +	opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
> } +    }
> } +
> } +    if (opts[LOCALLOOPS]) {
> } +	if (contflag)
> } +	    zwarn("`continue' active at end of function scope");
> } +	if (breaks)
> } +	    zwarn("`break' active at end of function scope");
> } +	contflag = breaks = 0;
> 
> Since breaks is saved as obreaks on entry to doshfunc, shouldn't this be
> 
> 	breaks = obreaks;
> 
> Also, probably need to save/restore contflag and loops?  In case the
> function is called from a trap handler, for example?

As this is a new feature anyway, there's no reason we shouldn't do
that.  I couldn't think of a case where the continue or break wouldn't
already have been dealt with if they were set; traps do the saving and
restoring of stuff elsewhere.  But for a couple of extra variables it seems
a reasonable piece of safety.

pws


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

end of thread, other threads:[~2014-06-13 10:05 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-06-04  2:12 Oddball output from zerrmsg() Bart Schaefer
2014-06-05  5:37 ` [PATCH] " Bart Schaefer
2014-06-05 15:53   ` break/continue vs. try-always Bart Schaefer
2014-06-06 20:58     ` Peter Stephenson
2014-06-06 21:08       ` Bart Schaefer
2014-06-06 21:45         ` Peter Stephenson
2014-06-07  6:22           ` Bart Schaefer
2014-06-08 17:54             ` Peter Stephenson
2014-06-08 18:41               ` Bart Schaefer
2014-06-08 19:43                 ` Peter Stephenson
2014-06-08 20:29                   ` Peter Stephenson
2014-06-08 21:01                   ` Bart Schaefer
2014-06-08 21:50                     ` Peter Stephenson
2014-06-09  2:11                       ` Bart Schaefer
2014-06-12 19:35                         ` Peter Stephenson
2014-06-13  6:57                           ` Bart Schaefer
2014-06-13  9:55                             ` Peter Stephenson

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).