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