Authors: Joseph Myers
Date: 2025-12-18
Submitted against: C23
Status: Open
The following issue is based on a discussion that started in reflector message 34564.
When an expression with enumerated type (with or without fixed
underlying type) is used in an expression, if the type's integer
conversion rank (which equals the rank of the underlying type) is less
than or equal to the rank of int and unsigned int, the integer
promotions convert it to int or unsigned int (depending on whether
int can represent all values of the type). When an expression with
enumerated type with larger rank is used in an expression, it is
unchanged by the integer promotions, but the usual arithmetic
conversions still act to convert it to the underlying type.
This means that in contexts that apply the integer promotions but not
the usual arithmetic conversions, such as unary + and -, shift
operators, and the default argument promotions when passing arguments
to variadic functions, such an expression is not converted to the
underlying type.
For unary + and -, this differs from some implementation practice,
which accepts the following example that is not valid according to the
current wording.
enum e : long { E1 };
enum f : long { F1 };
extern typeof (+(enum e)0) x;
extern typeof (+(enum f)0) x; // Not valid; enum e and enum f not compatible.
For shift operators, the standard wording assumes that the promoted left operand has a signed or unsigned integer type. But while enumerated types have signed or unsigned behavior, they do not fall into the definition of signed or unsigned types.
For variadic functions, the following example shows that calling
va_arg with a different enumerated type whose underlying type
differs in signedness can be valid when calling it with a different
enumerated type with the same underlying type is not valid, which does
not seem intuitive.
#include <stdarg.h>
typedef enum e : long { E1 } e;
typedef enum f : long { F1 } f;
typedef enum g : unsigned long { G1 } g;
void foo(int, ...)
{
va_list ap;
va_start(ap);
va_arg(ap, g); // okay
va_arg(ap, f); // UB
}
int main(void)
{
foo(0, (e)0, (e)0);
}
No specific wording change is proposed here to address these issues.
The committee should first give direction on whether the conversion to
the underlying type should be added to the integer promotions (and
removed from the usual arithmetic conversions) for enumerated types
with rank larger than that of int and unsigned int. If not, then
appropriate fixes should be considered individually for each of the
cases listed here.
Comment from Issues list maintainer on 2026-02-09:
This issue was discussed at the February 2026 meeting of WG14. Direction was given that the conversions to an enumeration's underlying type should occur in all cases discussed in the issue.
Comment from Issues list maintainer on 2026-02-25:
Reflector message 35358 (12 Feb 2026) proposes wording for this issue to make the integer promotions convert all enumerations to their underlying type.
In C23 6.3.2.1, change the wording about integer promotions:
The following can be used in an expression wherever anintorunsigned intcan be used:
An object or expression with an integer type (other thanintorunsigned int) whose integer conversion rank is less than or equal to the rank ofintandunsigned int.
A bit-field of typebool,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 not a bit-precise integer type (6.2.5): if anintcan represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to anint;50) otherwise, it is converted to anunsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
to:
The integer promotions are the following sequence of conversions, applied to certain kinds of expressions where specified in their respective subclauses. Each conversion is applied in turn if the type resulting from the previous conversions satisfies the given conditions.
If the expression has enumerated type, it is converted to the underlying type of the enumeration.
If the expression after the previous conversion is a bit-field of a bit-precise integer type (6.2.5), it is converted to the corresponding bit-precise integer type.50)
If the type resulting from the previous conversions is a bit-precise integer type, no further conversions are applied as part of the integer promotions.
If the expression after the previous conversions:
has an integer type (other than
intorunsigned int) whose integer conversion rank is less than or equal to the rank ofintandunsigned int; oris a bit-field of type
bool,int,signed int, orunsigned intthen:
if an
intcan represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to anint;otherwise, it is converted to an
unsigned int.All other types are unchanged by the integer promotions.
Keep the existing footnote (number 50 in C23), moved to the new
location indicated, and the existing NOTE following the replaced text.
If the option for plain int bit-fields to be implicitly unsigned is
removed, signed int can be removed from the list of bit-field types
above.
In C23 6.3.2.8 (Usual arithmetic conversion), change:
Otherwise, if any of the two types is an enumeration, it is converted to its underlying type. Then, theThe integer promotions are performed on both operands.