https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80060

            Bug ID: 80060
           Summary: RFE: -Wformat knob to ignore same-width incorrect
                    types
           Product: gcc
           Version: 6.3.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: eblake at redhat dot com
  Target Milestone: ---

When porting from one machine to another, especially when porting from 32-bit
to 64-bit software, getting warnings about mismatches such as printf("%ld",
(uint32_t)1) is awesome.  But once something has been ported to a given
platform, if two types have the same size but different rank, getting warnings
about something that is not portable to other machines but which works in
practice on the given machine is very noisy.  And sometimes, the noise is
unavoidable - there are many situations in which different implementations can
pick a type that is consistent for their platform, but uses a different rank
than what another platform chose - not all the world uses the <stdint.h> types,
but has a variety of other typedefs in place.

Here's some examples: on 64-bit Linux, the header <linux/userfaultfd.h>
declares a struct with members specified with a typedef __u64:

    struct uffd_msg msg;
struct uffd_msg {
..
        union {
                struct {
                        __u64   flags;
                        __u64   address;
                } pagefault;
..
        } arg;
}

Printing one of these values is obvious - the typedef name tells you it should
be an unsigned 64-bit quantity, so let's try printf("%"PRIx64,
msg.arg.pagefault.flags).  Oops, on 64-bit Linux, that fails under -Wformat,
because "%ld" is incompatible with 'unsigned long long'.  But since the kernel
headers typedef'd __u64 without any counterpart to something like PRIx64, there
is no sane way to print a __u64 without writing an extra cast at every caller,
which is prone to introduce more bugs than the warnings it silences:
printf("%"PRIx64, (uint64_t)msg.arg.pagefault.flags).

Another case: on 32-bit mingw, the declaration for ntohl() says that it returns
a 'u_long', which is a 32-bit type.  But POSIX says that ntohl() returns
'uint32_t'.  So the obvious printf("%"PRIx32, ntohl(1)) fails to compile on
mingw, because 'u_long' (which is 'unsigned long') is incompatible with
'uint32_t' (which is 'unsigned int') on that platform (arguably a bug in
mingw's headers, but such is life).  Again, the mismatch warning can be avoided
with a cast, but that does not scale well: printf("%"PRIx32,
(uint32_t)ntohl(1))

gcc recently introduced -Wformat-signedness (and it's counterpart
-Wno-format-signedness) to let developers fine-tune how MUCH verbosity they
want during warning checks.  Are you porting code to a platform where 'int' and
'unsigned int' have different widths? (Some weirdnix hardware might exist like
that, and C seems to make it possible, even though I can't name such a system)
- then turn the warning on. But if you really DON'T care about "%d" used with
'unsigned', or "%x" used with 'int' (because in the end, the 32-bit value
passed through varargs gets reinterpreted into the correct 32-bit value that
gets printed, regardless of intermediate sign-incorrectness), then turn it off.

So I'd love to have a new knob, maybe named -Wformat-same-rank, which controls
whether gcc warns about using the wrong format specifier EVEN THOUGH the type
passed to varargs has the same rank and therefore will print accurately; and
projects can then use -Wno-format-same-rank to silence __u64/uint64_t or
u_long/uint32_t differences while still getting warnings about real bugs of
32-bit vs. 64-bit mismatches.

Reply via email to