loop unrolling and completion values in ES6

Alan Schmitt alan.schmitt at polytechnique.org
Thu Apr 9 14:44:32 UTC 2015

On 2015-04-08 16:59, Allen Wirfs-Brock <allen at wirfs-brock.com> writes:

> Well, they do for normal loop completions (according to the spec.) but not for
> breaks. I this the latter is a bug. In particular, I think it is pretty
> obvious that:
>    eval(“ {0; while (true) {1; break}; 2}”)
> should evaluate to 1
> It is a little less obvious for:
>    eval(“{0; L: while (true) {1; while (true) {2; break L; 3}; 4}; 5}”)
> but, I think that consistency with the first case requires this to
> evaluate to 2.

I think these cases are already fine (modulo the final expression
statement, "; 2" and "; 5", which should not be there as Brendan said):
since the body of the while is a block, the return value is patched as
expected in the block evaluation semantics (step 5):

1. Let sl be the result of evaluating StatementList.
2. ReturnIfAbrupt(sl).
3. Let s be the result of evaluating StatementListItem.
4. If s.[[type]] is throw, return Completion(s).
5. If s.[[value]] is empty, let V = sl.[[value]], otherwise let V = s.[[value]].
6. Return Completion{[[type]]: s.[[type]], [[value]]: V, [[target]]: s.[[target]]}.

The problem happens when the first statement of the body of the while
loop is a break: in that case the value from the previous iteration is
not carried over.

> So, it’s arguably a bug that the ES6 spec. hasn’t changed in that regard
> relative to ES3/5.

I looked deeper into the differences between ES5 and ES6, and something
has partially changed, as illustrated by this example:

  eval("var x=1; 12; while(true){ if (x) { x--; 42 } else { break } }")

In ES5 and in implementations this returns 42, in ES6 this returns
undefined. The difference is here:

ES5 (12.6.2):

2. Repeat
   a. Let exprRef be the result of evaluating Expression.
   b. If ToBoolean(GetValue(exprRef)) is false, return (normal, V, empty).
   c. Let stmt be the result of evaluating Statement.
   d. If stmt.value is not empty, let V = stmt.value.
   e. If stmt.type is not continue || stmt.target is not in the current
      label set, then
      i. If stmt.type is break and stmt.target is in the current label
         set, then
         1. Return (normal, V, empty).
      ii. If stmt is an abrupt completion, return stmt.

In the example, 2.d does not apply for the second iteration of the loop
(the returned value is empty), and we are in the 2.e.i.1 case (local
break) so the returned value is patched in the completion statement.

ES6 (13.0.7 and

the evaluation of the second iteration of the loop ends like this:
  e. Let stmt be the result of evaluating Statement.
  f. If LoopContinues (stmt, labelSet) is false, return Completion(stmt).

at this point we return (break, empty, empty) to the evaluation of the
iteration statement, which transforms it into a (normal, undefined,

The example I gave is different. Adapting it so that is resembles the
previous one:

  eval("var x=1; a: { 12; while(true) { if (x) { x--; 42} else { break a; } } }")

This returns 12 in both ES5 and ES6 (the 12 gets patched in because of
the outer block evaluation), so the labeled statement conversion of
empty to undefined does not kick in.

>> Does this inconsistency matter?
> Probably. In ES6 we are trying to “reform” completion values so, among other
> things, program transformaions do have to worry too much about breaking the
> specified specified completions values.

We found this by looking into loop unrolling, so it would be great if
completion values could propagate across loop iterations.



OpenPGP Key ID : 040D0A3B4ED2E5C7
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 494 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150409/57de4ae6/attachment.sig>

More information about the es-discuss mailing list