digitalmars.D - D's SwitchStatement accepts statements with ridiculous semantics
- Don Clugston (30/30) Sep 29 2017 Guess what this prints
- Dukc (4/5) Sep 29 2017 My guess is it prints "1".
- sarn (31/37) Sep 29 2017 Yeah, it's a lot like Duff's Device:
- drug (2/35) Sep 30 2017 Thanks for clarification!
- Timon Gehr (19/63) Sep 29 2017 It is very likely that this part of the grammar was deliberately copied
- Don Clugston (23/86) Sep 29 2017 That case looks quite different to me.
- Timon Gehr (31/85) Sep 29 2017 This can be done with basically any control flow construct we have:
- Jacob Carlborg (5/8) Sep 30 2017 I've used that as well, but the other way around. I put the switch
- H. S. Teoh (11/14) Sep 29 2017 I believe one of the reasons the grammar was written this way was to
- user1234 (6/10) Sep 30 2017 There are two big family of switches. C-like and Pascal like. In
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. But regardless of the original motivation, it allows some truly dreadful semantics. Can we disallow this silliness please?
Sep 29 2017
On Friday, 29 September 2017 at 09:12:54 UTC, Don Clugston wrote:Guess what this printsMy guess is it prints "1". By "guess" I mean it, I did not test! Anyway reminds me a lot of very badly used gotos.
Sep 29 2017
On Friday, 29 September 2017 at 09:56:17 UTC, Dukc wrote:On Friday, 29 September 2017 at 09:12:54 UTC, Don Clugston wrote:Yeah, it's a lot like Duff's Device: https://en.wikipedia.org/wiki/Duff's_device For anyone who's wondering, it works because switch is just a computed goto. The code's equivalent to this: import std.stdio; void main() { int i = 0; // switch(i) if (i == 7) { goto case_7; } else { goto case_default; } // for loop initialiser i = 8; // for loop test while (i < 10) { case_7: writeln(i); return; case_default: // for loop update ++i; } }Guess what this printsMy guess is it prints "1". By "guess" I mean it, I did not test! Anyway reminds me a lot of very badly used gotos.
Sep 29 2017
30.09.2017 05:35, sarn пишет:For anyone who's wondering, it works because switch is just a computed goto. The code's equivalent to this: import std.stdio; void main() { int i = 0; // switch(i) if (i == 7) { goto case_7; } else { goto case_default; } // for loop initialiser i = 8; // for loop test while (i < 10) { case_7: writeln(i); return; case_default: // for loop update ++i; } }Thanks for clarification!
Sep 30 2017
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.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).
Sep 29 2017
On Friday, 29 September 2017 at 10:32:02 UTC, Timon Gehr wrote:On 29.09.2017 11:12, Don Clugston wrote: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.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.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.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).
Sep 29 2017
On 29.09.2017 17:05, Don Clugston wrote:Oh, I see. (They look the same to me.)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. ...This can be done with basically any control flow construct we have: if(i) for(i=8;i<10;++i) { // ... } (This relates to my point about consistency.)I don't see why. For example, my compiler implementation of SwitchStm does not mention BlockStm.I believe it makes it simpler. You cannot avoid the reference to BlockStatement.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).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. ...Well, I have used the switch(...) with(...) idiom you mentioned in the original post a few times, and I'm quite confident you'd meet some opposition if you were to break it. There's also this case: switch(x) static foreach(i;0..10) static if(i==0) default: return f!0(); else case i: return f!i(); and, of course, this one: final switch(x) static foreach(i;0..10) case i: return f!i();We improved the switch statement a lot by disallowing implicit fallthrough.Explicit fallthrough is still allowed though.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.Well, 'switch' does not uphold the principles of structured programming. It is natural that one can extract some silliness from that, and I don't think changing grammar rules in inconsistent ways in order to disallow particular silly examples is a very good way to design a language. I think removing () and requiring {} everywhere as Go and Rust have done is quite good language design which eliminates many of C's grammar issues, but it is not the path that D has taken and it does not improve switch semantics.
Sep 29 2017
On 2017-09-29 21:56, Timon Gehr wrote:Well, I have used the switch(...) with(...) idiom you mentioned in the original post a few times, and I'm quite confident you'd meet some opposition if you were to break it.I've used that as well, but the other way around. I put the switch statement inside the with statement. -- /Jacob Carlborg
Sep 30 2017
On Fri, Sep 29, 2017 at 12:32:02PM +0200, Timon Gehr via Digitalmars-d wrote: [...]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.I believe one of the reasons the grammar was written this way was to allow Duff's device in D. Personally, I don't care much for it -- an optimizing compiler ought to be able to generate the best code without requiring the programmer to explicitly write Duff's device. Though Walter did indicate that dmd's optimizer is lacking in the loop unrolling department. T -- It is not the employer who pays the wages. Employers only handle the money. It is the customer who pays the wages. -- Henry Ford
Sep 29 2017
On Friday, 29 September 2017 at 09:12:54 UTC, Don Clugston wrote:Or it may have just been an accident. But regardless of the original motivation, it allows some truly dreadful semantics. Can we disallow this silliness please?There are two big family of switches. C-like and Pascal like. In Pascal you can only have cases in the switch. See https://en.wikipedia.org/wiki/Switch_statement#Semantics The C form doesn't prevent you to be stricter but it's all about self-discipline since the compiler will allow everything in there.
Sep 30 2017