The problem is that this is different from C++, so probably we can not do the reasonable thing because it makes the "it must be exactly like C++" people upset.
1) ........................ The behavior is undefined if the right operand is negative,
or greater than or equal to the width of the promoted left operand.
2) The value of E1 << E2 is the unique value congruent to E1 × 2**E2 modulo 2**N, where
N is the width of the type of the result.
[Note 1 : E1 is left-shifted E2 bit positions; vacated bits are zero-filled. — end note]
3) The value of E1 >> E2 is E1 / 2**E2, rounded down.
[Note 2 : E1 is right-shifted E2 bit positions. Right-shift on signed integral types is
an arithmetic right shift, which performs sign-extension. — end note]
So you can adopt that as well. In any case: since C23/C++23 the integers are two's complement, so make them behave so. Again, I don't mind signed overflow being undefined all that much (although again, I feel like there has to be some middle ground between "full-blown UB and nasal demons" and "the implementations/sanitiziers are allowed to trap it instead of silently wrapping/assuming it never happens") so perhaps you can slightly deviate from C++ here: it apparently allows 1 << (INT_WIDTH - 1) to become INT_MIN, fully defined-ly, maybe this can still be UB in C? But (-1) << (whatever up to INT_WIDTH - 1) absolutely should be defined, it's just ridiculous otherwise.