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.
Apparently, C++23 went on and fully defined it, see the sibling comment from leni536 [0]:
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.