This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c/66249] New: -Wformat-signedness should not warn on enums
- From: "eblake at redhat dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Thu, 21 May 2015 16:56:30 +0000
- Subject: [Bug c/66249] New: -Wformat-signedness should not warn on enums
- Auto-submitted: auto-generated
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.