Hey Chris!
I have been trying to dip my toes into using signed types more and I
found something really annoying about left shifting in C:
https://godbolt.org/z/sMWj9s63Y
As you can see, what is essentially "1 << 31" is reported by UBSAN as an
error, but only in C, even when the standard is set to C23 and C++23, by
which point both languages adapted to reality where only 2's complement
integers exist.
Apparently, the C++ standard specifies that for bit-wise operators, the
operands are used as bags of bits, which results in the desirable behavior.
In C, you have to cast to the bit-width equivalent unsigned type and
then back to mimic the same behavior. I have not looked at what
standardese results in this difference, but the outcome is the same in
both GCC and Clang.
Just thought I'd let you know and I'm curious if you ever came across this.
Yup, I've run into it frequently in the wild, usually on left shift by 24
when assembling integers from bytes. For example, there's an instance in
Rob Pike's otherwise excellent byte order fallacy article:
https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
Cryptographic hash implementations often get it wrong, too. For example,
here's a case in first result searching for "sha256.c":
https://github.com/B-Con/crypto-algorithms/blob/cfbde484/sha256.c#L49
It's not *really* about 2's complement, or lack thereof, but that C
considers shifts arithmetic operators rather than bitwise as people
usually think about them. From that point of view, 1<<31 cannot be
represented, so it's an overflow. WG14 ought to follow C++ and remove the
UB from C in this particular case. There should be less (but not zero) UB,
and this is one that doesn't make sense.