archives

« Bugzilla Issues Index

#2984 — Ch.14, cumulative bugs & suggestions


Ch.14, cumulative bugs & suggestions

[Note: this is against Rev. 23, some things might have been fixed or changed already.]


TECHNICAL ISSUES

14.1.2, 1st case, last bullet: I thought we decided to drop this restriction (at the 2014/09/18 meeting)
14.2.1, 1st case, 2nd bullet: dito
14.2.1, 2nd case: "if any early errors are present" seems an inadequate formulation. For example, there might be early ReferenceErrors. It doesn't make sense to convert them to SyntaxErrors in this rule.

14.3.1, 1st case, 2nd bullet: we decided to drop this, see above
14.3.1, 2nd case, last bullet: dito
14.4.1, 1st case, 2nd bullet: dito
14.4.1, 2nd case, last bullet: dito

14.5.17: steps 15.b and 17.b return without resetting the context's environment

14.6: The specification of IsInTailPosition seems broken. It just verifies that there exists _some_ occurrence of the phrase in question in tail position, but does not check that it is the one we are looking for. For example, consider "() => { f(); return f() }". AFAICS, given the definition as is, IsInTailPosition would give true no matter which call to f() we are asking for.

14.6.2.1, last case: "Finally" -> "Block"
14.6.3, step 3: "it remains in its suspended state" -- I'm not sure I understand, "it" refers to the now-on-top context, which is not the suspended one


SUGGESTIONS

14.1.8: Why not define that in terms of BoundNames being empty/non-empty?


TYPOS & FORMATTING

14.1.2: layout of first production
14.1.14: bogus xref to 13.11.2
14.2.16: bogus xref to 14.1.18 (should be 14.1.19)
14.2.16, Note: "is" -> "in"
14.4.11: spurious comma in xrefs
14.4.13: bogus xref to 14.1.18 (should be 14.1.19)
14.5.1, 2nd case, 1st bullet: formatting
14.6.2.2, cases for Expression: formatting of 'Expression' and 'AssignmentExpression'


Re: let redeclaration of parameters in body issues (14.1.2, 14.2.1):

https://github.com/rwaldron/tc39-notes/blob/master/es6/2013-09/sept-18.md#--rules-about-redeclaration show an example:

function f(x = {}) {
let x;
}

// Error for redeclaration.

and I don't see any discussion or recorded decision that changes that.

As the the rationale, consider:

function (x = { } {
var x;
let x;
}

would definitely be an error under our current static semantics. It's a var/let conflict. And formal parameters are var-like, in the sense that in:

function (x= { }) {
var x;
}

the var declaration of x does not introduce a new x binding the body scope. Hence, it is equivalent to:

function (x= { }) {
}

Given that equivalence, it seems that
function (x = { } {
var x;
let x;
}

and

function (x = { } {
let x;
}

should use the static 'let/var conflict rules.

The rule: It is a Syntax Error if any element of the BoundNames of FormalParameters also occurs in the LexicallyDeclaredNames of FunctionBody.

Is an easy to understand statement of this restriction.


fixed in rev26 editor's draft.

Except for the the parameter/let conflict which I don't I believe is correct.

For 14.6 added a note that the algorithms are comparing an actual matched production corresponding to a specific source code range and looking for the equivalent source. A round about way to avoid talking about ASTs and AST subtree matching.


Hm, it's the logical consequence of the two scopes decision. I notedly remember this point being clarified in the discussion (by Brendan?), but yeah, I can't find that in the notes. OTOH, I think the code snippet you cite was merely a discussion item, not a resolution.

The arguments were: Minimise rules and special cases. {} indicates a block, it should behave like one. No reason for special nannying in this one case.

In particular, I think one can reasonably expect the following equivalence to hold for arrow functions and do expressions in ES7:

(...) => {...} === (...) => do {...}


(In reply to comment #3)
> Hm, it's the logical consequence of the two scopes decision.

Two scopes are only created if parameter defaults are present. If no defaults are present a single scope is required to preserve backwards compatibility (-> deletable eval bindings).


> The arguments were: Minimise rules and special cases. {} indicates a block, it
> should behave like one. No reason for special nannying in this one case.

Except in `catch` blocks, but that's a different story...


(In reply to comment #4)
> Two scopes are only created if parameter defaults are present. If no defaults
> are present a single scope is required to preserve backwards compatibility (->
> deletable eval bindings).

The question only affects block-scoped bindings, so there shouldn't be any compatibility issue either way.

> > The arguments were: Minimise rules and special cases. {} indicates a block, it
> > should behave like one. No reason for special nannying in this one case.
>
> Except in `catch` blocks, but that's a different story...

Right. I think the same principle should apply there.


(In reply to comment #5)
> The question only affects block-scoped bindings, so there shouldn't be any
> compatibility issue either way.
>

Does that mean that in the following function `f`, `p` and `a` are in the same scope, whereas `b` is in a different (nested) scope?

function f(p) {
var a;
let b;
}

Var-scoped variables and parameters need to live in the same scope, so compatibility is preserved for this example:

function g(x) {
print(x);
eval("var x = 1");
print(x);
delete x;
print(x);
}
g(0); // Prints "0 1 1"

NB: If `x` is a default parameter, the example will print "0 1 0" instead (per the current semantics in rev25).


(In reply to comment #6)
> (In reply to comment #5)
> > The question only affects block-scoped bindings, so there shouldn't be any
> > compatibility issue either way.
>
> Does that mean that in the following function `f`, `p` and `a` are in the same
> scope, whereas `b` is in a different (nested) scope?
>
> function f(p) {
> var a;
> let b;
> }

This would simply behave like

function f(p) {{
var a;
let b;
}}

does with what's currently spec'ed. That is, `a` is hoisted to the same scope as `p`, but `b`, being block-scoped, isn't.

I'm not sure I understand what your concern is. The question is whether function bodies should behave as block scopes. That's irrelevant to `var`, because its meaning is never affected by the absence or presence of block scopes. Var-scoping is broken beyond repair, nobody suggests we could fix that. But having as simple & uniform rules as possible for lexical scoping would be preferable.

> Var-scoped variables and parameters need to live in the same scope, so
> compatibility is preserved for this example:
>
> function g(x) {
> print(x);
> eval("var x = 1");
> print(x);
> delete x;
> print(x);
> }
> g(0); // Prints "0 1 1"
>
> NB: If `x` is a default parameter, the example will print "0 1 0" instead (per
> the current semantics in rev25).

Yes, no change here.


(In reply to comment #7)
> This would simply behave like
>
> function f(p) {{
> var a;
> let b;
> }}
>
> does with what's currently spec'ed. That is, `a` is hoisted to the same scope
> as `p`, but `b`, being block-scoped, isn't.

IIUC: Functions without default parameters will have two scopes - one scope for parameters and var-declared bindings and an additional scope for lexical bindings. Functions with defaults will have three scopes - the parameter list scope, the var-declared bindings scope and the lexical bindings scope. Is that correct?

> I'm not sure I understand what your concern is. The question is whether
> function bodies should behave as block scopes.

I just wanted to point out that parameters and var-declared bindings in functions without defaults should live in the same scope. That way (unnecessary?) differences between ES5 and ES6 semantics are reduced. Cf. the wrong "invisible environment" statement in [1].

[1] https://github.com/rwaldron/tc39-notes/blob/master/es6/2014-04/apr-8.md#41-review-latest-spec-draft


added bug 3005 to track issue regard parameter/let name conflict early error


in rev26