On Sat. 7 Dec. 2024 at 20:19, David Laight <David.Laight@xxxxxxxxxx> wrote: > From: Vincent Mailhol > > Sent: 07 December 2024 07:43 > ... > > > So maybe the slightly long lines: > > > #define const_true(x) _Generic(0 ? (void *)((x) + 0 ? 0L : 1L) : (char *)0, char *: 1, void *: 0) > > > #define const_expr(x) _Generic(0 ? (void *)((x) + 0 ? 0L : 0L) : (char *)0, char *: 1, void *: 0) > > Clearly they can be implemented in terms of a common define. > But I don't see a need for a const_zero() and nested expansions make extra > work for the compiler. > > > > > This still throws a -Wnull-pointer-arithmetic on clang on const_expr(NULL): > > https://godbolt.org/z/vo5W7efdE > > I was worried about that one. > > > I just do not see a method to silence that one. So three options: > > > > 1. is_const() does not accept pointers and throws a constraint violation: > > #define is_const(x) __is_const_zero(0 * (x)) > > This is my current patch. > > Is that going to affect things like const_true(x << y)? No warnings for const_true(x << y). See by yourself: https://godbolt.org/z/v79x3dnor > Disallowing that seems counter-productive. > (Remember it might be passed into a #define that is then > checking its argument for being constant.) I understand how a static inline may use __builtin_constant_p() to check if its argument is NULL. But I am having a hard time understanding how this would look like with macros and with is_const(). Do you know one real life example where you have to pass NULL to a function like macro which can not be written as a static inline function? And if that real life use case does not exist, then disallowing it looks sane: it will push the developper to do the right thing: write a static inline with __builting_constant_p(). > > 2. is_const() accept pointers but is_const(NULL) returns false: > > #define is_const(x) __is_const_zero((x) != (x)) > > This keeps the current __is_constexpr() behaviour. > > No good - expands everything twice. And? __is_const_zero() does not evaluate its arguments, so no side effect: https://godbolt.org/z/W988P4v9e Or am I missing something else? > > 3. is_const() accepts pointers and is_const(NULL) return true: > > > > #define const_expr(x) _Generic(0 ? (void *)((x) + 0 ? 0L : 0L) > > : (char *)0, char *: 1, void *: 0) > > > > David's latest proposal, it requires to remove the > > -Wnull-pointer-arithmetic clang warning. > > Only for const_expr(NULL) - and since clang gets that wrong > maybe the warning is a good thing. > > You can just add: > #define const_NULL(ptr) const_true(!(ptr)) > Probably the only place where you actually want to test for zero. Which goes back to my first point: if we have to declare const_NULL(), then we probably do not want is_const() to accept NULL so that the user sees the mismatch (and I am not acknowledging that we need const_NULL(): as long as no use case arize, no need for it). > > I vote for 1. or 2. (with a preference for 1.). IMHO, we are just > > adding an unreasonable level of complexity for making the macro treat > > NULL as an integer. Would someone find a solution for 3. that does not > > yield a warning, then why not. But if we have to remove a compiler > > check for a theoretical use case that does not even exist in the > > kernel, then it is not worth the trade off. > > > > Concerning is_const(var << 2), the patch I submitted works fine as-is > > with all scalars including that (var << 2): > > > > https://godbolt.org/z/xer4aMees > > > > And can we ignore the case (!(var << 2))? This is not a warning > > because of the macro, but because of the caller! If I do any of: > > > > if (!(var << 2)) {} > > (void)__builtin_constant_p(!(var << 2)); > > > > I also got the warning. The point is that the macro should not > > generate *new* warnings. If the given argument already raises a > > warning, it is the caller's responsibility to fix. > > Except it could easily happen way inside some other expansion. > Perhaps someone optimises frobnicate(x) for constant input. > Suddenly frobnicate(!(var << 2)) generates a compile error. Then, that person will see that compile error and will modify frobnicate() to remove the compile error. End of the story. My point is simple: if the use case you are mentioning is valid, why did it never collide with __builtin_constant_p() that we have been using for many years? And I am sure that there are hundreds of examples of macro that will either pass their argument as-is to an if() or to the ternary operator without accounting for if their input is (!(var << 2)). So why did not these collide? Yours sincerely, Vincent Mailhol