2026-09-27
integration into IS ISO/IEC 9899:202y
| document number | date | comment |
|---|---|---|
| n3970 | 202609 | This paper |
| n3886 | Working draft | |
| n2958 | 202204 | Bit-fields, expressions and types |
| C23 issue 1021 | 202512 | |
| C23 issue 1077 | 202606 |
CC BY, see https://creativecommons.org/licenses/by/4.0
C23
issue 1021 asked about the conversion of enumerated types when the
rank of the underlying type is greater than the rank of int. Consensus in
WG14 seems to be that the necessity of such a conversion had been
overlooked when wider enumeration types were introduced in C23. This was
in particular not treated clearly and satisfactory for bit-precise
types. Several wording proposals have then been made to integrate this
type of conversion into the integer promotions.
When doing so, other problems appeared. The first of them being that the handling of bit-fields was unclear between different parts of the standard, see also C23 issue 1077. In particular, in principle integer promotions only kick in after lvalue conversion, and since the declared width of a bit-field is not part of the type system for integer types, it is not imminently clear how this information would be transferred into the integer promotions.
Therefore, in this proposal, we insist of talking about the operand of a expression that has to undergo promotion. This leaves us the possibility to add a special rule for the situation where an operand stems from a bit-field.
Then, the intent seemingly had been that if the underlying type is a bit-precise type T, that promotion always leads to that type, regardless of whether the operand refers to a bit-field. This proposal clarifies that.
Also, surprisingly, some implementations that we tested are not
conforming when it comes to bit-fields with an underlying standard
integer type that is wider than int (which is
permitted by the standard as implementation-defined extension). Here the
current standard clearly stipulates that the integer promotions only
apply to
int, andbool, int, signed int,
or unsigned int.For all other types it normatively requires without exception:
All other types are unchanged by the integer promotions.
Nevertheless, some the implementations that we tested also apply
integer promotions for example to long long
bit-fields, as long as their possible values fit in signed int
or in unsigned int.
This can be tested by the following program.
#include <limits.h>
#include <stdio.h>
struct tt {
long long K:INT_WIDTH-1;
long long L:INT_WIDTH;
long long M:INT_WIDTH+1;
} x;
#define TYPE_NAME(X) \
_Generic(+(X), /* apply integer promotion */ \
int : "int", \
long : "long", \
long long : "long long", \
default: "other")
#define FORMAT "promoted `long long:%d` has type `%s`\n"
int main() {
printf(FORMAT, INT_WIDTH-1, TYPE_NAME(x.K));
printf(FORMAT, INT_WIDTH, TYPE_NAME(x.L));
printf(FORMAT, INT_WIDTH+1, TYPE_NAME(x.M));
}For clang this program prints
promoted `long long:31` has type `int`
promoted `long long:32` has type `int`
promoted `long long:33` has type `long long`for gcc
promoted `long long:31` has type `int`
promoted `long long:32` has type `int`
promoted `long long:33` has type `other`In contrast to that, MSVC is conforming
promoted `long long:31` has type `long long`
promoted `long long:32` has type `long long`
promoted `long long:33` has type `long long`In the proposed text we weaken the requirements in that case such that these compilers become conforming and such that C imposes less restrictions than C++.
But note also, that this proposal here is not meant to resolve other apparent discrepancies between gcc and other C compiler implementations, see n2958, namely that gcc extends the type system by the bit-field width and exposes this non-standard type to their users. (Thus the “other”, above.) Resolving this discrepancy would require more changes than just for integer promotions, and probably also some convincing such that the gcc behavior is recognized as erroneous.
Then, when revisiting the whole, it appears that the text was more complicated than it ought to be and also that it still contained some factual errors in non-normative text.
If all enumerated types are already changed to the underlying type, we may refer to the width of the type (which is always well defined).
For a better readability, we introduce the terms of narrow and wide integer type. This is the right place to do so, because this is the clause where we define the rank.
Talking about changing a narrow type or the type of a bit-field
only makes sense if the width is less than INT_WIDTH. And then, int is always the
right choice.
For the remaining narrow types with a width that is equal to
INT_WIDTH the promoted type needs to
be int if the
type has negative values, otherwise it has to be unsigned int.
Clarify that for bit-fields with wide types, integer promotions are only specified up to the necessary. This to get the standard aligned with current practice.
A declaration of the form unsigned _BitInt(7) : 2
declares a bit-field member but is, as such, not a type specification of
an integer type. Clarify the language of a footnote with respect to
that.
The original note (p3 in 6.3.2.1) was imprecise in one case and
was missing another case, namely the switch
statement.
Indexing of the term “promotion” is inconsistent.
Append the following sentence to 6.3.2.1 p1 (which defines the integer conversion rank)
A narrow integer type is an integer type with a rank that is less than the rank of
int; a wide integer type is a type where it is greater.YY)
YY) The types
signed intandunsigned intand enumerated types that have these as underlying type are neither narrow nor wide integer types.
Replace 6.3.2.1 p2 to p4 and footnote 38) as follows. Change a non-normative phrase in p4 into a footnote.
2 The following can be used in an expression wherever an
intorunsigned intcan be used:
- An object or expression with an integer type (other than
intorunsigned int) whose integer conversion rank is less than or equal to the rank ofintandunsigned int.- A bit-field of type
bool,int,signed int, orunsigned int.
The value from a bit-field of a bit-precise integer type is converted to the corresponding bit-precise integer type. If the original type is neither a bit-precise integer type (6.2.5) nor an enumerated type compatible with a bit-precise integer type: if an
intcan represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int;38) otherwise, it is converted to anunsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
2 As defined in the following, the integer promotions preserve the value (including sign) and potentially change the type of operands of expressions where this is specified in their respective subclauses. Let T be the type of the operand, or if the operand has an enumerated type, the underlying type of the enumeration. If the original operand before lvalue conversion refers to a bit-field member, T is determined without consideration of the declared width. If T is a bit-precise integer type38), the promoted type is T.
3 Otherwise, if T is not a wide integer type, let w be the declared width of the member if the original operand is a bit-field member; otherwise, let w be the width of T:
- If w is less than
INT_WIDTH, the promoted type issigned int.- Otherwise, if w is equal to
INT_WIDTH:
- if negative values are representable in TXX), the promoted type is
signed int,- otherwise, the promoted type is
unsigned int.
Otherwise, if T is a wide integer type and if the original operand is a bit-field member, the promoted type is an integer type that is not narrow, that is able to hold all possible values of the bit-field and that is otherwise unspecified. Otherwise, the promoted type is T.
38) E.g.
unsigned _BitInt(7)BF: 2isdeclares a bit-field memberBFthat can hold the values0,1,2,3, andconverts toretains the typeunsigned _BitInt(7)when it undergoes integer promotion.
34 NOTE The integer promotions are applied only:
- as part of the usual arithmetic conversions (6.3.2.8),
to certain argument expressions,as part of the default argument promotions (6.5.3.3),- to the operands of the unary
+,-, and~operators (6.5.4.4),andto both operands of the bitwise shift operators (6.5.8),- and to the controlling expression of a
switchstatement (6.8.5.3).
as specified by their respective subclauses.
4 The integer promotions preserve value including sign.
XX) As discussed earlier, whether a “plain”
charcan hold negative values is implementation-defined.
It also seems that indexing of the words “promotion(s)” is not entirely consistent. Integer promotions are classified under “promotion” (singular) and default argument promotions are classified under “promotions” (plural).
There are several remaining issues concerning integer promotions and bit-fields.
The requirement that the second operator of a bitwise shift is promoted is superfluous. For the wording of the operation itself, only the value of the operand is used. Type is irrelevant. We should remove this useless requirement.
For bit-fields the interaction between lvalue conversion and integer promotion is weird. Probably we should have in a more central place that an lvalue conversion followed by integer promotion is a fused operation.
Again for bit-fields one major C implementation (gcc) has a weird
interpretation of lvalue conversion and provides a type that is not
backed by the standard. It is neither a standard integer type nor an
extended integer type, so that implementation is not conforming in that
point. This shows only in places where the type of an expression is
observable, that is with auto declarations
and within _Generic. WG14
should work on convincing this implementation to change their behavior
to be conforming. Once that would be achieved, the text on integer
promotions (for wide bit-fields) could be improved.
Thanks to Joseph Myers for discussions and pointing out errors in previous versions of this paper.