www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - final switch and straight integers

reply Steven Schveighoffer <schveiguy yahoo.com> writes:
I was surprised that in my code, I had a final switch on an integer, and 
compiler never complained.

I looked at the rules here: 
http://dlang.org/spec/statement.html#final-switch-statement

Rules are:

     1. No DefaultStatement is allowed.
     2. No CaseRangeStatements are allowed.
     3. If the switch Expression is of enum type, all the enum members 
must appear in the CaseStatements.
     4. The case expressions cannot evaluate to a run time initialized 
value.

What if expression is not of enum type? Apparently that just means no 
default statement is required.

In other words:

void foo(int x)
{
    final switch(x) {
       case 0:
          break;
    }
}

compiles. I was surprised about this, apparently it just means throw an 
error if no case is matched. I don't see a lot of value in this over a 
straight switch statement. Is there something I'm missing?

I find this inconsistent with the enum version -- shouldn't I have to 
handle all possible values? I think either rule 3 should remove the 
qualifier "If the expression is of enum type" and qualify rule 2 which 
only would make sense for enums, or we should do away with requiring 
handling all enum cases.

-Steve
Apr 19 2016
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 19 April 2016 at 14:53:18 UTC, Steven Schveighoffer 
wrote:

or we
 should do away with requiring handling all enum cases.
Are you suggesting getting rid of final switch ?
Apr 19 2016
next sibling parent reply Dominikus Dittes Scherkl <Dominikus.Scherkl continental-corporation.com> writes:
On Tuesday, 19 April 2016 at 22:04:21 UTC, Stefan Koch wrote:
 On Tuesday, 19 April 2016 at 14:53:18 UTC, Steven Schveighoffer 
 wrote:

or we
 should do away with requiring handling all enum cases.
Are you suggesting getting rid of final switch ?
No - I think he suggests to error out if final switch is used on something other than enums. I support this suggestion, final switch makes no sense on things that are not enumerated. Even on ubyte almost nobody will ever list all 256 cases, not to mention larger types.
Apr 19 2016
parent reply bearophile <bearophileHUGS lycos.com> writes:
Dominikus Dittes Scherkl:

 final switch makes no sense on things that are not enumerated. 
 Even on ubyte almost nobody will ever list all 256 cases, not 
 to mention larger types.
It's easy to cover all the values in a switch, using ranges. No need to forbid final switch for integral values. It just needs to be implemented correctly. Bye, bearophile
Apr 19 2016
next sibling parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Wednesday, 20 April 2016 at 06:36:01 UTC, bearophile wrote:
 Dominikus Dittes Scherkl:

 final switch makes no sense on things that are not enumerated. 
 Even on ubyte almost nobody will ever list all 256 cases, not 
 to mention larger types.
It's easy to cover all the values in a switch, using ranges.
2. No CaseRangeStatements are allowed.
Apr 20 2016
parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Wednesday, 20 April 2016 at 07:18:55 UTC, Bastiaan Veelo wrote:
 On Wednesday, 20 April 2016 at 06:36:01 UTC, bearophile wrote:
 Dominikus Dittes Scherkl:

 final switch makes no sense on things that are not 
 enumerated. Even on ubyte almost nobody will ever list all 
 256 cases, not to mention larger types.
It's easy to cover all the values in a switch, using ranges.
2. No CaseRangeStatements are allowed.
Sorry, pressed Send too early. You mean implement to remove the above limitation?
Apr 20 2016
parent reply Dominikus Dittes Scherkl <Dominikus.Scherkl continental-corporation.com> writes:
On Wednesday, 20 April 2016 at 07:23:09 UTC, Bastiaan Veelo wrote:
 On Wednesday, 20 April 2016 at 07:18:55 UTC, Bastiaan Veelo 
 wrote:
 On Wednesday, 20 April 2016 at 06:36:01 UTC, bearophile wrote:
 Dominikus Dittes Scherkl:

 final switch makes no sense on things that are not 
 enumerated. Even on ubyte almost nobody will ever list all 
 256 cases, not to mention larger types.
It's easy to cover all the values in a switch, using ranges.
2. No CaseRangeStatements are allowed.
Sorry, pressed Send too early. You mean implement to remove the above limitation?
Anyway, something need to be changed. a) allow Range Cases (nice for ints but bad idea for enums) b) require also non-enum types to explicitly state all cases (bad idea for any multi-byte type, even near useless for single bytes) c) forbid other types than enum in final switch I strongly vote for (c).
Apr 20 2016
parent reply Basile Burg <b2.temp gmx.com> writes:
On Wednesday, 20 April 2016 at 10:19:17 UTC, Dominikus Dittes 
Scherkl wrote:
 Anyway, something need to be changed.
 a) allow Range Cases (nice for ints but bad idea for enums)
 b) require also non-enum types to explicitly state all cases 
 (bad idea for any multi-byte type, even near useless for single 
 bytes)
 c) forbid other types than enum in final switch

 I strongly vote for (c).
A good `int` value for a variable `int x` can be enforced (min(), max(), clamping, warping, etc) **before** a final switch(x). If c) is done then the compiler in this cas would disallow something that's completly safe (generally speaking I mean, here safe == no SwitchException possible).
Apr 20 2016
parent Dominikus Dittes Scherkl <Dominikus.Scherkl continental-corporation.com> writes:
On Wednesday, 20 April 2016 at 17:42:03 UTC, Basile Burg wrote:
 On Wednesday, 20 April 2016 at 10:19:17 UTC, Dominikus Dittes 
 Scherkl wrote:
 Anyway, something need to be changed.
 a) allow Range Cases (nice for ints but bad idea for enums)
 b) require also non-enum types to explicitly state all cases 
 (bad idea for any multi-byte type, even near useless for 
 single bytes)
 c) forbid other types than enum in final switch

 I strongly vote for (c).
A good `int` value for a variable `int x` can be enforced (min(), max(), clamping, warping, etc) **before** a final switch(x).
No, because final switch requires you to enumerate all possible cases.
 If c) is done then the compiler in this cas would disallow 
 something that's completly safe (generally speaking I mean, 
 here safe == no SwitchException possible).
Why would you ever want to use final switch on int? Why not simply use the normal switch? Especially if you enforced a useful range with min(), max(), etc. would it not be better to do the remaining cases manually? (or even do the range check in the default case?)
Apr 21 2016
prev sibling parent reply Johan Engelen <j j.nl> writes:
On Wednesday, 20 April 2016 at 06:36:01 UTC, bearophile wrote:
 It's easy to cover all the values in a switch, using ranges.
Not as easy as you would think: int i; switch(i) { case 0: .. case 9: break; case 10: ..case 10000000: break; default: break; } --> Error: had 9999990 cases which is more than 256 cases in case range The FE always lowers CaseRangeStatements into a list of CaseStatements, so LDC currently has the same limitation.
Apr 20 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/20/16 4:23 AM, Johan Engelen wrote:
 On Wednesday, 20 April 2016 at 06:36:01 UTC, bearophile wrote:
 It's easy to cover all the values in a switch, using ranges.
Not as easy as you would think: int i; switch(i) { case 0: .. case 9: break; case 10: ..case 10000000: break; default: break; } --> Error: had 9999990 cases which is more than 256 cases in case range The FE always lowers CaseRangeStatements into a list of CaseStatements, so LDC currently has the same limitation.
Um... this seems a horrid limitation. -Steve
Apr 21 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/19/16 6:04 PM, Stefan Koch wrote:
 On Tuesday, 19 April 2016 at 14:53:18 UTC, Steven Schveighoffer wrote:

 or we
 should do away with requiring handling all enum cases.
Are you suggesting getting rid of final switch ?
No, what I'm suggesting is that final switch behave consistently. For integers, final switch doesn't require all possible values be handled, but for enums it does. One way to make this consistent is to not require all enums be handled. I'm not suggesting that this is the best answer, just that it is a possible way to square the inconsistency. Note that even with final switch on an enum, you have issues, especially if the enum is considered to be a bitfield: enum bitfield { flag1 = 1 << 0, flag2 = 1 << 1 } ... void foo(bitfield x) final switch(x) { case flag1: writeln("flag1"); break; case flag2: writeln("flag2"); break; } This compiles, but doesn't handle flag1 | flag2. So final switch isn't helping to cover all cases. In other cases, you may have a member of an enum that isn't ever used as a value, but you have to cover it: enum message { foo, bar, mask = foo | bar } final switch is nice when you fit into the use case. When not, you have to fall back to normal switch. I find many times wanting to use final switch but having to follow the rules would make it look silly. -Steve
Apr 21 2016
parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Thursday, 21 April 2016 at 12:45:34 UTC, Steven Schveighoffer 
wrote:
 On 4/19/16 6:04 PM, Stefan Koch wrote:
 On Tuesday, 19 April 2016 at 14:53:18 UTC, Steven 
 Schveighoffer wrote:

 or we
 should do away with requiring handling all enum cases.
Are you suggesting getting rid of final switch ?
No, what I'm suggesting is that final switch behave consistently. For integers, final switch doesn't require all possible values be handled, but for enums it does. One way to make this consistent is to not require all enums be handled. I'm not suggesting that this is the best answer, just that it is a possible way to square the inconsistency. Note that even with final switch on an enum, you have issues, especially if the enum is considered to be a bitfield: enum bitfield { flag1 = 1 << 0, flag2 = 1 << 1 }
`final enum` was suggested for strict enums that can't be used as flags. If we had that, we could phase out `final switch` on non-strict enums.
Apr 21 2016