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. -- Summary: problems with -Wformat and bit-fields Product: gcc Version: 4.0.1 Status: UNCONFIRMED Severity: normal Priority: P2 Component: c AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: wilson at gcc dot gnu dot org CC: gcc-bugs at gcc dot gnu dot org GCC host triplet: ia64-linux http://gcc.gnu.org/bugzilla/show_bug.cgi?id=22421