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.