https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731
--- Comment #12 from Alejandro Colomar <alx at kernel dot org> --- Hi Martin, On Tue, Apr 16, 2024 at 05:35:03AM +0000, uecker at gcc dot gnu.org wrote: > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97100 > > --- Comment #7 from uecker at gcc dot gnu.org --- > > The fix suppresses certain warnings which are guarded by a flag, but it is not > always clear whether a specific warning should be suppressed or not in dead > code. _Generic(3)'s branches are by design using different types, to call appropriate overload implementations. These will necessarily result in incompatible types somewhere. If you're lucky and your overload implementations are functions _and_ you call them all with the same argument list, you can move the argument list out of the _Generic expression, so as to avoid the warnings (because _Generic() will have already been evaluated at the time the argument list is part of the function call and the compiler has the information to diagnose. If either you pass a different argument list (not my case) to each overload, or your overloads must be implemented via macros (my case), then you must put the argument list inside the _Generic() expression, which will necessarily result in incompatible types (otherwise you wouldn't be using _Generic() at all, probably). > > You could also always add a cast. I designed these macros precisely for type safety. That is, to have the compiler diagnose type mismatches. For example, the call err = a2i(time_t, &t, "42", endp, base, min, max); is a type-safe version of BSD's t = strtoi("42", endp, base, min, max); Because nothing guarantees that intmax_t will be appropriate for parsing a time_t. My macro will call a wraper function that makes sure that we call the right one, strtoi(3) or strtou(3), depending on the signedness of time_t. It goes even further and makes sure that &t is actually a time_t, by wrapping strtoi(3) in a call that accepts a long*. If I do cast, then I lose all that type safety, and need to increase the complexity by tinkering with GNU extesions such as ({}) and __builtin_types_compatible_p(), and I also need to insert a _Static_assert(3). All of this is error prone, and requires that the program is not DRY, so I would really like to avoid it. It's nice, though, that it's possible to workaround it with a cast + GNU extensions + some extra complexity. At least I have a fallback. But I don't think I'm content with keeping it like that. See the program below to see what needs to be done to make it work safely. You mentioned in the #97100 thread that a C compiler must diagnose all constraint violations. Is the following then a violation of the standard? $ cat constraint.c void f(int *); int main(void) { unsigned u; f(&u); } $ cc constraint.c -S $ I don't see the compiler should be allowed not emit a diagnostic there, and it's not ok to not emit a diagnostic in a dead branch of _Generic(), independently of me having asked the compiler to warn about -Wincompatible-pointer-types. Dead branches of _Generic() are not just any dead code; it's dead code that we know will result in incompatible types, by design. A warning about them there is bogus. Please present a case where not warning about incompatible types in a dead _Generic() branch would be bad. > Fundamentally, the program is that _Generic is not ideally designed for this > use case. One could consider an extension Why not? What's the design use-case for _Generic() if it's not overloading a macro for different types, and then call the appropriate overload function or macro? Even if you open-code the branches in the _Generic() expressions, you're doing the same thing: writing code for handling different types, which will almost necessarily result in incompatible types in all branches but one. Have a lovely day! Alex