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