[Bug c/66249] New: -Wformat-signedness should not warn on enums

eblake at redhat dot com gcc-bugzilla@gcc.gnu.org
Thu May 21 16:56:00 GMT 2015


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

            Bug ID: 66249
           Summary: -Wformat-signedness should not warn on enums
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: eblake at redhat dot com
  Target Milestone: ---

The standard is clear that enums have implementation-defined signedness (C99
6.7.2.2P4 "Each enumerated type shall be compatible with char, a signed integer
type, or an unsigned integer type. The choice of type is
implementation-defined, but shall be capable of representing the values of all
the members of the enumeration.").  However, while there is a way to force gcc
to use a smaller or larger type (-fshort-enums), I don't know how to force gcc
to use a signed or unsigned type for enums whose members could fit in either. 
As such, since I cannot guarantee that compiling on some other platform will
want to use a different signedness, I'm forced to add casts any time I want to
print an enum, when using the new -Wformat-signedness (implied by -Wformat=2).

Here's a demonstration of what I mean (testing on Fedora rawhide):

$ gcc --version | head -n1
gcc (GCC) 5.1.1 20150422 (Red Hat 5.1.1-1)
$ cat foo.c
#include <stdio.h>
int main(void) {
  int i = 0;
  unsigned u = 0;
  enum e_signed { as, bs = -1 } es = as;
  enum e_unsigned { au, bu = 0xffffffff } eu = au;
  enum e_implementation { ai } ei = ai;
  printf ("%u %d %u %d %u %d %u %d\n", i, u, es, es, eu, eu, ei, ei);
  return 0;
}
$ gcc -Wformat -Wformat-signedness foo.c -o foo
foo.c: In function ‘main’:
foo.c:8:11: warning: format ‘%u’ expects argument of type ‘unsigned int’, but
argument 2 has type ‘int’ [-Wformat=]
   printf ("%u %d %u %d %u %d %u %d\n", i, u, es, es, eu, eu, ei, ei);
           ^
foo.c:8:11: warning: format ‘%d’ expects argument of type ‘int’, but argument 3
has type ‘unsigned int’ [-Wformat=]
foo.c:8:11: warning: format ‘%u’ expects argument of type ‘unsigned int’, but
argument 4 has type ‘int’ [-Wformat=]
foo.c:8:11: warning: format ‘%d’ expects argument of type ‘int’, but argument 7
has type ‘unsigned int’ [-Wformat=]
foo.c:8:11: warning: format ‘%d’ expects argument of type ‘int’, but argument 9
has type ‘unsigned int’ [-Wformat=]
[dummy@rawhide64 ~]$ 

The warning for arguments 2, 3, 4, and 7 are expected (2 and 3 to show the
normal usage of the warning, 4/5 to show that I can force an enum to be signed
by including negative members, 6/7 to show that I can force an enum to be
unsigned by including members larger than INT_MAX); and the lack of warning for
arguments 5 and 6 is good.  I can squelch the warning for the first four
problematic arguments by either using the correct %d vs. %u counterpart, or by
changing the signedness of the variable that I'm printing.

However, argument 8/9 is problematic.  From the compiler warning on argument 9,
it looks like gcc defaults to unsigned, at least on my platform.  But I
_cannot_ know whether the compiler will pick a signed or unsigned
representation for the enum on other platforms (since all members would fit in
either type, and since the platform ABI may demand a particular signedness), so
I cannot know whether %u or %d would trigger a warning because I picked the
wrong type.  My only recourse is to add a cast (such as "%d",(int)ei); but that
is ugly.

And look what happens when I add -fshort-enums to the mix:

$ gcc -fshort-enums -Wformat -Wformat-signedness foo.c -o foo
foo.c: In function ‘main’:
foo.c:8:11: warning: format ‘%u’ expects argument of type ‘unsigned int’, but
argument 2 has type ‘int’ [-Wformat=]
   printf ("%u %d %u %d %u %d %u %d\n", i, u, es, es, eu, eu, ei, ei);
           ^
foo.c:8:11: warning: format ‘%d’ expects argument of type ‘int’, but argument 3
has type ‘unsigned int’ [-Wformat=]
foo.c:8:11: warning: format ‘%u’ expects argument of type ‘unsigned int’, but
argument 4 has type ‘int’ [-Wformat=]
foo.c:8:11: warning: format ‘%d’ expects argument of type ‘int’, but argument 7
has type ‘unsigned int’ [-Wformat=]
[dummy@rawhide64 ~]$ 

Here, arguments 4/5 are still signed, arguments 6/7 are still unsigned; but
arguments 8/9 are now a type compatible with 'char', and promotes to 'int'
(whether the enum's underlying type is signed or unsigned).  Oddly enough, the
compiler did not warn for EITHER 8 or 9, but by the technical argument, "%u"
should have warned for argument 8.  And consider what happens from a promotion
standpoint: if gcc picked 'char' (rather than 'signed char' or unsigned char')
as the underlying type when -fshort-enums is in effect, and my enum contained a
value in the range [128-255], then I am now dependent on whether 'char' is
signed or unsigned for whether %d will print 128 or -128 (and %u would print
128 or 4294967168).

Conversely, we should be able to reason that if the compiler was able to pick a
smaller type when -fshort-enums is in effect, and particularly if that smaller
type is unsigned, then it doesn't matter whether I use %u or %d - every value
of the enum will print to the same representation, and therefore, squelching
the warning was the right thing to do.  But if that is the case, then omitting
-fshort-enums should _also_ squelch the warning - again, if I have an enum that
has no members that require the use of 'int' (for an unsigned member) or
'unsigned' (for a member larger than INT_MAX), then we KNOW that the range of
the enum is such that both %u and %d will print it identically, regardless of
what type is actually assigned to the enum.

So I'm arguing that -Wformat-signedness has a bug, and should NOT print a
warning about a %d vs 'unsigned' mismatch when it is compared against an enum
value whose range could be reduced were -fshort-enums were in effect.

See also bug 65446 where -Wformat-signedness has issues when integer promotion
of smaller unsigned types is involved.


More information about the Gcc-bugs mailing list