Bug 22421 - problems with -Wformat and bit-fields
Summary: problems with -Wformat and bit-fields
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 4.0.1
: P2 minor
Target Milestone: 4.0.2
Assignee: Joseph S. Myers
URL:
Keywords: diagnostic
: 21962 (view as bug list)
Depends on:
Blocks:
 
Reported: 2005-07-12 01:49 UTC by Jim Wilson
Modified: 2006-01-24 00:06 UTC (History)
2 users (show)

See Also:
Host: ia64-linux
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2005-07-15 22:05:00


Attachments
31/32/33 bit-fields vs %u/%lu/%ld/%d printf formats (197 bytes, text/plain)
2005-07-12 01:53 UTC, Jim Wilson
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Jim Wilson 2005-07-12 01:49:14 UTC
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.
Comment 1 Jim Wilson 2005-07-12 01:53:23 UTC
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.
Comment 2 jsm-csl@polyomino.org.uk 2005-07-12 02:03:19 UTC
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.

Comment 3 Andrew Pinski 2005-07-12 04:18:01 UTC
*** Bug 21962 has been marked as a duplicate of this bug. ***
Comment 4 Andrew Pinski 2005-07-12 04:20:43 UTC
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.
Comment 5 jsm-csl@polyomino.org.uk 2005-07-12 12:00:37 UTC
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).

Comment 6 Joseph S. Myers 2005-07-15 22:05:00 UTC
Testing a patch.
Comment 7 GCC Commits 2005-07-16 16:02:11 UTC
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

Comment 8 GCC Commits 2005-07-16 16:04:51 UTC
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

Comment 9 Andrew Pinski 2005-07-16 17:53:47 UTC
Fixed in 4.0.2.
Comment 10 tony.luck 2006-01-24 00:06:51 UTC
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?
Comment 11 Jim Wilson 2006-01-30 23:24:59 UTC
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.