proposal-explicit-resource-management
proposal-explicit-resource-management copied to clipboard
Is `for (using of = obj;;);` valid syntax?
(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.
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.
for ... of: https://github.com/tc39/proposal-explicit-resource-management/pull/171- C-style
for: https://github.com/tc39/proposal-explicit-resource-management/pull/140
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.
@arai-a Oh, sorry, I misunderstood. Here's per the spec as it is:
for (using of of a)is not valid: https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-for-in-and-for-of-statements&secAll=truefor (using of = expr; cond; update) expris valid: https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-for-statement&secAll=true
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.
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-ofwithoutusingdeclaration, whereusingbeing LHS, and the expr being iteratedfor (using of of <expr>):for-ofwithusingdeclaration, where the 1stofbeing 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-ofwithoutusing, where the "ofDIVaDIVg" being the iterated value. This is not SyntaxError in the current spec without this proposal.for-ofwithusing, where a RegExp literal/a/gbeing 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.
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.
Allowing
for (using of of <expr>)is not possible, in term of tokenization and parsing.If
for (using of of <expr>)is allowed, it meansfor (using of ofcan be a prefix of both of the following:
for (using of <expr starts with of>):for-ofwithoutusingdeclaration, whereusingbeing LHS, and the expr being iteratedfor (using of of <expr>):for-ofwithusingdeclaration, where the 1stofbeing 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.
Also, misread the spec (again): for using ... in isn't valid.
I believe the spec is correct: there should be no restriction for the LexicalDeclaration within ForStatement: If for(using of = obj;;;); is invalid while using of = obj is valid, this is a refactoring hazard as moving variable declarations into ForStatement should be generally safe as long as the scope is valid.
Therefore I think it is an implementation bug: I have proposed a fix for Babel in https://github.com/babel/babel/pull/17254. The cost of the fix should be at most a lookahead when the parser sees using of, which is presumably pretty rare in real-world code base.
I've added a test to test262 to verify this: https://github.com/tc39/test262/pull/4483/commits/fc3079a585d827fdd094f323674e1e2d66f531d4
This also appears to be a bug in TypeScript:
I can verify this is parsed incorrectly in V8 13.6.233.8:
Welcome to Node.js v24.0.2. Type ".help" for more information. > for (using x = null;;) break; undefined > for (using of = null;;) break; for (using of = null;;) break; ^ Uncaught SyntaxError: Unexpected token '=' > process.versions.v8 '13.6.233.8-node.10'
@syg
what the hell
It matches var, let, and const in the same position.
Is the fix equivalent to another lookahead checking for = or ;?
Edit: Oh I guess just = because it's a const.
why is "of" allowed as a variable name in JavaScript?
"in" is not allowed
SyntaxError: Unexpected token 'in'
why is "of" allowed as a variable name in JavaScript?
"in" is not allowed
SyntaxError: Unexpected token 'in'
of was never reserved. Same reason async and let are still allowed. It's just a contextual keyword.