Open dherman opened 7 years ago
During ES6 development I actually did this for the looping constructs and the semantics was included in the ES6 drafts until relatively near completion (it was probably removed in the last half of 2014). One of those draft would probably be a good starting point.
You may or may not agree with the actual semantics I provided. My recollection is that generally, break/continue in the control part of a loop were treated similarly to what occurs if they appear in the loop body. EG.
while (do {continue}) ;
and
while (true) continue;
did essentially the same thing.
I believe that most other non-loop places where a do block containing break/continue/return could occur is already handled by the existing abrupt completion handling run-time logic (that logic already has to deal with exception abrupt completiouns that can occur anywhere).
I also assume that you would want:
foo: while (true) {
if (true) do {break foo};
}
to have the same behavior as:
foo: while (true) {
if (true) break foo;
}
In order to detect references to undefined labels from within do expressions, the static semantic routines ContainsUndefinedBreak and ContainsUndefinedContinue need to be extended to recur down all expression positions and not just statement positions. Unfortunately this means that these routines probably also need to be added for all of the expression productions so that the checks propagate to do blocks deeply nested within expressions.
In general having break
/continue
in the loop head seems a bad idea, might be alright for while
loops but for-in
/for-of
it really just makes no sense:
const it = {
next() { done: false, value: 1 },
[Symbol.iterator]() { return this }
}
// The loop is continued before the iterator is even obtained?
for (const i of do { continue ; it }) {
}
// Or worse no iterator at all
for (const i of do { continue }) {
}
Regarding var
in do-expressions, I feel like in the weird case of parameters it should add the variable to the environment record for the parameters (see part 28), not for the body (this is what v8's experimental version does already) e.g.:
function foo(x=do { var y = 10 }) {
const x = 10 // This is allowed
const y = 20 // I think this should also be allowed
// if var y were considered part of the body this would
// be a SyntaxError
}
It's not Stage-4 yet but I think it's a case that could open a whole can-of-worms later if not planned for and that's class fields:
class Foo {
x = do {
// Where on earth is this hoisted to?
var y = 20
}
z = do {
y // y visible? Or would each field get its own var-record
}
}
The goal of
do
-expressions is to be a completely transparent construct, as free of caveats as possible. But there may be some syntactic contexts that create trickiness for the semantics. From the January 2017 meeting, a few interesting cases that came up:In general it's necessary to audit all the expression contexts in the ES grammar and make sure the semantics for
do
-expressions (particularly completions, variable scoping, and label scoping) makes sense in each context. In any contexts where it doesn't, we need to figure out what implications that has on the design, either syntactically, semantically, or both.