Skip to content
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

Fix #1403: Variables inconsistently masked inside of do #4854

Merged
merged 1 commit into from
Jan 16, 2018

Conversation

zdenko
Copy link
Collaborator

@zdenko zdenko commented Jan 12, 2018

Fixes #1403.
It seems that For::pluckDirectCall isn't needed.

Examples from #1403

for dummy in [0...1]
  masked = 10
  do -> alert masked = masked + 1
alert 'a line needs to be here to trigger the issue'
for (dummy = i = 0; i < 1; dummy = ++i) {
  masked = 10;
  (function() {
    return alert(masked = masked + 1);
  })();
}
alert('a line needs to be here to trigger the issue');

for dummy in [0...1]
  notMasked = 10
  do -> alert notMasked += 1
alert 'a line needs to be here to trigger the issue'
for (dummy = j = 0; j < 1; dummy = ++j) {
  notMasked = 10;
  (function() {
    return alert(notMasked += 1);
  })();
}
alert('a line needs to be here to trigger the issue');

@GeoffreyBooth
Copy link
Collaborator

Before we jettison pluckDirectCall entirely, can someone explain why it was added in the first place? From my Git diving I see that @satyr added it in 4e39e2e; @satyr, do you mind sharing what was on your mind back in 2010? The commit message was “made dynamic calls at the last of for expression be defined outside”.

I did some testing, and on master pluckDirectCall is currently called by the following tests, all in comprehensions.coffee:

I pulled those tests into their own file and compiled them into JS using both master and this branch. Here’s the diff.

A minimal case seems to be this:

for i in [1..3] then do -> console.log i

On master:

var fn, i, j;

fn = function() {
  return console.log(i);
};
for (i = j = 1; j <= 3; i = ++j) {
  fn();
}

Per this PR:

var i, j;

for (i = j = 1; j <= 3; i = ++j) {
  (function() {
    return console.log(i);
  })();
}

Both versions print 1, 2, 3, which might not be obvious at first; the reference to i is consistent no matter where the do function is put. But the two outputs illustrate the difference pluckDirectCall makes, which is to shift a do-wrapped function outside of the for loop, presumably for scope reasons. Sounds a lot like “made dynamic calls at the last of for expression be defined outside.” So . . . why? What is gained by creating a temporary variable fn and assigning this do function to it, and calling fn inside the for loop rather than leaving the function itself there?

4e39e2e has this test added, in obsolete CoffeeScript syntax:

# If the last expression is dynamic call,
# define it outside and pass loop variables to it.
fs = for i from 0 to 2 when i > 0
  void
  do ->
    {callee} = arguments
    -> [i, callee]
[one, two] = (f() for f in fs)
eq one[0], 1
eq two[0], 2
eq one[1], two[1]

Even converting this into modern syntax:

fs = for i in [0..2] when i > 0
  do ->
    {callee} = arguments
    -> [i, callee]
[one, two] = (f() for f in fs)
eq one[0], 1
eq two[0], 2
eq one[1], two[1]

none of these tests pass, assuming I converted it correctly. Perhaps pluckDirectCall is a relic of those times, when scope and comprehensions behaved differently?

@vendethiel
Copy link
Collaborator

@GeoffreyBooth my memory is a bit fuzzy because it's been a long time... But AFAIK not defining the function inside the loop is an optimization (see coco's wiki page "Additions").
As to why the test passes: I don't remember everything, but I think at some point do auto-captured loop variables(?). I think that's why the test passed.

@vendethiel
Copy link
Collaborator

Also, paging @satyr about some reasoning from 2010 is probably not going to work...

@GeoffreyBooth
Copy link
Collaborator

Ah, an optimization makes sense. Well, it's not really worth keeping if it's buggy, which it appears to be; and it's an optimization the user could do themselves. If they want the function defined before the loop and referenced in the loop, they could write their code as such.

So this looks good to me. @vendethiel, any notes? And have we lost @satyr?

@vendethiel
Copy link
Collaborator

LGTM. As for @satyr, I think their last commit was a few years back, so we'd have to wait for a while.

@GeoffreyBooth GeoffreyBooth merged commit 7ea3ea9 into jashkenas:master Jan 16, 2018
@connec
Copy link
Collaborator

connec commented Jan 16, 2018

This may lead to non-trivial performance degradations for code using the old for ... then do -> construct:

for a in [1, 2, 3] then do ->
  setTimeout (-> console.log a), 1000

zdenko added a commit to zdenko/coffeescript that referenced this pull request Jan 17, 2018
GeoffreyBooth pushed a commit that referenced this pull request Feb 1, 2018
…ariables (#4853)

* fix #2047

* Additional check for 'step'; tests

* Fix #4105 (#4855)

* Update output

* Throw warning for unsupported runtimes, e.g. Node < 6 (#4839)

* fix #1403 (#4854)

* Update output

* [Change]: Destructuring with non-final spread should still use rest syntax (#4517) (#4825)

* destructuring optimization

* refactor

* minor improvement, fix errors

* minor refactoring

* improvements

* Update output

* Update output

* Fix #4843: bad output when assigning to @prop in destructuring assignment with defaults (#4848)

* fix #4843

* improvements

* typo

* small fix

* Fix #3441: parentheses wrapping expression throw invalid error  (#4849)

* fix #3441

* improvements

* refactor

* Fix #1726: expression in property access causes unexpected results (#4851)

* fix #1726

* Explain what's happening, rather than just linking to an issue

* Updated output

* Optimization

* Update output

* remove casting to number

* cleanup tests
@GeoffreyBooth GeoffreyBooth mentioned this pull request Feb 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants