Re: Feature request: enum init shouldn't create a new enumeration

2012-10-17 Thread Daniel Murphy
https://github.com/D-Programming-Language/dmd/pull/673

Tommi tommitiss...@hotmail.com wrote in message 
news:ozvdphftlwxkeumrn...@forum.dlang.org...
 On Monday, 15 October 2012 at 08:25:18 UTC, Tommi wrote:
 in reality, it's very easy to write a bug that makes an enum variable 
 have an invalid value. E.g:

 Writing that bug wasn't as easy as I thought though. Here's the code with 
 a bug:

 enum MyEnum { first, second, third }

 auto me = MyEnum.min;

 while (me = MyEnum.max)
 {
 // do something
 ++me;
 }

 final switch (me) // this should throw
 {
 case MyEnum.first:  break;
 case MyEnum.second: break;
 case MyEnum.third:  break;
 }

 Instead, currently this code runs just fine, none of the switch cases are 
 taken. So, this bug might manifest itself very far from where it actually 
 should have been throwing: on that final switch expression. Thus hunting 
 this bug down could be tedious. 




Re: Feature request: enum init shouldn't create a new enumeration

2012-10-15 Thread Tommi

On Sunday, 14 October 2012 at 19:40:17 UTC, Nick Sabalausky wrote:
Yes, but it still has to be taken into account in things like 
final switch. You can't just pretend that nothing will ever

have that value, because by making it the init value, you've
*made* it an actual possible value, one that just happens to
indicate uninitialized.


*I* know that named enum variables can have whatever values; it's 
rather *you* and DMD who are pretending that enum variables can 
have only those values which are specifically enumerated.


You say that final switch should take into account an enum init 
value which can be used to represent an invalid value. I say that 
final switch shouldn't consider that (invalid) init value any 
different from all the other values that are invalid for that 
specific enum type, that is: all the values that are not 
speficied explicitly by the enumerations. And the way final 
switch should take all those invalid values into account, is by 
throwing an error when a final switch switches on a value not 
specifically defined by the enumerations.


Dmd also seems blinded into thinking that the specified 
enumerations are all that an enum variable can ever be, while in 
reality, it's very easy to write a bug that makes an enum 
variable have an invalid value. E.g:


enum MyEnum { first, second, third }

auto me = MyEnum.min;

while (me  MyEnum.max)
{
// do something
++me;
}

switch (me) // this should throw
{
case MyEnum.first:  break;
case MyEnum.second: break;
case MyEnum.thrird: break;
}


Re: Feature request: enum init shouldn't create a new enumeration

2012-10-15 Thread Tommi

On Monday, 15 October 2012 at 08:25:18 UTC, Tommi wrote:
in reality, it's very easy to write a bug that makes an enum 
variable have an invalid value. E.g:


Writing that bug wasn't as easy as I thought though. Here's the 
code with a bug:


enum MyEnum { first, second, third }

auto me = MyEnum.min;

while (me = MyEnum.max)
{
// do something
++me;
}

final switch (me) // this should throw
{
case MyEnum.first:  break;
case MyEnum.second: break;
case MyEnum.third:  break;
}

Instead, currently this code runs just fine, none of the switch 
cases are taken. So, this bug might manifest itself very far from 
where it actually should have been throwing: on that final switch 
expression. Thus hunting this bug down could be tedious.


Re: Feature request: enum init shouldn't create a new enumeration

2012-10-14 Thread Tommi
On Saturday, 13 October 2012 at 20:25:56 UTC, Jonathan M Davis 
wrote:


 MyEnum me;

 final switch (me) // no init case necessary nor allowed
 {
 case MyEnum.first:  break;
 case MyEnum.second: break;
 }
}


Think about that for a moment. What happens when that final 
switch statement is actually run?


There's a bug in that code, because MyEnum default-initializes to 
an invalid value. It's effectively the same as this following 
code, where the programmer has failed to initialize MyEnum 
variable with a valid value:


enum MyEnum { first, second }

void main()
{
MyEnum me = cast(MyEnum)(-123);

final switch (me)
{
case MyEnum.first:  break;
case MyEnum.second: break;
}
}

I think that the final switch statement above should throw an 
unrecoverable error. I tested it, and nothing happens. I strongly 
disagree with this behavior of the compiler (a bug perhaps?).


