Range of enum type values

Johan Engelen j at j.nl
Fri Dec 27 18:12:44 UTC 2019


On Friday, 27 December 2019 at 14:58:59 UTC, Steven Schveighoffer 
wrote:
> On 12/27/19 9:12 AM, Timon Gehr wrote:
>> On 27.12.19 13:14, Johan Engelen wrote:
>>>
>>> Current compiler behavior results in an infinite value range. 
>>> (but it's implicit behavior, i.e. not explicitly mentioned in 
>>> spec)
>>>
>>> - Currently, are operations resulting in a value larger than 
>>> the underlying integer storage type UB,
>> 
>> They are @safe. You can't have UB in @safe code.
>> 
>>> like for normal signed integers?
>> 
>> Signed integers have wraparound semantics.
>> https://forum.dlang.org/thread/n23bo3$qe$1@digitalmars.com

Thanks for the correction!
I hope someone finds the time to make that more explicit in the 
spec.

>> The spec mentions this for AddExpressions (but the example 
>> only shows it for uint): https://dlang.org/spec/expression.html
>> "If both operands are of integral types and an overflow or 
>> underflow occurs in the computation, wrapping will happen."
>> 
>> There simply _can't_ be any UB in signed integer operations, 
>> as they are considered @safe.

I don't accept this argument [*], but no argument needed here. 
Just needs some clarification in spec text.

>>> - Should we limit the range of valid values of the Flags enum 
>>> (C++ defines valid range to be [0..7])?
>>> - Do we want to limit operations allowed on enum types? Or 
>>> change the result type? (e.g. the type of `Flags + Flags` is 
>>> `int` instead of `Flags`.
>> 
>> I see those options:
>> 
>> 1. The valid range is the full range of the underlying type 
>> (as DMD treats it now).
>> 
>> 2. The range is [1..4]. In this case, the operations have to 
>> promote their operands to the enum base type, and most casts 
>> to enum types must be @system.
>> 
>> 3. The range is [0..7]. In this case, only operations that 
>> preserve this range (such as bitwise operators) should yield 
>> the enum type, and other operations should promote their 
>> operands to the enum base type, and most casts to enum types 
>> must be @system.
>> 
>> 
>> Personally, I think 2 makes most sense (especially with `final 
>> switch`, as the current semantics forces compilers to insert 
>> default cases there), but this would be a breaking language 
>> change.
>
> We have another option, which I like. That is, only allow 
> bitwise operations on enums that are flagged as allowing 
> bitwise operations (either with a uda, or via some other 
> mechanism). Many languages actually treat enums just like 
> structs, where you can add operators and functions. This is 
> also a possibility.
>
> This is also a breaking change, but also I don't want the 
> compiler complaining about final switch on enums where the enum 
> is intended not to be a bitwise flag. So I'd prefer 2 over 3.

Let's separate the discussion into what it _currently is_ and 
what _it might be in future_.

Current language behavior:
enum value range = full range of base type; integer operations 
work as-if the type is the base type.

Future:
Several options + lots of discussion ;-) and DIP needed.

Can I summarize it like that?

cheers,
   Johan



[*]
Let's not go off on a tangent, but there is enough UB in D that I 
do not accept that @safe=="no UB" argument. One example that 
comes to mind is bitshifting by more than the operand bit width: 
"illegal" is what the spec says but that doesn't make sense for 
runtime shift values and, in practice, turns into UB at runtime. 
;-)



More information about the Digitalmars-d mailing list