2026-10-04
integration into IS ISO/IEC 9899:202y
| document number | date | comment |
|---|---|---|
| n3975 | 202610 | This paper: |
| no implementation seems to be conforming | ||
| no fix for implementations that do not have promotion | ||
| n3970 | 202609 | Original 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, none of the implementations that we were able to test on the compiler explorer are conforming with respect to C23. Here, “able to test” means that the compiler implements bit-fields for wide integer types, compiles and executes our test program.
The first set of non-conformance of implementations is about
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.
Another set of non-conforming implementations do not seem to apply integer promotions to bit-fields at all. Thus more or less by accident they get the non-promotion for bit-fields of a wide type correct.
This can be tested by the following program.
#include <stdio.h>
#define INT_WIDTH (sizeof(int)*8)
struct tt {
long long K:INT_WIDTH-1;
long long L:INT_WIDTH;
long long M:INT_WIDTH+1;
unsigned long long KUL:INT_WIDTH-1;
unsigned long long LUL:INT_WIDTH;
unsigned long long MUL:INT_WIDTH+1;
unsigned KU:INT_WIDTH-1;
unsigned LU:INT_WIDTH;
} x;
#define TYPE_NAME(X) \
_Generic(+(X), /* apply integer promotion */ \
int : "int", \
long : "long", \
long long : "long long", \
unsigned int : "unsigned int", \
unsigned long : "unsigned long", \
unsigned long long : "unsigned long long", \
default: "other")
#define FORMAT "promoted `long long:%zu` has type `%s`\n"
#define FORMATUL "promoted `unsigned long long:%zu` has type `%s`\n"
#define FORMATU "promoted `unsigned:%zu` 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));
printf(FORMATUL, INT_WIDTH-1, TYPE_NAME(x.KUL));
printf(FORMATUL, INT_WIDTH, TYPE_NAME(x.LUL));
printf(FORMATUL, INT_WIDTH+1, TYPE_NAME(x.MUL));
printf(FORMATU, INT_WIDTH-1, TYPE_NAME(x.KU));
printf(FORMATU, INT_WIDTH, TYPE_NAME(x.LU));
}For clang, icc (old versions), icx, nvc, and tcc this program has the following output:
promoted `long long:31` has type `int`
promoted `long long:32` has type `int`
promoted `long long:33` has type `long long`
promoted `unsigned long long:31` has type `int`
promoted `unsigned long long:32` has type `unsigned int`
promoted `unsigned long long:33` has type `unsigned long long`
promoted `unsigned:31` has type `int`
promoted `unsigned:32` has type `unsigned int`In the proposed text we change the requirements such that these compilers become conforming and also such that the new rules are not in contradiction to those of C++.
For all variants of gcc it is:
promoted `long long:31` has type `int`
promoted `long long:32` has type `int`
promoted `long long:33` has type `other`
promoted `unsigned long long:31` has type `int`
promoted `unsigned long long:32` has type `unsigned int`
promoted `unsigned long long:33` has type `other`
promoted `unsigned:31` has type `int`
promoted `unsigned:32` has type `unsigned int`Note that this is different to the previous ones by the occurrence of “other” for wider bit-fields. An optional wording is proposed to also make this one conforming of some sorts, although other points of non-conformance would then still remain.
In contrast to that CCC, Chibicc and MSVC produce:
promoted `long long:31` has type `long long`
promoted `long long:32` has type `long long`
promoted `long long:33` has type `long long`
promoted `unsigned long long:31` has type `unsigned long long`
promoted `unsigned long long:32` has type `unsigned long long`
promoted `unsigned long long:33` has type `unsigned long long`
promoted `unsigned:31` has type `unsigned int`
promoted `unsigned:32` has type `unsigned int`Since these compilers do not seem to even make an attempt to apply promotion, we do not try to salvage them.
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, signed 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 signed 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 an 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.
4 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.
35 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).
Does WG14 want to accept the proposed wording of n3975 for integration into C2Y?
If that does not find consensus, try the same with an additional phrase for bit-fields with a wide underlying type.
Does WG14 want to accept the proposed wording of n3975 where the phrase
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.
is added as first phrase in paragraph 4, for integration into C2Y?
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 stipulate 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, depending on the option that is chosen above, if
any.
Thanks to Joseph Myers and Halalaluyafail3 for discussions and pointing out errors in previous versions of this paper.