The spec is somewhat fuzzy on the rules for the language mode that applies to parameter destructuring. Consider
'use sloppy';
function f(g = () => { with (x); }) { 'use strict'; }
Is this a syntax error? Reading the spec pedantically, the local strictness directive does only apply to the function body, but it is not clear whether that is really the intention.
If it wasn't, however, and the parameters are supposed to be in strict mode as well, then that would be tricky to implement a single pass parser, because mode-related errors could not be diagnosed directly. This problem is particularly hard due to arrow functions: in general, the parser would not even know when it starts parsing a subexpression with a potentially different language mode:
'use sloppy';
let f = (g = () => { with (x); }) /**/ => { 'use strict' }
Until we reach the comment, the parser does not know that a different mode might apply to the inner arrow. A vast number of expression will be in this situation.
On the web, parsing is the most expensive bottle neck, so the resolution for this bug should be considered very carefully.
Closely related to bug 4243.
The spec makes the arrow function a strict mode function. 10.2 and 10.2.1 should (or rather are intended to) be read as follows:
---
The |FunctionBody| of the |FunctionDeclaration| "f" contains a Use Strict Directive, therefore the function code of "f" is strict mode code (10.2.1). This includes the |FunctionBody|, but also the |FormalParameters| of "f" per 10.2. As a result of this the |ArrowFunction| is also strict mode code, because it is contained in strict mode code (10.2.1).
---
(In reply to André Bargull from comment #1)
> The spec makes the arrow function a strict mode function.
Yes, the 'use strict' is indeed redundant in my second example (copy & paste).
> 10.2 and 10.2.1
> should (or rather are intended to) be read as follows:
> ---
> The |FunctionBody| of the |FunctionDeclaration| "f" contains a Use Strict
> Directive, therefore the function code of "f" is strict mode code (10.2.1).
> This includes the |FunctionBody|, but also the |FormalParameters| of "f" per
> 10.2. As a result of this the |ArrowFunction| is also strict mode code,
> because it is contained in strict mode code (10.2.1).
> ---
Well, that's not quite what it's currently saying, though. As a I pointed out, we have to be very careful here. Changing this might cause measurable performance regressions, for a lot of code not even using ES6 features (at least in V8 it would be pretty costly). I'm not convinced it's worth that.
(In reply to Andreas Rossberg from comment #2)
> (In reply to André Bargull from comment #1)
> > The spec makes the arrow function a strict mode function.
>
> Yes, the 'use strict' is indeed redundant in my second example (copy &
> paste).
Oh, and of course, you probably were referring to the fact that the defaults shouldn't be arrows either in my examples. For clarity, here are the corrected examples:
function f(g = function() { with (x); }) { 'use strict'; }
let f = (g = function() { with (x); }) /**/ => { /* strict*/ }
The abstract issue is that backwards scoping of strictness would essentially require infinite look-ahead.
(In reply to Andreas Rossberg from comment #2)
> Well, that's not quite what it's currently saying, though. As a I pointed
> out, we have to be very careful here. Changing this might cause measurable
> performance regressions, for a lot of code not even using ES6 features (at
> least in V8 it would be pretty costly). I'm not convinced it's worth that.
I don't understand what exactly you mean by "that's not quite what it's currently saying". I was referring to the first example:
'use sloppy';
function f(g = () => { with (x); }) { 'use strict'; }
But the same reasoning applies to the altered example from comment #3:
function f(g = function() { with (x); }) { 'use strict'; }
The function expression in the default parameter initializer should also be treated as a strict mode function. FWIW I do agree this issue makes implementing strict mode restrictions in a parser more difficult.
Can you point out which of the definitions should be made more clear? Thanks!
---
10.2 Types of Source Code
- Function code is source text that is parsed to supply the value of the [[ECMAScriptCode]] and [[FormalParameters]] internal slots (see 9.2) of an ECMAScript function object. [...]
10.2.1 Strict Mode Code
- Function code is strict mode code if the associated FunctionDeclaration, FunctionExpression, GeneratorDeclaration, GeneratorExpression, MethodDefinition, or ArrowFunction is contained in strict mode code or if the code that produces the value of the function’s [[ECMAScriptCode]] internal slot begins with a Directive Prologue that contains a Use Strict Directive.
---
(In reply to André Bargull from comment #4)
> ...
>
> Can you point out which of the definitions should be made more clear? Thanks!
> ---
> 10.2 Types of Source Code
> - Function code is source text that is parsed to supply the value of the
> [[ECMAScriptCode]] and [[FormalParameters]] internal slots (see 9.2) of an
> ECMAScript function object. [...]
>
Right, the use strict direct in a function body is intended to apply to the formal parameter list as well as the function body. That is what the 10.2 is trying to say.
This same issue came up in the context of ES5 as strict mode restricts some identifiers from being used as formal parameter names. At that time, TC39 discussed the look-ahead concern and talked about various ways it might be implemented. After those discussions, TC39 confirmed that the use strict directive was still intended to apply to the formal parameters.
(In reply to Allen Wirfs-Brock from comment #5)
> (In reply to André Bargull from comment #4)
> > ...
> >
> > Can you point out which of the definitions should be made more clear? Thanks!
> > ---
> > 10.2 Types of Source Code
> > - Function code is source text that is parsed to supply the value of the
> > [[ECMAScriptCode]] and [[FormalParameters]] internal slots (see 9.2) of an
> > ECMAScript function object. [...]
> >
>
> Right, the use strict direct in a function body is intended to apply to the
> formal parameter list as well as the function body. That is what the 10.2
> is trying to say.
Okay, I see.
> This same issue came up in the context of ES5 as strict mode restricts some
> identifiers from being used as formal parameter names. At that time, TC39
> discussed the look-ahead concern and talked about various ways it might be
> implemented. After those discussions, TC39 confirmed that the use strict
> directive was still intended to apply to the formal parameters.
Well, in ES5 the look-ahead problem of course was trivial -- it's easy enough to remember a list of parameter names and check them later. With ES6, the problem becomes much more general, and cross-cuts basically all the grammar. It's not just error reporting. In particular, mode-specific hacks like B.3 become even more of a headache than they are already, because the context you have to consider to pick a semantics can extend arbitrarily after the current function.
Anyway, we'll probably try to implement it, and I'll report back.
(In reply to Andreas Rossberg from comment #6)
>
> ... In particular, mode-specific hacks
> like B.3 become even more of a headache than they are already, because the
> context you have to consider to pick a semantics can extend arbitrarily
> after the current function.
>
> Anyway, we'll probably try to implement it, and I'll report back.
Annex B.3.3 exists solely to provide legacy compatibility with the listed set of use case involving Block nested FunctionDeclarations. Because parameter destructurings that might contain such a FunctionDeclaration did not exist prior to ES6, that can be no such legacy code which requires the B.3.3 semantic be applicable to parameters.
Give that, I that it would be reasonably for B.3.3 to explicitly state that this legacy semantics is not to be applied to formal parameter lists.
(In reply to Allen Wirfs-Brock from comment #7)
> (In reply to Andreas Rossberg from comment #6)
>
> >
> > ... In particular, mode-specific hacks
> > like B.3 become even more of a headache than they are already, because the
> > context you have to consider to pick a semantics can extend arbitrarily
> > after the current function.
> >
> > Anyway, we'll probably try to implement it, and I'll report back.
>
> Annex B.3.3 exists solely to provide legacy compatibility with the listed
> set of use case involving Block nested FunctionDeclarations. Because
> parameter destructurings that might contain such a FunctionDeclaration did
> not exist prior to ES6, that can be no such legacy code which requires the
> B.3.3 semantic be applicable to parameters.
>
> Give that, I that it would be reasonably for B.3.3 to explicitly state that
> this legacy semantics is not to be applied to formal parameter lists.
I'd be fine with that. That was just one example, though. I plan to go through the V8 parser tomorrow to get a better overview of what mode-specific code paths there are, and how difficult it would be to defer them.
A more scalable alternative would be to make all non-simple parameter lists implicitly strict. That would widen the kind of semi-strictness we already enforce for them in terms of variable disjointness.
In either of these options I am yet unsure how well they help for arrow functions.
We seemd to agree at the July TC39 meeting to make a non-simple parameter list followed by "use strict"; prologue directive at front of the function's body be an early error:
- Make it an error to have a "use strict" directive in a function with a non-simple parameter list.
- Early error
- No matter what mode you were already in
- When people want to use local "use strict", doing it b/c they want to know that this is always strict, no matter where it ends up.
- Applies to all kinds of function/generator syntax
- IsSimpleParameterList http://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions-static-semantics-issimpleparameterlist
from http://oksoclap.com/p/july-29-2015. I think this decision "stuck" but some demurred or voiced the suspicion that it would bounce, down the road.
/be