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

Is for (using of = obj;;); valid syntax? #248

Open
arai-a opened this issue Dec 20, 2024 · 7 comments
Open

Is for (using of = obj;;); valid syntax? #248

arai-a opened this issue Dec 20, 2024 · 7 comments

Comments

@arai-a
Copy link

arai-a commented Dec 20, 2024

(discovered in https://bugzilla.mozilla.org/show_bug.cgi?id=1934205 )

In the proposal, ForInOfStatement definition is modified and the using handling is added into ForDeclaration with [Using] parameter, where the parameter is set only after a negative lookahead for for of using of:

https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-for-in-and-for-of-statements&secAll=true

ForInOfStatement[Yield, Await, Return] :
  for ( [lookahead ≠ let [] LeftHandSideExpression[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( var ForBinding[?Yield, ?Await, +Pattern] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( ForDeclaration[?Yield, ?Await, ~Using] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( [lookahead ∉ { let, async of }] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( var ForBinding[?Yield, ?Await, +Pattern] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( [lookahead ≠ using of] ForDeclaration[?Yield, ?Await, +Using] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  [+Await] for await ( [lookahead ≠ let] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  [+Await] for await ( var ForBinding[?Yield, ?Await, +Pattern] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  [+Await] for await ( [lookahead ≠ using of] ForDeclaration[?Yield, ?Await, +Using] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]

ForDeclaration[Yield, Await, Using] :
  LetOrConst ForBinding[?Yield, ?Await, +Pattern]
  [+Using] using [no LineTerminator here] ForBinding[?Yield, ?Await, ~Pattern]
  [+Using, +Await] await [no LineTerminator here] using [no LineTerminator here] ForBinding[?Yield, +Await, ~Pattern]

So, for (using of cannot become a prefix of the for-in/for-of with using declaration, but it can be a prefix of for-of with the using being an identifier in LeftHandSideExpression.

On the other hand, the proposal doesn't touch the ForStatement definition, and the using handling is added into LexicalDeclaration unconditionally.

https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-for-statement&secAll=true

ForStatement[Yield, Await, Return] :
  ...
  for ( LexicalDeclaration[~In, ?Yield, ?Await] Expression[+In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]

https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-let-const-using-and-await-using-declarations&secAll=true

LexicalDeclaration[In, Yield, Await] :
  LetOrConst BindingList[?In, ?Yield, ?Await, +Pattern] ;
  UsingDeclaration[?In, ?Yield, ?Await]
  [+Await] AwaitUsingDeclaration[?In, ?Yield]

https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-let-const-using-and-await-using-declarations

UsingDeclaration[In, Yield, Await] :
  using [no LineTerminator here] BindingList[?In, ?Yield, ?Await, ~Pattern] ;

AwaitUsingDeclaration[In, Yield] :
  CoverAwaitExpressionAndAwaitUsingDeclarationHead[?Yield] [no LineTerminator here] BindingList[?In, ?Yield, +Await, ~Pattern] ;

This means ForStatement matches for (using of = obj;;);.
Is this expected?

TypeScript doesn't recognize the syntax, and
Babel, esbuild, Chromium, and SpiderMonkey throw syntax error.
Apparently the negative lookahead is applied also to ForStatement in all of them.

If for (using of = obj;;); should throw SyntaxError, the ForStatement syntax should be modified to have a negative lookahead, with propagating the existence of using to LexicalDeclaration, or maybe replacing that part with something similar to ForDeclaration.

@dead-claudia
Copy link

dead-claudia commented Dec 22, 2024

It's valid in for statements, and all of those tools supportint using declarations but not in either/both for statement types are wrong. Here's some related PRs showing that it's clearly intended to be supported.

@arai-a
Copy link
Author

arai-a commented Dec 22, 2024

Just to make sure, here's my understanding and my intent.

Those PRs looks related only to the C-style for + using.
I agree that the for + using is valid in the proposal, and all those implementation parses for (using x = obj;;);.
This issue is about the of being a binding identifier in the context, so, using being followed by of.

Is it correct that of can be a binding identifier in C-style for + using declaration, and for (using of can be a prefix for both for (using of obj); (regular for-of without using declaration, where using is an identifier) and for (using of = obj;;); (C-style for with using declaration, where of is an identifier) ?

If that's the case, I think there should be some explicit note about that, given that all implementation misinterpreted it.

@dead-claudia
Copy link

@arai-a Oh, sorry, I misunderstood. Here's per the spec as it is:

Feels like a spec bug, to be honest. The spec should either reject both or allow both.

IMHO it makes more sense to allow both. Allowing it is as simple as adding explicit productions for for ( using in <expr> ) <stmt>, for ( using of <expr> ) <stmt>, and for ( using of of <expr> ) <stmt> and adding using to the negative lookahead in the for ( <lhs> of <expr> ) <stmt> clause, and then specifying runtime semantics accordingly for each.

for ( using <ident> in <expr> ) <stmt> should be wholly disallowed IMHO and that's probably also a spec oversight.

@arai-a
Copy link
Author

arai-a commented Dec 22, 2024

Allowing for (using of of <expr>) is not possible, in term of tokenization and parsing.

If for (using of of <expr>) is allowed, it means for (using of of can be a prefix of both of the following:

  • for (using of <expr starts with of>) : for-of without using declaration, where using being LHS, and the expr being iterated
  • for (using of of <expr>) : for-of with using declaration, where the 1st of being a binding identifier and the expr being iterated

In this case, tokenization (DIV vs RegExp) after the 2nd of conflicts between those cases, and it's not deterministic.

For example, if we have for (using of of /a/g); source, it matches both of the following:

  • for-of without using, where the "of DIV a DIV g" being the iterated value. This is not SyntaxError in the current spec without this proposal.
  • for-of with using, where a RegExp literal /a/g being the iterated value.

Thus, if we're to match the behaviors of C-style for and for-of, the only way would be to reject of being a binding identifier of using declaration in both cases.

@arai-a
Copy link
Author

arai-a commented Dec 23, 2024

Possible modification would be to first add the Using parameter to LexicalDeclaration, with making both UsingDeclaration and AwaitUsingDeclaration conditional on the parameter:

LexicalDeclaration[In, Yield, Using, Await] :
  LetOrConst BindingList[?In, ?Yield, ?Await, +Pattern] ;
  [+Using] UsingDeclaration[?In, ?Yield, ?Await]
  [+Using, +Await] AwaitUsingDeclaration[?In, ?Yield]

and then split the ForStatement with LexicalDeclaration case into 2, one without Using parameter, and one with Using parameter but only when not starts with using of:

ForStatement[Yield, Await, Return] :
  ...
  for ( LexicalDeclaration[~In, ?Yield, ~Using, ?Await]
        Expression[+In, ?Yield, ?Await]opt ;
        Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]
  for ( [lookahead ≠ using of] LexicalDeclaration[~In, ?Yield, +Using, ?Await]
        Expression[+In, ?Yield, ?Await]opt ;
        Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]

and then modify the other occurrences of LexicalDeclaration to have +Using parameter.

Or maybe add another symbol that replaces LexicalDeclaration in ForStatement, so that the other existing LexicalDeclaration occurrences aren't affected by this change.

@dead-claudia
Copy link

dead-claudia commented Dec 23, 2024

Allowing for (using of of <expr>) is not possible, in term of tokenization and parsing.

If for (using of of <expr>) is allowed, it means for (using of of can be a prefix of both of the following:

  • for (using of <expr starts with of>) : for-of without using declaration, where using being LHS, and the expr being iterated
  • for (using of of <expr>) : for-of with using declaration, where the 1st of being a binding identifier and the expr being iterated

Oh, whoops, that was the one case I missed. 🤦‍♀️ I knew it'd be possible with some grammar hackery, just was wrong about the specifics.

I still stand by my opinion on either allowing both or prohibiting both, but to be clear, that's not what the current spec says.

@dead-claudia
Copy link

Also, misread the spec (again): for using ... in isn't valid.

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

No branches or pull requests

2 participants