Time to move std.experimental.checkedint to std.checkedint ?

tsbockman thomas.bockman at gmail.com
Wed Mar 24 20:28:39 UTC 2021


On Tuesday, 23 March 2021 at 21:26:43 UTC, mw wrote:
> https://issues.dlang.org/show_bug.cgi?id=21169
>
>  Issue 21169 - make checkedint as a drop-in replacement of 
> native int/long

Years ago I submitted a checkedint module of my own for inclusion 
in Phobos (https://code.dlang.org/packages/checkedint), which was 
ultimately rejected by Andrei Alexandrescu because my design 
goals did not align with his well enough, prompting him to write 
what became std.experimental.checkedint himself.

Maximum convenience and similarity to D's native integer types 
were high priorities for me, so I spent a lot of time thinking 
about and experimenting with this problem. My conclusions:

///////////////////////////////////
1) Checked types are different from unchecked types. That's the 
whole point!

I found that trying too hard to make transitioning between 
checked and unchecked types seamless created holes in the 
automated protection against overflow that the checked types are 
supposed to provide. Implicit conversions from checked to 
unchecked integers are dangerous for the same reason that 
implicit conversions from @system to @safe delegates are 
dangerous.

I think the urge to make that transition seamless comes from the 
fact that trying to actually use checkedint (whether mine or 
Andrei's) for defensive programming is extremely tedious and 
annoying, because no one else is doing so. But, this is the wrong 
solution: the real answer is that checked operations should have 
been the default in D from the beginning, with unchecked 
intrinsics available for those rare cases where wrapping overflow 
and other strange behaviors of machine integers are actually 
desired, or where maximum performance is needed.

Unchecked integer operations are mostly just a micro-optimization 
that is pointless outside of very hot code, like inner loops. (It 
is very puzzling that people consider memory safety so important, 
and yet are totally disinterested in integer overflow, which can 
violate memory safety.)

2) While there are many things that can be done to make the 
behavior of two types more similar, it is impossible in D to make 
any custom type an actual drop-in replacement for a different 
type.

This is because D, by design, has only partial support for 
implicit conversions, and because template constraints and 
overload resolution are sensitive to the exact type of the 
arguments.

Thus, whether to treat two different types as equivalent is 
ultimately a choice that each and every API that may interact 
with those types makes for itself, either intentionally or by 
accident. For example:

V f(V)(V value)
     if(std.traits.isIntegral!V)
{
     // Do something here ...
}

The perfectly reasonable template constraint above rejects 
checkedint types. Should it? There is no way to answer this 
question without seeing and understanding the body of the 
function: while uncommon, it is valid and sometimes desirable to 
depend upon wrapped integer overflow. So, the API designers must 
explicitly permit checkedint inputs if they consider that 
desirable.

Automating good solutions to these ambiguities is possible in 
many cases, but would require deep, breaking, and controversial 
changes to the D language.
///////////////////////////////////

TLDR; What you're really asking for is impossible in D2. It would 
require massive breaking changes to the language to implement 
without undermining the guarantees that a checked integer type 
exists to provide.


More information about the Digitalmars-d mailing list