Improve the wording for integer promotions

Jens Gustedt, INRIA and ICube, France

2026-10-04

target

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

license

CC BY, see https://creativecommons.org/licenses/by/4.0

1 Rationale

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

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.

2 Proposed wording

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 int and unsigned int and 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 int or unsigned int can be used:

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 int can 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 an unsigned 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 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: 2 is declares a bit-field member BF that can hold the values 0, 1, 2, 3, and converts to retains the type unsigned _BitInt(7) when it undergoes integer promotion.

35 NOTE The integer promotions are applied only:

  1. as part of the usual arithmetic conversions (6.3.2.8),
  2. to certain argument expressions, as part of the default argument promotions (6.5.3.3),
  3. to the operands of the unary +, -, and ~ operators (6.5.4.4),
  4. and to both operands of the bitwise shift operators (6.5.8),
  5. and to the controlling expression of a switch statement (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” char can hold negative values is implementation-defined.

3 Note to the editor

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).

4 Questions for WG14

4.1 As presented above

Does WG14 want to accept the proposed wording of n3975 for integration into C2Y?

4.2 Adding a loophole for gcc

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?

5 Possible future directions

There are several remaining issues concerning integer promotions and bit-fields.

Acknowledgments

Thanks to Joseph Myers and Halalaluyafail3 for discussions and pointing out errors in previous versions of this paper.