And remember, that in many cases, T.init is considered to be 
perfectly valid. By allowing a final switch _not_ to have it,

it then becomes easy to forget to add a case for it when you
_need_ to, completely defeating the purpose of the final
switch (to make it so that both you and the compiler know that 
all of the possible values are accounted for).


If T.init is considered to be perfectly valid, then it means, 
that a synonym for it exists among the enumerations. E.g:


enum MyEnum  { init = 1, first = 1, second = 42 }
enum ThyEnum { first, second }

In both of those cases, T.first is the synonym of T.init, which 
is what both of those enums default-initialize to. Therefore, if 
T.init is a valid value, and thus has a synonym among the 
enumerations of T, then you can't add a case for init in a final 
switch:


MyEnum me;

final switch (me)
{
MyEnum.first:  break;
MyEnum.second: break;
MyEnum.init:   // Error: duplicate case cast(MyEnum)1
   // in switch statement
}

So, this situation you describe, where you *need* to add a case 
for init in a final switch, it doesn't exist. T.init should 
always either 1) have a synonym among the enumerations or 2) 
represent an invalid value.


Re: Feature request: enum init shouldn't create a new enumeration

2012-10-14 Thread Jonathan M Davis
On Sunday, October 14, 2012 08:20:40 Tommi wrote:
 There's a bug in that code, because MyEnum default-initializes to
 an invalid value. It's effectively the same as this following
 code, where the programmer has failed to initialize MyEnum
 variable with a valid value:

Valid or not, MyEnum.init is still a value for MyEnum and _must_ be accounted 
for. Trying to treat MyEnum.init as not being a value of MyEnum is likely to 
break all kinds of stuff. Being able to have a variable of an enum type with a 
value which is not really a member of the enum is just plain broken.

And honestly, declaring a specific init value for an enum is a stupid idea. 
It's going to screw with all kinds of stuff. Anything assuming that the init 
property is the first value (is it is in _all_ other cases but isn't 
necessarily if you declare your own) will be broken. It's still going to end 
up being in stuff like std.traits.EnumMembers or pretty much anything which 
operates on enums unless all kinds of special casing is added. TDPL does 
mention (p. 275) that you can declare enum members with the names max, min, 
and init, but it also points out that it's a dumb idea. I'd argue that it 
shouldn't even be legal at all. It's just begging for trouble.

- Jonathan M Davis


Re: Feature request: enum init shouldn't create a new enumeration

2012-10-14 Thread Tommi
On Sunday, 14 October 2012 at 06:51:48 UTC, Jonathan M Davis 
wrote:
And honestly, declaring a specific init value for an enum is a 
stupid idea.


I think that declaring a specific *valid* init value for an enum 
has no purpose. But declaring a specific *invalid* init value, to 
which the enum initializes to by default, is a very good idea. 
The reason for why it's a good idea, is exactly the same as why 
default-initializing floating point variables to NaN is a good 
idea. Others have reasoned about that enough, thus I don't have 
to.


On Sunday, 14 October 2012 at 06:51:48 UTC, Jonathan M Davis 
wrote:

... It's going to screw with all kinds of stuff.


True, it would break code.





Re: Feature request: enum init shouldn't create a new enumeration

2012-10-14 Thread Nick Sabalausky
On Sun, 14 Oct 2012 09:16:28 +0200
Tommi tommitiss...@hotmail.com wrote:

 On Sunday, 14 October 2012 at 06:51:48 UTC, Jonathan M Davis 
 wrote:
  And honestly, declaring a specific init value for an enum is a 
  stupid idea.
 
 I think that declaring a specific *valid* init value for an enum 
 has no purpose. But declaring a specific *invalid* init value, to 
 which the enum initializes to by default, is a very good idea. 
 The reason for why it's a good idea, is exactly the same as why 
 default-initializing floating point variables to NaN is a good 
 idea. Others have reasoned about that enough, thus I don't have 
 to.
 

Yes, but it still has to be taken into account in things like final
switch. You can't just pretend that nothing will ever have that value,
because by making it the init value, you've *made* it an actual
possible value, one that just happens to indicate uninitialized.



Feature request: enum init shouldn't create a new enumeration

