Repository navigation
Is for (using of = obj;;); valid syntax? #248
Description
(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 ofusing 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]
LexicalDeclaration[In, Yield, Await] :
LetOrConst BindingList[?In, ?Yield, ?Await, +Pattern] ;
UsingDeclaration[?In, ?Yield, ?Await]
[+Await] AwaitUsingDeclaration[?In, ?Yield]
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.
Activity
dead-claudia commentedon Dec 22, 2024
It's valid in
forstatements, and all of those tools supportintusingdeclarations but not in either/bothforstatement types are wrong. Here's some related PRs showing that it's clearly intended to be supported.for ... of: Correctly use hint in ForIn/OfBodyEvaluation #171for: Revert changes to ForBodyEvaluation/CreatePerIterationEnvironment #140arai-a commentedon 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+usingis valid in the proposal, and all those implementation parsesfor (using x = obj;;);.This issue is about the
ofbeing a binding identifier in the context, so,usingbeing followed byof.Is it correct that
ofcan be a binding identifier in C-stylefor+usingdeclaration, andfor (using ofcan be a prefix for bothfor (using of obj);(regularfor-ofwithoutusingdeclaration, whereusingis an identifier) andfor (using of = obj;;);(C-styleforwithusingdeclaration, whereofis 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 commentedon Dec 22, 2024
@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=trueFeels 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>, andfor ( using of of <expr> ) <stmt>and addingusingto the negative lookahead in thefor ( <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 commentedon 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 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 iteratedIn this case, tokenization (DIV vs RegExp) after the 2nd
ofconflicts 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
forandfor-of, the only way would be to rejectofbeing a binding identifier ofusingdeclaration in both cases.arai-a commentedon Dec 23, 2024
Possible modification would be to first add the
Usingparameter toLexicalDeclaration, with making bothUsingDeclarationandAwaitUsingDeclarationconditional on the parameter:and then split the
ForStatementwithLexicalDeclarationcase into 2, one withoutUsingparameter, and one withUsingparameter but only when not starts withusing of:and then modify the other occurrences of
LexicalDeclarationto have+Usingparameter.Or maybe add another symbol that replaces
LexicalDeclarationinForStatement, so that the other existingLexicalDeclarationoccurrences aren't affected by this change.dead-claudia commentedon Dec 23, 2024
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 commentedon Dec 23, 2024
Also, misread the spec (again):
for using ... inisn't valid.JLHwung commentedon Apr 23, 2025
I believe the spec is correct: there should be no restriction for the LexicalDeclaration within ForStatement: If
for(using of = obj;;;);is invalid whileusing of = objis 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 babel/babel#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.rbuckton commentedon May 21, 2025
I've added a test to test262 to verify this: tc39/test262@fc3079a
This also appears to be a bug in TypeScript:
rbuckton commentedon May 21, 2025
I can verify this is parsed incorrectly in V8 13.6.233.8:
@syg
syg commentedon May 22, 2025
what the hell
rbuckton commentedon May 22, 2025
It matches
var,let, andconstin the same position.syg commentedon May 22, 2025
Is the fix equivalent to another lookahead checking for
=or;?Edit: Oh I guess just
=because it's a const.[explicit-resource-management] Fix parsing for (using of=null;;)
deps: V8: cherry-pick 249de887a8d3
deps: V8: cherry-pick 249de887a8d3
deps: V8: cherry-pick 249de887a8d3
deps: V8: cherry-pick 249de887a8d3
deps: V8: cherry-pick 249de887a8d3
alexsch01 commentedon Jun 14, 2025
why is "of" allowed as a variable name in JavaScript?
"in" is not allowed
dead-claudia commentedon Jun 15, 2025
ofwas never reserved. Same reasonasyncandletare still allowed. It's just a contextual keyword.