This problem showed up compiling the IA-64 linux kernel with gcc-4.0.1. There was an unexpected printf format warnings for a 32-bit bit-field. The same problem can be reproduced on any LP64 machine, such as x86_64-linux. I have generated an small testcase, which tries bit-fields of size 31, 32, and 33, and tries printing them with %u, %lu, %ld, and %d. For the 31-bit bit-fields, we accept either %u or %d for both signed and unsigned which is OK. For the 32-bit bit-fields, we accept only %u for the signed bit-field and %d for the unsigned bit-field, which is backwards from what one would expect. For the 33-bit bit-fields, we accept none of the alternatives. gcc-3.3.x behaves the way I would expect. Both %u and %d are accepted for both the signed and unsigned 31- and 32-bit bit-fields, and both %lu and %ld are accepted for both the signed and unsigned 33-bit bit-fields. The underlying problem here is that bit-fields are now their own unique types in the C front end. Also, the type checking that c-format.c does is rather simple. It looks for two types whose TYPE_MAIN_VARIANT is the same, or two types which are the same except for signedness. For the 31-bit bit-fields, this works because there is a special check for bit-fields smaller than int in perform_integral_promotions (called via default_conversion), which then converts them to int or unsigned int. However, this code has a ??? comment which implies it may be removed in future gcc versions. Because we have a standard integral type here, the TYPE_MAIN_VARIANT checks succeed. For 32-bit bit-fields, the TYPE_MAIN_VARIANT checks fail because there is no default conversion. We then fall through to the signedness check, which uses c_common_signed_or_unsigned_type (called via c_common_[signed,unsigned]_type) which returns the input type if the signedness matches, otherwise, it returns a standard type if the TYPE_PRECISION matches. Thus for a 32-bit bit-field, it returns a standard type only if the signedness is wrong, and thus we get the odd behaviour that %u works only for a signed bit-field, and %d works only for an unsigned bit-field. For a 33-bit bit-field, we get neither the default conversion, nor the TYPE_PRECISION match, and all format alternatives are rejected. There are a number of issues to resolve here. 1) What printf formats should be allowed to match for a bit-field? Or do we want to force use of a cast? I think the gcc-3.3.x behaviour is correct and desirable. 2) The type checking code in check_format_types needs to be extended to handle bit-field types properly. 3) The behaviour of c_common_signed_or_unsigned_type should be checked. Is it OK to return a standard integral type when the input is a non-standard integral type? If this is OK, then shouldn't we also do this when the signedness is the same? It seems odd that this routine can return either a standard integral type or a non-standard integral type, depending on signedness. 4) We should check if we still want the special bit-field check in perform_integral_promotions. This one I think we can defer indefinitely.
Created attachment 9247 [details] 31/32/33 bit-fields vs %u/%lu/%ld/%d printf formats Compile on an LP64 machine (e.g. ia64-linux, x86_64-linux, etc) with -Wformat, and look at the warnings. gcc-3.3.x generates 12 warnings. gcc-4.0 and up generate 18 warnings, 6 of which appear to be wrong.
Subject: Re: New: problems with -Wformat and bit-fields On Tue, 12 Jul 2005, wilson at gcc dot gnu dot org wrote: > This problem showed up compiling the IA-64 linux kernel with gcc-4.0.1. There > was an unexpected printf format warnings for a 32-bit bit-field. The same > problem can be reproduced on any LP64 machine, such as x86_64-linux. The format issues are bug 21962, but this bug is more detailed. > 1) What printf formats should be allowed to match for a bit-field? Or do we > want to force use of a cast? I think the gcc-3.3.x behaviour is correct and > desirable. If the bit-field has rank above that of int, then we must require a cast. > 2) The type checking code in check_format_types needs to be extended to handle > bit-field types properly. I don't think any format checking code should need changing for this if the code handling creating bit-field types, promotions and diagnostics is made correct. > 3) The behaviour of c_common_signed_or_unsigned_type should be checked. Is it > OK to return a standard integral type when the input is a non-standard integral > type? If this is OK, then shouldn't we also do this when the signedness is the > same? It seems odd that this routine can return either a standard integral type > or a non-standard integral type, depending on signedness. > 4) We should check if we still want the special bit-field check in > perform_integral_promotions. This one I think we can defer indefinitely. If 32-bit bit-field types are to be nonstandard types (i.e. extended integer types) then they have rank less than that of 32-bit int so should promote to int and be accepted for int formats. Either there should be a special check to make nonstandard types the same width as int promote to int, or possibly simpler the standard type should be used for bit-fields in that case. More generally it may make sense to use standard types for bit-fields which have the same width as a standard type.
*** Bug 21962 has been marked as a duplicate of this bug. ***
As mentioned, just the text in the diagnostic is wong, not the diagnostic is wrong. I don't know if I should mark this as a regression as this is really a progression as mentioned in comment #2.
Subject: Re: problems with -Wformat and bit-fields On Tue, 12 Jul 2005, pinskia at gcc dot gnu dot org wrote: > I don't know if I should mark this as a regression as this is really a progression as mentioned in > comment #2. The special case of 32-bit bit-fields is a regression, as they should be accepted where int is (most simply done by making bit-fields of the same width as int have type int / unsigned int; likewise for other standard types but with int / unsigned int taking precedence in the choice of types where multiple standard types have the same width).
Testing a patch.
Subject: Bug 22421 CVSROOT: /cvs/gcc Module name: gcc Changes by: jsm28@gcc.gnu.org 2005-07-16 16:01:58 Modified files: gcc : ChangeLog c-decl.c c-pretty-print.c gcc/testsuite : ChangeLog Added files: gcc/testsuite/gcc.dg/format: bitfld-1.c Log message: PR c/22421 * c-decl.c (c_build_bitfield_integer_type): New function. (finish_struct): Call it. * c-pretty-print.c (pp_c_type_specifier): Handle bit-field types. testsuite: * gcc.dg/format/bitfld-1.c: New test. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/ChangeLog.diff?cvsroot=gcc&r1=2.9457&r2=2.9458 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-decl.c.diff?cvsroot=gcc&r1=1.675&r2=1.676 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-pretty-print.c.diff?cvsroot=gcc&r1=1.64&r2=1.65 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/ChangeLog.diff?cvsroot=gcc&r1=1.5774&r2=1.5775 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/gcc.dg/format/bitfld-1.c.diff?cvsroot=gcc&r1=NONE&r2=1.1
Subject: Bug 22421 CVSROOT: /cvs/gcc Module name: gcc Branch: gcc-4_0-branch Changes by: jsm28@gcc.gnu.org 2005-07-16 16:04:38 Modified files: gcc : ChangeLog c-decl.c c-pretty-print.c gcc/testsuite : ChangeLog Added files: gcc/testsuite/gcc.dg/format: bitfld-1.c Log message: PR c/22421 * c-decl.c (c_build_bitfield_integer_type): New function. (finish_struct): Call it. * c-pretty-print.c (pp_c_type_specifier): Handle bit-field types. testsuite: * gcc.dg/format/bitfld-1.c: New test. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=2.7592.2.316&r2=2.7592.2.317 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-decl.c.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.630.6.14&r2=1.630.6.15 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-pretty-print.c.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.59&r2=1.59.2.1 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.5084.2.281&r2=1.5084.2.282 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/gcc.dg/format/bitfld-1.c.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=NONE&r2=1.1.2.1
Fixed in 4.0.2.
Broken (again?) in 4.1 $ cat u.c typedef unsigned long u64; struct pal_freq_ratio { u64 den : 32, num : 32; /* numerator & denominator */ } x; main() { printf("den=%lx num=%lx\n", x.den, x.num); } $ cc -Wformat u.c u.c:9: warning: format ‘%lx’ expects type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ u.c:9: warning: format ‘%lx’ expects type ‘long unsigned int’, but argument 3 has type ‘unsigned int’ $ cc --version cc (GCC) 4.1.0 20060109 (prerelease) (SUSE Linux) Copyright (C) 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Should this be re-opened, or is it logged elsewhere?
Subject: Re: problems with -Wformat and bit-fields On Mon, 2006-01-23 at 16:06, tony dot luck at intel dot com wrote: > u64 den : 32, num : 32; /* numerator & denominator */ > printf("den=%lx num=%lx\n", x.den, x.num); > u.c:9: warning: format %lx expects type long unsigned int, but argument 2 > has type unsigned int This is deliberate, as per our interpretation of the ISO C99 rules for integer conversions. If a bit-field has 32-bits, int is a 32-bit type, and long is a 64-bit type, then the bit-field promotes to int/unsigned int, even if declared with a long or unsigned long type. If long is a 32-bit type, then the bit-field promotes to long if declared with long type. This is mentioned in Joseph's response in comment #2. See also the testcase that was added for this PR. You can click on the testsuite link in comment #8 to see it.