March 01, 2013 Re: bit-level logic operations on enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Friday, 1 March 2013 at 21:27:25 UTC, Steven Schveighoffer wrote: > Then, when determining the type of an enum operation, if the two operands are of the same enum type (meaning, they haven't gone through any integer promotion), the result is the same enum type. > > So this unintuitively results in: > > enum A : int { a } > enum B : byte { b } > > A a; > B b; > writeln(typeof(a | a).stringof); // => A > writeln(typeof(b | b).stringof); // => int (byte is promoted to int) > writeln(typeof(a | b).stringof); // => int > writeln(typeof(a | 1).stringof); // => int > writeln(typeof(b | 1).stringof); // => int The only way the first makes sense is the compiler knows that it's the same value and is a no-op, thereby doesn't lose it's enum state; At which case b|b should do the same thing. However I'm quite sure the following is true, as otherwise it wouldn't make sense (Unless it knows the enum can only ever be 0 and doesn't matter?) writeln(typeof(a | A.a).stringof); // => int writeln(typeof(A.a | a).stringof); // => int > I would push for one of 4 behaviors, which make some sense to me: > > 1. Math operations between two enum values of the same type ALWAYS result in the same enum type regardless of base type (like int-based enums) Isn't this the same (or close enough) as 4? > 2. Math operations between two enum values of the same type ALWAYS result in the base type (full-scale enforcement of "enum only contains values from the identified list") > 3. Operations between two enums or between an enum and an int, that result at compile-time in a valid member of the enum result in the enum type. Otherwise, the operation is converted to the base type. It might be okay to do it against compile-time known values as it can then confirm if the value is even possible, but this seems inconsistent, and can then break code if the enums values change. > 4. Enums can be assigned any value from its base type, or any value implicitly convertible to that base type. Then 'final switch' won't work, nor could you know if the value was valid at any point and enums would be easy to abuse. > As it stands now, the compiler makes a half-assed attempt to prevent invalid enum values, but fails miserably in some cases, and is overzealous in others, which is in fact worse than either one alone! At this point, you can't say anything about enums that is always valid except the manifest-constant usage of them. > > TDPL is puzzlingly silent on enum math. Regardless it should likely be consistent with how it handles math and binary operators and enums. |
March 01, 2013 Re: bit-level logic operations on enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Era Scarecrow | On Fri, 01 Mar 2013 17:24:22 -0500, Era Scarecrow <rtcvb32@yahoo.com> wrote: > On Friday, 1 March 2013 at 21:27:25 UTC, Steven Schveighoffer wrote: >> Then, when determining the type of an enum operation, if the two operands are of the same enum type (meaning, they haven't gone through any integer promotion), the result is the same enum type. >> >> So this unintuitively results in: >> >> enum A : int { a } >> enum B : byte { b } >> >> A a; >> B b; >> writeln(typeof(a | a).stringof); // => A >> writeln(typeof(b | b).stringof); // => int (byte is promoted to int) >> writeln(typeof(a | b).stringof); // => int >> writeln(typeof(a | 1).stringof); // => int >> writeln(typeof(b | 1).stringof); // => int > > The only way the first makes sense is the compiler knows that it's the same value and is a no-op, thereby doesn't lose it's enum state; At which case b|b should do the same thing. However I'm quite sure the following is true, as otherwise it wouldn't make sense (Unless it knows the enum can only ever be 0 and doesn't matter?) > > writeln(typeof(a | A.a).stringof); // => int > writeln(typeof(A.a | a).stringof); // => int I was sure too. It's not. Any operation between A types results in A. writeln(typeof(a + a).stringof); // => A writeln(typeof(a << a).stringof); // => A writeln(typeof(a * a).stringof); // => A > >> I would push for one of 4 behaviors, which make some sense to me: >> >> 1. Math operations between two enum values of the same type ALWAYS result in the same enum type regardless of base type (like int-based enums) > > Isn't this the same (or close enough) as 4? It would make math between enums and integers type as integers, which would then not be assignable back to the enum. In fact, this is exactly how it works for ints. Not so for smaller types. I'm just going for consistency. > >> 2. Math operations between two enum values of the same type ALWAYS result in the base type (full-scale enforcement of "enum only contains values from the identified list") > >> 3. Operations between two enums or between an enum and an int, that result at compile-time in a valid member of the enum result in the enum type. Otherwise, the operation is converted to the base type. > > It might be okay to do it against compile-time known values as it can then confirm if the value is even possible, but this seems inconsistent, and can then break code if the enums values change. I'm ok with breaking code if the result is a more consistent enum feature. >> 4. Enums can be assigned any value from its base type, or any value implicitly convertible to that base type. > > Then 'final switch' won't work, nor could you know if the value was valid at any point and enums would be easy to abuse. You can already do this. See above. -Steve |
March 01, 2013 Re: bit-level logic operations on enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Fri, 01 Mar 2013 18:00:57 -0500, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> I was sure too. It's not. Any operation between A types results in A.
>
> writeln(typeof(a + a).stringof); // => A
> writeln(typeof(a << a).stringof); // => A
> writeln(typeof(a * a).stringof); // => A
I should add that in my test code (not the code posted previously), A is defined as:
enum A
{
a = 2
}
So the zero theory is out. All those result in a non-member value.
-Steve
|
March 02, 2013 Re: bit-level logic operations on enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Friday, 1 March 2013 at 23:00:58 UTC, Steven Schveighoffer wrote:
> I was sure too. It's not. Any operation between A types results in A.
>
> writeln(typeof(a + a).stringof); // => A
> writeln(typeof(a << a).stringof); // => A
> writeln(typeof(a * a).stringof); // => A
It has to be a bug, as it doesn't make sense for it to be legal.
|
March 02, 2013 Re: bit-level logic operations on enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Era Scarecrow | On Fri, 01 Mar 2013 19:22:14 -0500, Era Scarecrow <rtcvb32@yahoo.com> wrote: > On Friday, 1 March 2013 at 23:00:58 UTC, Steven Schveighoffer wrote: >> I was sure too. It's not. Any operation between A types results in A. >> >> writeln(typeof(a + a).stringof); // => A >> writeln(typeof(a << a).stringof); // => A >> writeln(typeof(a * a).stringof); // => A > > It has to be a bug, as it doesn't make sense for it to be legal. Read the spec, carefully. It's implemented as designed. http://dlang.org/type.html Implicit conversion: "A enum can be implicitly converted to its base type, but going the other way requires an explicit conversion." But for integer promotion: "If a enum has as a base type one of the types in the left column, it is converted to the type in the right column." The left column referred to does not have anything 32-bit or above in it besides dchar. Remember that. Later on, regarding arithmetic operations: If one or both of the operand types is an enum after undergoing the [Integer promotion] conversions, the result type is: * If the operands are the same type, the result will be the (sic) that type. * If one operand is an enum and the other is the base type of that enum, the result is the base type. * If the two operands are different enums, the result is the closest base type common to both. A base type being closer means there is a shorter sequence of conversions to base type to get there from the original type. Note that if no integer promotion occurs (i.e. base type is 32 bits or above and not dchar), and you are adding A with A, both operands are of the same type, so "the result will be that type." Just bad design IMO. It should be fixed. -Steve |
March 02, 2013 Re: bit-level logic operations on enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Saturday, 2 March 2013 at 00:36:19 UTC, Steven Schveighoffer wrote: > Read the spec, carefully. It's implemented as designed. > > http://dlang.org/type.html > > Implicit conversion: "A enum can be implicitly converted to its base type, but going the other way requires an explicit conversion." > > But for integer promotion: > > "If a enum has as a base type one of the types in the left column, it is converted to the type in the right column." > > The left column referred to does not have anything 32-bit or above in it besides dchar. Remember that. > > Later on, regarding arithmetic operations: > > If one or both of the operand types is an enum after undergoing the conversions, the result type is: > * If the operands are the same type, the result will be the (sic) that type. > * If one operand is an enum and the other is the base type of that enum, the result is the base type. > * If the two operands are different enums, the result is the closest base type common to both. A base type being closer means there is a shorter sequence of conversions to base type to get there from the original type. > Note that if no integer promotion occurs (I.e. base type is 32 bits or above and not dchar), and you are adding A with A, both operands are of the same type, so "the result will be that type." I've heard more than once the spec changing to fit an implementation. Still seems wrong. The enum's converted (not promoted) to it's numeric version and cannot be converted back. The details quoted are regarding integer promotion. If it's intended to be that way (enums + math/binary operators = same enum type) then the spec is flat out wrong. Following the simple logic and feature for 'final switch' that it's guaranteed to handles ALL cases and combinations of what the enum could be (thus there's no default); but it can't handle the cases presented. An enum (unless forcibly cast) should always be valid; So anything that could potentially modify the enum can only result in it's base type. > Just bad design IMO. It should be fixed. Agreed, needs to be fixed. |
Copyright © 1999-2021 by the D Language Foundation