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

Reply via email to