let/const in switch cases

Allen Wirfs-Brock allen at wirfs-brock.com
Tue Aug 14 08:52:42 PDT 2012

On Aug 9, 2012, at 8:48 PM, Brendan Eich wrote:

> Luke Hoban wrote:
>> According to the current draft spec text 'let'/'const' are allowed in the statement list of a switch case, but contribute to the block scope of the outer block.  This can lead to some confusing situations:
>> function(x) {
>>    do {
>>         switch(x) {
>>             case 0:
>>                 return x;
>>             case 1:
>>                 let x = 'let';
>>         }
>>     } while (foo());
>> }
>> The 'x' in 'case 0' here will bind to the later 'let x',
> That's right, let hoists to braced body or block top -- explicit is better than implicit.
> I did this in JS1.7 and we've had years of experience with it, in SpiderMonkey and Rhino. Alternative of implicit per-"case" scope does not work due to C-like fall-through inherited via Java. Programmers do write let in case- and default-labeled statement lists in switches in Mozilla-specific code and I've never heard of a usability problem.
> We could be restrictive (more below), but you have to make a usability or implementation hardship case. I don't think you have.

A more illustrative example for Luke's concerns would be:

function(x) {
   do {
        switch(x) {
            case 0:
                return x;    //always a runtime error
            case 1:
                let x;
		x = 'let';     //never a runtime error
            case 2:
                return x;    //sometimes a runtime error
    } while (foo());

The classifications (always, never, sometimes)  of references can be determined by a fairly straightforward static analysis. You only need to do a  runtime TDZ check on the sometimes case. You can issue compile-time warnings on the always and sometimes cases.

From a usability case we have to consider which is more likely something like the above or something like:

     switch (x) {
       case 0:
           let a = ...;
           // lots of lines of code using a
       case 1:
            let b = ...;
            // lots of lines of code using b
        case 2:
My guess was that the disjoint case would be more common and more annoying if it was disallowed.  I considered spec'ing each case alternative as a block but I concluded it would be unwise to introduce a block scope that was not associated with {  }'s.

Basically, the switch body is a block and all the static semantic rules for blocks apply to it as do the same dynamic initialize-before-reference TDZ rules. This seems straightforward enough to explain to users. The only oddity is that it has multiple entry points which means some TDZ violations can't be be statically predicted, but that is an optimization issue rather than one of user understanding.


More information about the es-discuss mailing list