What the heck am i doing wrong? I'm just trying to create a 8 bit unsigned variable.
Jonathan M Davis
newsgroup.d at jmdavisprog.com
Sat May 17 13:09:40 UTC 2025
On Friday, May 16, 2025 1:19:41 PM Mountain Daylight Time H. S. Teoh via Digitalmars-d-learn wrote:
> On Fri, May 16, 2025 at 07:04:24PM +0000, WhatMeWorry via Digitalmars-d-learn wrote:
> [...]
> > void main()
> > {
> > ubyte a;
> > a = a + 5; // onlineapp.d(11): Error: cannot implicitly convert expression `cast(int)a + 5` of type `int` to `ubyte`
> [...]
>
> Welcome to your first encounter with why I hate D's narrow integer
> promotion rules.
>
> Basically, `a + 5` gets promoted to int, and that's why it can't be
> assigned back to `a`. (Don't ask me why, that's just the way it is and
> Walter has refused and probably will continue to refuse to change it.)
It's because C (and I'm pretty sure the CPU as well) promotes integer types
smaller than 32 bits to 32 bits to do arithmetic on them. So, in both C and
D, the result is int. In general, C code is supposed to have the same
semantics in D, or it's not supposed to be compile, and Walter is insistent
that the behavior of arithmetic follows that (the one exception I'm aware of
being that D defines what happens with overflow whereas C does not).
The difference between C and D is then that D doesn't allow narrowing
conversions. This prevents certain classes of bugs, but when it's combined
with the fact that smaller integer types get promoted to int when doing
arithmetic, it can definitely become annoying if you want to do arithmetic
with smaller integer types.
In some circumstances, VRP (value range propagation) will make it so that
the D compiler is able to determine that the result will fit in the smaller
integer type, so it will then do the implicit conversion, but because Walter
tends to hate data flow analysis, it mostly just works in pretty basic
situations.
> Ironically, this is OK:
>
> ```
> ubyte a;
> a += 5;
> ```
>
> But writing it as `a = a + 5;` is not.
This is because with +=, you don't get a result that's int. It's just
mutating the ubyte, whereas a + 5 _does_ result in int, and you can't
implicitly convert int to ubyte, because it's a narrowing conversion. It is
kind of ugly that it works in one case but not the other, but it does make
logical sense.
Given that Java and C# disallow narrowing conversions, I expect that they
have similar restrictions, but I haven't done much with either of them any
time recently, so I'm not sure what they do with smaller integer types and
arithmetic. It's possible that they allow the narrowing conversion in some
specific cases in order to be more user-friendly, but they may also treat it
more or less the same way that D does. C and C++ avoid it because they allow
narrowing conversions and thus invisibly truncate the result, which creates
its own set of problems - but it does also mean that some code that doesn't
truncate the values just works, whereas in languages that don't allow
narrowing conversions, you typically have to cast, which of course can be
annoying. But that's the price of avoiding invisible truncation with
arithmetic. It's just that the fact that smaller integer types get converted
to int for arithmetic makes it worse, though if they weren't, you'd be
getting integer overflow with them pretty frequently, I expect.
- Jonathan M Davis
More information about the Digitalmars-d-learn
mailing list