const and eval in non-strict code
On Jul 8, 2012, at 4:51 PM, Luke Hoban wrote:
What is the intended behavior of this code:
(function() { eval("const x = 4"); const x = 3; })();
The ES6 draft spec seems to be missing a few of the details that would contribute to answering this. Specifically, section 15.1.2.1 on eval refers to 10.4.2, which no longer exists, and 10.5, which is now a series of separate algorithms. My guess is that the intention is that eval code is intended to be treated as "top level declaration instantiation" (10.5.1), but that section doesn't handle LexicalDeclarations. Block Declaration Instantiation (10.5.4) does handle LexicalDeclarations, but it's not clear that there is a block instantiation here.
Yes, the eval spec. is still incomplete and there are still top level declarations issues to resolve that should be done first. There may end up being a seperate eval declaration instantiation section.
Regarding, this specific issue. I assume that it would be a early error (during the eval) because the scope contour (the function's top level) where eval needs to instantiate x already has a declaration for x and multiple declaration are not allowed.
From: Allen Wirfs-Brock [mailto:allen at wirfs-brock.com]
What is the intended behavior of this code:
(function() { eval("const x = 4"); const x = 3; })();
Regarding, this specific issue. I assume that it would be a early error (during the eval) because the scope contour (the function's top level) where eval needs to instantiate x already has a declaration for x and multiple declaration are not allowed.
That makes some sense, but seems to mix phases of the current spec. The "duplicate identifier" errors are today solely in places (12.1) where they can be identified with just the information about a single parse. Making the above an early error requires compilation time to know about the scope in which the compilation is being done just to report early errors. As I understand it, that would be new to the spec, and to implementations. I doubt that complexity would be warranted here. I could imagine this being a late error during binding instantiation in the future "Eval Declaration Binding" section instead would be cleaner?
A couple more questions:
.#1: The current spec says that an early error is reported on an assignment to a const-bound variable only if it "can be statically determined to always resolve to a declarative environment record binding and the resolved binding is immutable". Does that mean that the example below is not an error? By the spec text, it should not be an error, but I'm unclear on exactly what set of "static analysis" is required. For instance, is the mere presence of an eval inside the body of g enough to assume it cannot be statically determined? What if the eval did not redeclare x? Or is the spec text not what is actually intended, and this should be an early error even though it would run without any error?
function f() { const x = 5; function g() { eval("var x = 10"); x = 20; } }
.#2: The spec has added block scoped function declarations. With 1JS, this is a non-opt-in breaking change for most browsers. I have not yet investigated how significant this break is in practice, but I understand there has been concern about changes here in the past due to web compatibility.
If it happened to not be okay to break compatibility here, and browsers with function scoped function statements were to need to keep support for that extension, it seems that several concerns arise with let/const. For example - the following would need to capture a reference to a nested scope during creation of a function object upon entering f, which requires the nested scope to exist earlier than it needs to in the spec today. I suspect there are cases where that is not even implementable. Was this discussed previously? If so, was the result an approach that would allow reasonable semantics for block scoped bindings combined with function scoped function statements? Or was the result that block scoped bindings only make sense when combined with block scoped function statements?
function f() { g(); { let x = 10; function g() { return x; } } }
Luke
On Jul 18, 2012, at 7:08 PM, Luke Hoban wrote:
From: Allen Wirfs-Brock [mailto:allen at wirfs-brock.com]
What is the intended behavior of this code:
(function() { eval("const x = 4"); const x = 3; })();
Regarding, this specific issue. I assume that it would be a early error (during the eval) because the scope contour (the function's top level) where eval needs to instantiate x already has a declaration for x and multiple declaration are not allowed.
That makes some sense, but seems to mix phases of the current spec. The "duplicate identifier" errors are today solely in places (12.1) where they can be identified with just the information about a single parse. Making the above an early error requires compilation time to know about the scope in which the compilation is being done just to report early errors. As I understand it, that would be new to the spec, and to implementations. I doubt that complexity would be warranted here. I could imagine this being a late error during binding instantiation in the future "Eval Declaration Binding" section instead would be cleaner?
You're right that this might imply some new mechanism in the spec. However, it occurs to me we many well need similar mechanisms to support modules. Too early to be sure about that.
The spec. doesn't currently say anything about the ordering of detection of early errors. Just that they are reported prior to executing any user code. I believe, from that perspective, detecting and reporting those errors during the implementation of "Eval Declaration instantiation" would still qualify as early error detection . Or another way to look at it, am implementation could insert a prolog that validates these conditions against the actual scope prior to actually commencing Eval Declaration Instantiation.
A couple more questions:
#1: The current spec says that an early error is reported on an assignment to a const-bound variable only if it "can be statically determined to always resolve to a declarative environment record binding and the resolved binding is immutable". Does that mean that the example below is not an error? By the spec text, it should not be an error, but I'm unclear on exactly what set of "static analysis" is required. For instance, is the mere presence of an eval inside the body of g enough to assume it cannot be statically determined? What if the eval did not redeclare x? Or is the spec text not what is actually intended, and this should be an early error even though it would run without any error?
function f() { const x = 5; function g() { eval("var x = 10"); x = 20; } }
Yes, not any early error because the existence of a direct eval (in non-strict code) means that the actual declarations that will exist at runtime can't be statically predicted. That's essentially why we distinguished between direct and indirect evals. However, the above would be a runtime error.
#2: The spec has added block scoped function declarations. With 1JS, this is a non-opt-in breaking change for most browsers. I have not yet investigated how significant this break is in practice, but I understand there has been concern about changes here in the past due to web compatibility.
Yes, this issue was discussed when we adopted 1JS and we agreed to this subject to experimental results. It isn't clear to me how we would fix it, if it proved too incompatable.
If it happened to not be okay to break compatibility here,
not sure with which compatibility we are referring to. The interoperable intersetion between all browsers for block nested function declarations is fairly small and certainly doesn't include functions that reference let bindings.
and browsers with function scoped function statements were to need to keep support for that extension, it seems that several concerns arise with let/const. For example - the following would need to capture a reference to a nested scope during creation of a function object upon entering f, which requires the nested scope to exist earlier than it needs to in the spec today. I suspect there are cases where that is not even implementable. Was this discussed previously? If so, was the result an approach that would allow reasonable semantics for block scoped bindings combined with function scoped function statements? Or was the result that block scoped bindings only make sense when combined with block scoped function statements?
generally the latter. In ES<=5.1 there are no block scoped declarations so there can't be any dependencies between hoisted functions in blocks (a known extension) and blocks scoped let/const/class.
We might make up more elaborate semantics, such as a function declaration is only block local if the block also contains a let/const/class declaration. But that seems highly undesirable. When we discussed this we convinced ourselves that the interoperable intersection of existing implementations is small enough in this regard that code that currently works cross platform is likely to continue to work with the new semantics. This is what needs to be experimentally verified.
What is the intended behavior of this code:
The ES6 draft spec seems to be missing a few of the details that would contribute to answering this. Specifically, section 15.1.2.1 on eval refers to 10.4.2, which no longer exists, and 10.5, which is now a series of separate algorithms. My guess is that the intention is that eval code is intended to be treated as "top level declaration instantiation" (10.5.1), but that section doesn't handle LexicalDeclarations. Block Declaration Instantiation (10.5.4) does handle LexicalDeclarations, but it's not clear that there is a block instantiation here.
Luke