D's SwitchStatement accepts statements with ridiculous semantics

Don Clugston edibleplutonium at completelysafe.com
Fri Sep 29 15:05:54 UTC 2017


On Friday, 29 September 2017 at 10:32:02 UTC, Timon Gehr wrote:
> On 29.09.2017 11:12, Don Clugston wrote:
>> Guess what this prints
>> 
>> ----
>> import std.stdio;
>> 
>> void main()
>> {
>>    int i = 0;
>> 
>>    switch (i) for (i = 8; i < 10; ++i)
>>    {
>>      case 7:
>>          writeln(i);
>>          return;
>> 
>>      default: ;
>>    }
>> }
>> ----
>> 
>> 
>> Why does this even compile? It's because the grammar is:
>> 
>> SwitchStatement:
>>      switch ( Expression ) ScopeStatement
>> 
>> 
>> and ScopeStatement allows almost anything.
>> I think the only sane grammar is
>> 
>> SwitchStatement:
>>      switch ( Expression ) BlockStatement
>> 
>> Initially I thought ScopeStatement was accepted in order to 
>> enable Duff's device, but it isn't necessary for that. It 
>> might have originally accepted ScopeStatement to support
>> 
>> E e; switch( e ) with (E) { .... }
>> 
>> Or it may have just been an accident.
>
> It is very likely that this part of the grammar was 
> deliberately copied from C. It's also consistent with how all 
> other control flow constructs are parsed.
>
>> But regardless of the original motivation, it allows some 
>> truly dreadful semantics.
>
> I don't see what your proposed grammar change accomplishes:
>
> switch(i){
>     for(i=8;i<10;++i){
>         case 7:
>             writeln(i);
>             return;
>         default:{}
>     }
> }
>
> I.e., you seem to have misidentified the culprit. Whether or 
> not to the curly braces are required by the parser has nothing 
> to do with switch semantics.

That case looks quite different to me.
It's rather more obvious that the `for` statement has been 
skipped.

The problem I have with the original example is that it looks as 
though the body of the `for` loop is the body of the switch 
statement.

>
>> Can we disallow this silliness please?
>> 
>
> Maybe this specific case can be disallowed during semantic (but 
> I don't really see why it helps, it mostly just makes the 
> language definition more complex).

I believe it makes it simpler. You cannot avoid the reference to 
BlockStatement.
Note that:  "A switch statement must have a default statement."

This is only possible only in two situations.

1. Silly degenerate case.
      switch (i) default: ;

2. A statement which contains a BlockStatement.

It accepts unreachable code.

   switch (i) if ( foo() ) {} else { default: ; }

A switch statement followed by anything other than a 
BlockStatement is *always* wrong. Always.

We improved the switch statement a lot by disallowing implicit 
fallthrough. But it still allows other nonsense that was 
presumably inherited from the very early days of K&R C.

This example just struck me as exceedingly silly, and quite 
misleading.




More information about the Digitalmars-d mailing list