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