2012-10-13 Thread Tommi
I'd like to be able to specify a default value for a named enum, 
E.init, without creating a new enumeration. There are three 
reasons:

1) Default initializing enum variables to an invalid value
2) Being able to use 'final switch' without the 'init' case
3) Invalid init value wouldn't affect E.min or E.max

Here's what currently happens:

enum MyEnum
{
init = -123,
first = 0,
second = 1
}

void main()
{
static assert(MyEnum.min == -123);

MyEnum me;

final switch (me)
{
case MyEnum.first:  break;
case MyEnum.second: break;
case MyEnum.init: // I'm forced to specify init case too
}
}

This is what I'd like to happen:

enum MyEnum
{
init = -123,
first = 0,
second = 1
}

void main()
{
static assert(MyEnum.min == 0); // no effect on min/max

MyEnum me;

final switch (me) // no init case necessary nor allowed
{
case MyEnum.first:  break;
case MyEnum.second: break;
}
}


Re: Feature request: enum init shouldn't create a new enumeration

2012-10-13 Thread denizzzka

On Saturday, 13 October 2012 at 15:39:24 UTC, Tommi wrote:

enum MyEnum
{
init = -123,
first = 0,
second = 1
}

void main()
{
static assert(MyEnum.min == -123);

MyEnum me;

final switch (me)
{
case MyEnum.first:  break;
case MyEnum.second: break;
case MyEnum.init: // I'm forced to specify init case too
}
}



Also, a quick question:

Why in case its need to write name of the enum?

 case first:  break;
 case second: break;
 case init: // I'm forced to specify init case too

looks better for me.


Re: Feature request: enum init shouldn't create a new enumeration

2012-10-13 Thread bearophile

denizzzka:


Why in case its need to write name of the enum?


Because D enums have a very simple design. But with() helps.

Bye,
bearophile


Re: Feature request: enum init shouldn't create a new enumeration

2012-10-13 Thread Ali Çehreli

On 10/13/2012 11:01 AM, bearophile wrote:

denizzzka:


Why in case its need to write name of the enum?


Because D enums have a very simple design. But with() helps.


For me, that is the only benefit of 'with':

final switch (me) with (MyEnum)
{
case first:  break;
case second: break;
case init: break;
}

The other uses of 'with' are more like obfuscations.



Bye,
bearophile


Ali


Re: Feature request: enum init shouldn't create a new enumeration

2012-10-13 Thread Jonathan M Davis
On Saturday, October 13, 2012 17:39:23 Tommi wrote:
 I'd like to be able to specify a default value for a named enum,
 E.init, without creating a new enumeration. There are three
 reasons:
 1) Default initializing enum variables to an invalid value
 2) Being able to use 'final switch' without the 'init' case
 3) Invalid init value wouldn't affect E.min or E.max
 
 Here's what currently happens:
 
 enum MyEnum
 {
  init = -123,
  first = 0,
  second = 1
 }
 
 void main()
 {
  static assert(MyEnum.min == -123);
 
  MyEnum me;
 
  final switch (me)
  {
  case MyEnum.first:  break;
  case MyEnum.second: break;
  case MyEnum.init: // I'm forced to specify init case too
  }
 }
 
 This is what I'd like to happen:
 
 enum MyEnum
 {
  init = -123,
  first = 0,
  second = 1
 }
 
 void main()
 {
  static assert(MyEnum.min == 0); // no effect on min/max
 
  MyEnum me;
 
  final switch (me) // no init case necessary nor allowed
  {
  case MyEnum.first:  break;
  case MyEnum.second: break;
  }
 }

Think about that for a moment. What happens when that final switch statement is 
actually run? Which statement would MyEnum.init use? The whole point of final 
switch is that the compile _knows_ that every single value for that type has a 
case. With your suggestion, it specifically _doesn't_ have a case for one of 
the type's values. And it _will_ happen at some point that you'll hit a switch 
like that with the init value rather than a valid one. I don't see how that 
can possibly work or make any sense at all.

And remember, that in many cases, T.init is considered to be perfectly valid. 
By allowing a final switch _not_ to have it, it then becomes easy to forget to 
add a case for it when you _need_ to, completely defeating the purpose of the 
final switch (to make it so that both you and the compiler know that all of the 
possible values are accounted for).

- Jonathan M Davis