-
-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Setting a variable in a 'temp binding' sets a global instead #329
Comments
It's definitely possible there's a bug here, but can you tell me what bash does? Lines like this frequently lead to confusing (but technically correct) behavior:
|
Here's a dump from bash (which is the same as zsh, dash, and my own shell, smoosh):
The only other behavior I've observed is the yash/ksh/mksh one:
|
I can narrow it down for you a little: it's not about nested scopes, but it is about functions and assignments (a very confusing and tricky part of the spec!). f() { echo "initial $x"; x=$((x+1)); echo "after $x"; }
x=5 f In osh yields:
While bash yields:
My guess is that somehow the logic in assignments is unable to update the (quasi-)local assignment from the function call. |
Thanks, I reproduced it. That is a pretty serious bug! I'm still looking into it, but a "git blame" led me to this commit: (The commit description looks like it has a typo -- it should read "are now visible" rather than "are not visible".) It looks like I was trying to fix some relatively subtle behavior, but this more obvious behavior was broken. I will look into this, but have you seen the HTML spec test reports for Oil? There are now 1368 total cases and 1198 passing ones. They do this kind of comparison to figure out what the status quo is. I found these tests through that commit, and they might interest you. http://www.oilshell.org/release/0.6.pre21/test/spec.wwz/assign.html
It is trivial to run tests with a new shell, normally I do:
But if I want to add a new shell, I just add it on the end:
|
And I'm glad to hear from you, since I have seen Smoosh and thought our projects would intersect at some point! I have been mainly slogging through a lot of bash features, but it would be definitely be nice to have some confirmation that OSH is POSIX compliant, whatever that means exactly :) One issue I have been meaning to revisit is IFS splitting. As far as I can tell, OSH is the only shell that implements it with a state machine, rather than a big pile of C code: https://github.com/oilshell/oil/blob/master/osh/split.py#L200 Although to be fair, all shells seem to highly agree on IFS behavior, which I found surprising, given how the code looked! OSH doesn't support http://www.oilshell.org/release/0.6.pre21/test/spec.wwz/word-split.html I'm trying to remember where shells disagree the most, on both POSIX features and others... I have been meaning to post-process the output of the spec tests to give some stats on that. i.e. which shells disagree most and which features are disagreed on most. |
Oh and the reason for the bug is that OSH is incorrectly setting the global variable to 6. It reads from the temporary binding and sets the global. For some reason when doing the dynamic scope lookup, I have an explicit check to skip over that "temporary" frame for writes. I thought there was a reason for that, but it might just be a mistake ... There is a comment: i.e.
at least as far as scope is concerned (ignoring IFS). |
Oh I think I figured out what's going on. We have 3 ways of interpreting "temporary bindings" (is there another term for them?):
I started with the construct of "temp frames" for the environment of external processes (temp frames don't have "$@", but other stack frames do). I suspect that when I made that December 2017 commit, I didn't realize I could get rid of that concept and make the temp bindings local variables in the new frame. I will test this theory by making them locals and seeing what breaks :-/ I think it will make the code a lot simpler. I was always suspicious of this mechanism because it seemed pretty complex. |
Fixes issue #329, found by Michael Greenberg. The problem was that temp frames were immutable, so a GLOBAL would be written instead. But on a read, we would still use the temp frame. It's fine to mutate the temp frame because of dynamic scope.
OK I fixed this -- thanks for the report, that was very useful! It turns out the "temp frame" concept is OK -- having a stack frame without But they shouldn't be immutable and I don't know why I made them that way. I think as mentioned in the prior commit I was in a rush to get Aboriginal Linux working around December 2017. I think at some point I had the idea of But that never happened, and this change was a significant simplification. All the tests still pass, in addition to the test case you provided. If you find any other anomalies, please let me know! OSH should pretty easy to build from
|
Test case 23 behaves has SIX different behaviors ni dash, bash, mksh, zsh, yash, and osh! (yash due to not having a 'local' keyword) Related to issue #329. - Split out the 'assign-extended' file for stuff dash doesn't support. - Support more shells in 'assign', and fewer in 'assign-extended'.
You might be interested in cases 23 and 24 here. I get 5 different behaviors on case 23, and if you count OSH, it's 6! http://www.oilshell.org/git-branch/dev/andy-14/3cb35d5d/spec/assign.html This might be where shells disagree the most! case 24 is pure POSIX by omitting (I realized that the temp frames are not purely an implementation detail, so which led me to write those cases. After some re-org, the regression for the bug is now case 22.) |
I'm glad it was a useful report with an easy fix---and thanks for the pointer to your test suite. These test cases are 23 indeed very interesting. Smoosh agrees with zsh, yielding
I'm of course biased, but that feels like the right behavior to me. In particular, I think it's important that immediately after running Bash and OSH feel wrong here because they leak scope information. That is, if locals were meant to allow for lexical scoping, then these implementations allow users to observe less-than-lexical scope. Bash is arguably worse than OSH, because it leaks a global rather than an intermediate, lexical temporary. Dash makes slightly more sense to me. Any write to a non-local variable is treated as globally visible. Case 24 is trickier. I think I disagree with OSH/bash's behavior again (because it means that |
Finally (and sorry to pollute this issue with what is just conversation), I haven't been able to build on OS X (which I think is a known bug). I can run some of the tests you have... if I have the time and you'd be interested in a PR, I can try to add annotate test cases with smoosh's expected behaviors. I may also steal some of your tests, if you don't mind. I can put in a URL pointing to the original... would you also like me to put in a full copy of the Apache 2.0 license and copyright line? |
Yeah I see what you're saying about "unset". I think I could make OSH match the zsh/smoosh behavior by making I doubt any shell scripts rely on this exact behavior (and if they do they will broken across almost every shell), but I guess using the Yes I would be interested in annotations for smoosh! I split the file into And yes feel free to use the tests. Adding the URL and license would be appreciated. Yes OSH is mostly Linux now. The end user tarball has run on OS X before (though I haven't tested it in awhile, I plan to for the 0.6.0 release). The Are you mainly developing smoosh on OS X or should I be able to build and run it on Linux? We can chat on https://oilshell.zulipchat.com/ if it's easier (requires log in e.g. with Github). |
It used to remove the name from the scope. Added spec tests that reveal the difference. Now there are 5 unique behaviors for case 23 of spec/assign, rather than 6. OSH behaves like zsh and smoosh. Inspired by conversation with Michael Greenberg in issue #329.
Fixed with 0.6.pre22: http://www.oilshell.org/release/0.6.pre22/changelog.html |
While comparing how various shells implement the POSIX-unspecified behavior of assignments with function calls, I was confused by OSH's behavior.
Running the above code, the increment in
f
doesn't seem to do anything. That is, I get the following output:Though I would expect
f incr [3]
andf incr [7]
, respectively. Here's my OSH version:The text was updated successfully, but these errors were encountered: