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