> If your program is correct (it may-overflow like everything does, but doesn't dynamically overflow)
I don't follow the distinction here. A correct program should never invoke signed overflow, regardless of input.
> by undefining overflow, you can tell the compiler that it doesn't happen
Right, that's essentially the effect of the standard saying it's undefined behaviour: it should never happen when the code runs.
> That lets it reorder operations in a way it couldn't if every + potentially wrapped around.
Right, or more generally, it enables various compiler optimisations.
> I was proposing trapping on all undefined behavior in debug mode!
Ok, I thought that by If something's undefined you know it's a bug every time you see it you were saying that UB always results in a loud explosion.
Unfortunately it's not easy to build a C compiler that traps whenever UB is encountered at runtime. An example: the compiler can't know the size of an array passed to your library. C uses 'thin pointers', unlike most languages where, whenever you pass an array, the callee can inspect the array's length.
> Trapping is plausible, but that's not always how people want to fix undefined behavior.
Ada's solution, of raising exceptions (roughly like Java), seems sensible. Of course, part of C's appeal is that it's very compact and lacks things like exceptions.
> some people want undefined memory reads to return 0
That doesn't sound reasonable. To implement that could be pretty burdensome.
> or want overflow to wrap.
This is something some compilers support as a non-standard feature. GCC supports it with the -fwrapv flag. I suppose it would be friendlier if there were a standard and portable #pragma to tell the compiler what you want, but I'm not sure it's a big enough problem to make it into the standard.
You can 'fake it' pretty well by converting to a unsigned integer type, doing the arithmetic, and then converting back to the original signed integer type. You could write a function to do this. You could use the preprocessor to defer to a compiler-specific intrinsic where one is available. I think GCC's __builtin_add_overflow would do the job but its definition isn't terribly explicit regarding wrapping behaviour.
I think this code would do the job portably, and I don't think it relies on anything platform specific. (I'm relying on the signed/unsigned conversions using two's-complement, I believe this is guaranteed by the C/C++ language specs. I've also used fixed-length integer types for good measure.) Godbolt tells me GCC can optimise it down to a single LEA instruction on AMD64.
#include <cstdint>
using std::int32_t;
using std::uint32_t;
/*inline*/ int32_t wrapping_add_int32t(int32_t num1, int32_t num2)
{
return (int32_t)((uint32_t)num1 + (uint32_t)num2); // Compiles down to LEA instruction
// Alternatively (also compiles down to an LEA instruction)
// int32_t ret;
// __builtin_add_overflow(num1, num2, &ret);
// return ret;
}
See also [0].
> I don't like exceptions very much either because control flow gets more complicated. Trapping like Swift does is fine, though.
I agree it introduces action at a distance flow-control. I'm afraid I don't know Swift.
[0] https://stackoverflow.com/q/59307930/
Vaguely related fun: https://github.com/MaxBarraclough/IntegerAbsoluteDifferenceC...