https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119170
--- Comment #10 from Alejandro Colomar <alx at kernel dot org> --- (In reply to Kang-Che Sung from comment #9) > Is there still room to make comments about the proposal? Yes. This will not be voted for inclusion in the standard until around 2025-09, and the proposal must be ready more than one month before that date. So, we have until around 2025-07 to clear any issues and give me time to write the definitive proposal. > ### `_Widthof(Type)` > > The main reason (to me) for justifying the inclusion of `_Widthof` in the > standard is to allow easy retrieval of bit width of **bit-precise integer > types a.k.a. `_BitInt(N)`**, as the traditional expression `sizeof(Type) * > CHAR_BIT` won't work for retrieving the width of them. Correct. > The `_Widthof` > operator working with traditional integer types is a plus but not a > necessity. Here's an example of a use case of _Widthof with normal integers: $ grepc -htfd csrand_uniform32 . static uint32_t csrand_uniform32(uint32_t n) { uint32_t bound, rem; uint64_t r, mult; if (n == 0) return csrand32(); bound = -n % n; // analogous to `2^32 % n`, since `x % y == (x-y) % y` do { r = csrand32(); mult = r * n; rem = mult; // analogous to `mult % 2^32` } while (rem < bound); // p = (2^32 % n) / 2^32; W.C.: n=2^31+1, p=0.5 r = mult >> WIDTHOF(n); // analogous to `mult / 2^32` return r; } Admittedly, I could hard-code a 32 there (just like it's hard-coded in the function name, and local variables). However, I think that's more brittle, and if I paste this code elsewhere and make the function work for uint64_t, I'd like to need minimal changes, so WIDTHOF() helps make it more robust. > > I think @alejandro-colomar can add the `sizeof` on `_BitInt(N)` issue as a > primary motive for introducing `_Widthof` in the standard. (This doesn't > change the standard text, just amendment on the Motive section.) It's already there in the paper: #define WIDTHOF(x) (sizeof(x) * CHAR_BIT) #define SMAXOF(T) ((T) (((((T) 1 << (WIDTHOF(T) - 2)) - 1) << 1) + 1)) #define UMAXOF(T) ((T) -1) #define IS_SIGNED(x) ((typeof(x)) -1 < 1) #define MAXOF(T) ((T) (IS_SIGNED(T) ? SMAXOF(T) : UMAXOF(T)) #define MINOF(T) ((T) ~type_max(T)) As can be seen from my implementations, they are hard to write correctly, and review. Also, now that we have bit-precise integers, one may want to get these to work with them, which would make it even harder to implement them. I show the implementation with CHAR_BIT and that making it work for _BitInt would be harder (it would involve popcount, and can't make it a constant expression at the moment). > There is one thing that's is useful to clarify (can add it in a footnote of > the standard text): Should `_Widthof(bool)` be 1? By literal interpretation > of the semantics it sounds like so, but it is useful to note that explicitly. Yes, _Bool must have a width of 1. > ### `_Minof(Type)` and `_Maxof(Type)` > > I'm personally skeptical of these two. And, if I have the decision power, I > wish these two operators be made as votes separate from the `_Widthof` > voting. I'm on the other side: I could live without _Widthof (except for the use within csrand_uniform32(), I only use it for implementing _Minof and _Maxof), but certainly need _Minof and _Maxof often. The following code can be found in shadow-utils <https://github.com/shadow-maint/shadow> $ grepc -tu ' type_m[ia][nx]' lib* src/ lib/atoi/getnum.h:inline int get_gid(const char *restrict gidstr, gid_t *restrict gid) { return a2i(gid_t, gid, gidstr, NULL, 10, type_min(gid_t), type_max(gid_t)); } lib/atoi/getnum.h:inline int get_pid(const char *restrict pidstr, pid_t *restrict pid) { return a2i(pid_t, pid, pidstr, NULL, 10, 1, type_max(pid_t)); } lib/atoi/getnum.h:inline int get_uid(const char *restrict uidstr, uid_t *restrict uid) { return a2i(uid_t, uid, uidstr, NULL, 10, type_min(uid_t), type_max(uid_t)); } lib/limits.c:static int setrlimit_value (unsigned int resource, const char *value, unsigned int multiplier) { rlim_t l, limit; struct rlimit rlim; /* The "-" is special, not belonging to a strange negative limit. * It is infinity, in a controlled way. */ if ('-' == value[0]) { limit = RLIM_INFINITY; } else { if (a2i(rlim_t, &l, value, NULL, 10, 0, type_max(rlim_t)) == -1 && errno != ENOTSUP) { return 0; // FIXME: We could instead throw an error, though. } if (__builtin_mul_overflow(l, multiplier, &limit)) { /* FIXME: Again, silent error handling... * Wouldn't screaming make more sense? */ return 0; } } rlim.rlim_cur = limit; rlim.rlim_max = limit; if (setrlimit (resource, &rlim) != 0) { return LOGIN_ERROR_RLIMIT; } return 0; } src/usermod.c:static struct id_range getid_range(const char *str) { id_t first, last; const char *pos; struct id_range result = { .first = type_max(id_t), .last = type_min(id_t) }; static_assert(is_same_type(id_t, uid_t), ""); static_assert(is_same_type(id_t, gid_t), ""); first = type_min(id_t); last = type_max(id_t); if (a2i(id_t, &first, str, &pos, 10, first, last) == -1 && errno != ENOTSUP) { return result; } if ('-' != *pos++) return result; if (a2i(id_t, &last, pos, NULL, 10, first, last) == -1) return result; result.first = first; result.last = last; return result; } > > With the inclusion of `_Widthof`, and the mandating of two's complement > representation for signed integers since C23, the `_Minof` and `_Maxof` > expressions would be **implementable in one way only**. Not really true. I implement them differently than you. alx@devuan:/srv/alx/src/shadow/shadow/master$ grepc type_max . ./lib/typetraits.h:#define type_max(T) \ ( \ (T) (is_signed(T) ? stype_max(T) : utype_max(T)) \ ) alx@devuan:/srv/alx/src/shadow/shadow/master$ grepc stype_max . ./lib/typetraits.h:#define stype_max(T) \ ( \ (T) (((((T) 1 << (_Wdithof(T) - 2)) - 1) << 1) + 1) \ ) alx@devuan:/srv/alx/src/shadow/shadow/master$ grepc utype_max . ./lib/typetraits.h:#define utype_max(T) \ ( \ (T) -1 \ ) Which means I'd have to go and check why both your implementation and mine do the same thing. > And that they are > "hard to write correctly and review" (quote from the Motive of the proposal) > doesn't apply. > > ```c > #define IS_SIGNED(T) ((T) -1 < 1) > #define MAXOF(T) ((T) ((((T) 1 << (_Widthof(T) - 1 - IS_SIGNED(T))) - 1) * 2 > + 1)) > #define MINOF(T) ((T) ~MAXOF(T)) /* Always works with two's complement > representation */ > ``` I certainly would prefer to avoid reviewing that code. Or writing it. I don't feel safe. > Yes, there is only one way to implement these, as like the code above. As I showed, I have a different implementation. This statement is false. > > I have multiple concerns with the `_Minof` and `_Maxof` proposal currently: > * There are not technically necessary, and when programs need them, they are > trivially implementable. (Standardizing them as keywords would only > introduce noise to the language.) I wouldn't call the above trivial. The programmer who had to review my implementation certainly didn't feel safe: <https://github.com/shadow-maint/shadow/pull/896#discussion_r1659894055> > * The keywords proposed `_Minof` and `_Maxof` can be easily confused with > `MIN()` and `MAX()` macros that programs often defined for retrieving the > least or greatest value among two or more expressions. And I see no > discussions regarding that potential user (I mean programmer, not compiler > writer) confusion. I don't think they can be accidentally misused one in place of the other, because one has two arguments and the other has one. > * Why not less confusing keywords such as `_Typemin` or `_Typemax`? No > discussion of this either. Because keyword operators named *of always act on types. 'type' would be redundant with 'of'. And not having 'of' would be inconsistent. Since I don't think they can be confused, due to the number of arguments, I'm okay with _Minof/_Maxof. > > That's all I think. Thanks for the feedback!