flagging unsigned subtraction assigned to bigger signed number?

Steven Schveighoffer schveiguy at gmail.com
Tue May 20 03:11:47 UTC 2025


I just spent about 15 minutes working on a problem where an 
unsigned subtraction converted to a float caused a totally 
unexpected value.

I had to printf debug this, because it was happening only at a 
certain point.

The code was something like this:

```d
foreach(i, v; messages) // messages is a string array
{
    float vpos = 600 - 30 * (messages.length - i);
    DrawText(messages.ptr, 100, vpos.to!int, 20, Colors.WHITE);
}

```

This is raylib code, and what ends up happening is that once 
messages.length exceeds 20, you get into a situation where `600 - 
30 * (messages.length - i)` wraps to ulong.max - 29. And 
obviously, that doesn't convert to an int.

So the diabolical thing about this, is that I focused on the 
`messages.length - i` because that is unsigned math, but there is 
zero chance this could wrap. So I was puzzled how this could be 
causing overflow.

But the unsigned-ness percolates throughout the whole expression!

I was brainstorming how we could flag this as error prone without 
flooding all projects with warnings that won't (usually) be a 
problem.

I thought of this possible filter:

1. Unsigned subtraction happens
2. The result is used as a signed number
3. The signed number's range encloses the range of all values of 
the unsigned value.

So that last point might seem puzzling, why do we not care about 
converting signed/unsigned integers of the same width? We don't 
care because actually it works out exactly as expected:

e.g.

```d
uint x = 100;
int v = x - 200;
assert(v == -100); // OK!
```

But if you assign to a type which actually can represent all the 
uint (or at least the range of uint), then you get bombs:

```d
int x = 100;
double v = x - 200;
assert(v == -100); // BOOM
long v2 = x - 200;
assert(v2 == -100); // BOOM
```

So why not just flag all unsigned/signed conversions? The problem 
with this idea (and technically it is a sound idea) is that it 
will flag so many things that are likely fine. Flagging all 
conversions just is going to be extremely noisy, and require 
casts everywhere. Subtraction is where the problems are.

In cases where the compiler knows that unsigned subtraction won't 
wrap, it can also skip the warning.

Thoughts?

-Steve


More information about the Digitalmars-d mailing list