Of course it should enforce a behavior. However that behavior
should be both rational and agree with existing practice. It seems
that existing practice already varies.
Others, and I, have pointed out the anomalies that can result from
this attitude. The opportunity should be taken to clean this up
without contravening any earlier standard. C is already a morass
of insecurities, there is no need to add another.
Personally I would have preferred the choice of "sign preserving"
promotions over the "value preserving" that was selected by the
committee. Apparently there were pre-standard implementations that
promoted unsigned char to signed int, although I can't remember ever
using one in the K&R days.
One ridiculous result of this attitude is that a 1 bit field,
whether declared as int or unsigned int, would always be treated as
-1 or 0. This is contrary to the usual C practice. The
opportunity exists to ensure that booleans stored in bit fields are
properly expanded. Or should we have to write !!x for any boolean
x stored in a bitfield?
The ridiculous thing about the paragraph above is your conclusion, as
I'm sure you'd realize if you rethink it carefully.
A signed int 1 bit field can indeed only contain the values 0 and -1.
An unsigned int 1 bit field can only contain the values 0 and 1. If
it contains the value 1, it will promote to a signed int with the
value 1. How can you possible maintain that it promotes to -1 in a
signed int? Perhaps you are thinking of how you would write assembly
language for some particular processor, but that has nothing to do
with preserving the value.
C conversions, and integral promotions, are always about the value,
not about the bit pattern.
Given the fact that "value preserving" has been the language standard
for a decade and a half, it is far more important that unsigned bit
fields promote exactly the same way is lesser rank integer types of
the same with and range of values.
Assume one of today's most common desk top platform with 8-bit chars,
16-bit shorts, and 32-bit ints. Now consider:
#include <stdio.h>
struct nu
{
unsigned uc: 8;
unsigned us: 16;
};
int main(void)
{
unsigned char uc = 0;
unsigned short us = 0;
struct nu nu = { 0, 0 };
if ((uc - 5) < 0)
puts("unsigned char promotes to signed");
else
puts("unsigned char promotes to unsigned");
if ((nu.uc - 5) < 0)
puts("unsigned :8 promotes to signed");
else
puts("unsigned :8 promotes to unsigned");
if ((us - 5) < 0)
puts("unsigned short promotes to signed");
else
puts("unsigned short promotes to unsigned");
if ((nu.us - 5) < 0)
puts("unsigned :16 promotes to signed");
else
puts("unsigned :16 promotes to unsigned");
return 0;
}
Under your interpretation, the results are different for the 8-bit
unsigned bit-field and the 8-bit unsigned char, and also different for
the 16-bit unsigned bit-field and the 16-bit unsigned short.
Amazingly, I just tested this with four compilers, all of which got it
wrong.
But the standard does specifically say you are wrong:
6.7.2.1 Structure and union specifiers P9:
"A bit-field is interpreted as a signed or unsigned integer type
consisting of the specified number of bits."
So the type of an unsigned int bit field is NOT unsigned int, it is a
(perhaps unnamed type) uintn_t, where 'n' is the width in bits.
And:
6.3.1.1 Boolean, characters, and integers P1
"The rank of a signed integer type shall be greater than the rank of
any signed integer type with less precision."
[snip]
"The rank of any unsigned integer type shall equal the rank of the
corresponding signed integer type, if any."
And finally:
6.3.1.2 P2
"If an int can represent all values of the original type, the value is
converted to an int; otherwise, it is converted to an unsigned int."
The first quote makes it clear that the type of an 'n' bit unsigned
integer bit-field is "n-bit unsigned integer type". By the definition
of all unsigned integer types, it has a precision of n-bits.
If n is less than the number of value bits in a signed int, it is
quite clear that this "n-bit unsigned integer type" has a lesser rank
than type int. This means it is subjected to the "integer
promotions".
And since an int can hold all the values of this type, it must be
promoted to int, not unsigned int.
While these quotations are copied and pasted from a copy of C99, C90
contained the same.
"The opportunity should be taken to clean this up without contravening
any earlier standard." is not possible.