Skip to content

Commit a0f8890

Browse files
authored
[clang][C23] Support N3029 Improved Normal Enumerations (#103917)
Basically clang already implemented 90% of the feature as an extension. This commit disables warnings for C23 and aligns types of enumerators according to the recent wording.
1 parent 1b57cbc commit a0f8890

File tree

7 files changed

+160
-42
lines changed

7 files changed

+160
-42
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ C2y Feature Support
179179
C23 Feature Support
180180
^^^^^^^^^^^^^^^^^^^
181181

182+
- Clang now supports `N3029 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3029.htm>`_ Improved Normal Enumerations.
183+
182184
Non-comprehensive list of changes in this release
183185
-------------------------------------------------
184186

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,6 @@ def err_opencl_invalid_param : Error<
10391039
"declaring function parameter of type %0 is not allowed%select{; did you forget * ?|}1">;
10401040
def err_opencl_invalid_return : Error<
10411041
"declaring function return value of type %0 is not allowed %select{; did you forget * ?|}1">;
1042-
def warn_enum_value_overflow : Warning<"overflow in enumeration value">;
10431042
def warn_pragma_options_align_reset_failed : Warning<
10441043
"#pragma options align=reset failed: %0">,
10451044
InGroup<IgnoredPragmas>;
@@ -6194,9 +6193,13 @@ def err_misplaced_ivar : Error<
61946193
def warn_ivars_in_interface : Warning<
61956194
"declaration of instance variables in the interface is deprecated">,
61966195
InGroup<DiagGroup<"objc-interface-ivars">>, DefaultIgnore;
6197-
def ext_enum_value_not_int : Extension<
6198-
"ISO C restricts enumerator values to range of 'int' (%0 is too "
6199-
"%select{small|large}1)">;
6196+
def ext_c23_enum_value_not_int : Extension<
6197+
"%select{|incremented }0enumerator value which exceeds the range of 'int' is "
6198+
"a C23 extension (%1 is too %select{small|large}2)">, InGroup<C23>;
6199+
def warn_c17_compat_enum_value_not_int : Warning<
6200+
"%select{|incremented }0enumerator value which exceeds the range of 'int' is "
6201+
"incompatible with C standards before C23 (%1 is too %select{small|large}2)">,
6202+
DefaultIgnore, InGroup<CPre23Compat>;
62006203
def ext_enum_too_large : ExtWarn<
62016204
"enumeration values exceed range of largest integer">, InGroup<EnumTooLarge>;
62026205
def ext_enumerator_increment_too_large : ExtWarn<

clang/lib/Sema/SemaDecl.cpp

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19479,11 +19479,13 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum,
1947919479
// representable as an int.
1948019480

1948119481
// Complain if the value is not representable in an int.
19482-
if (!isRepresentableIntegerValue(Context, EnumVal, Context.IntTy))
19483-
Diag(IdLoc, diag::ext_enum_value_not_int)
19484-
<< toString(EnumVal, 10) << Val->getSourceRange()
19485-
<< (EnumVal.isUnsigned() || EnumVal.isNonNegative());
19486-
else if (!Context.hasSameType(Val->getType(), Context.IntTy)) {
19482+
if (!isRepresentableIntegerValue(Context, EnumVal, Context.IntTy)) {
19483+
Diag(IdLoc, getLangOpts().C23
19484+
? diag::warn_c17_compat_enum_value_not_int
19485+
: diag::ext_c23_enum_value_not_int)
19486+
<< 0 << toString(EnumVal, 10) << Val->getSourceRange()
19487+
<< (EnumVal.isUnsigned() || EnumVal.isNonNegative());
19488+
} else if (!Context.hasSameType(Val->getType(), Context.IntTy)) {
1948719489
// Force the type of the expression to 'int'.
1948819490
Val = ImpCastExprToType(Val, Context.IntTy, CK_IntegralCast).get();
1948919491
}
@@ -19558,17 +19560,22 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum,
1955819560

1955919561
// If we're not in C++, diagnose the overflow of enumerator values,
1956019562
// which in C99 means that the enumerator value is not representable in
19561-
// an int (C99 6.7.2.2p2). However, we support GCC's extension that
19562-
// permits enumerator values that are representable in some larger
19563-
// integral type.
19564-
if (!getLangOpts().CPlusPlus && !T.isNull())
19565-
Diag(IdLoc, diag::warn_enum_value_overflow);
19566-
} else if (!getLangOpts().CPlusPlus &&
19567-
!EltTy->isDependentType() &&
19563+
// an int (C99 6.7.2.2p2). However C23 permits enumerator values that
19564+
// are representable in some larger integral type and we allow it in
19565+
// older language modes as an extension.
19566+
// Exclude fixed enumerators since they are diagnosed with an error for
19567+
// this case.
19568+
if (!getLangOpts().CPlusPlus && !T.isNull() && !Enum->isFixed())
19569+
Diag(IdLoc, getLangOpts().C23
19570+
? diag::warn_c17_compat_enum_value_not_int
19571+
: diag::ext_c23_enum_value_not_int)
19572+
<< 1 << toString(EnumVal, 10) << 1;
19573+
} else if (!getLangOpts().CPlusPlus && !EltTy->isDependentType() &&
1956819574
!isRepresentableIntegerValue(Context, EnumVal, EltTy)) {
1956919575
// Enforce C99 6.7.2.2p2 even when we compute the next value.
19570-
Diag(IdLoc, diag::ext_enum_value_not_int)
19571-
<< toString(EnumVal, 10) << 1;
19576+
Diag(IdLoc, getLangOpts().C23 ? diag::warn_c17_compat_enum_value_not_int
19577+
: diag::ext_c23_enum_value_not_int)
19578+
<< 1 << toString(EnumVal, 10) << 1;
1957219579
}
1957319580
}
1957419581
}
@@ -19887,9 +19894,6 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
1988719894
return;
1988819895
}
1988919896

19890-
// TODO: If the result value doesn't fit in an int, it must be a long or long
19891-
// long value. ISO C does not support this, but GCC does as an extension,
19892-
// emit a warning.
1989319897
unsigned IntWidth = Context.getTargetInfo().getIntWidth();
1989419898
unsigned CharWidth = Context.getTargetInfo().getCharWidth();
1989519899
unsigned ShortWidth = Context.getTargetInfo().getShortWidth();
@@ -19898,13 +19902,14 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
1989819902
// reverse the list.
1989919903
unsigned NumNegativeBits = 0;
1990019904
unsigned NumPositiveBits = 0;
19905+
bool MembersRepresentableByInt = true;
1990119906

1990219907
for (unsigned i = 0, e = Elements.size(); i != e; ++i) {
1990319908
EnumConstantDecl *ECD =
1990419909
cast_or_null<EnumConstantDecl>(Elements[i]);
1990519910
if (!ECD) continue; // Already issued a diagnostic.
1990619911

19907-
const llvm::APSInt &InitVal = ECD->getInitVal();
19912+
llvm::APSInt InitVal = ECD->getInitVal();
1990819913

1990919914
// Keep track of the size of positive and negative values.
1991019915
if (InitVal.isUnsigned() || InitVal.isNonNegative()) {
@@ -19916,6 +19921,8 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
1991619921
NumNegativeBits =
1991719922
std::max(NumNegativeBits, (unsigned)InitVal.getSignificantBits());
1991819923
}
19924+
MembersRepresentableByInt &=
19925+
isRepresentableIntegerValue(Context, InitVal, Context.IntTy);
1991919926
}
1992019927

1992119928
// If we have an empty set of enumerators we still need one bit.
@@ -19937,7 +19944,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
1993719944
// int, long long int, or unsigned long long int.
1993819945
// C99 6.4.4.3p2:
1993919946
// An identifier declared as an enumeration constant has type int.
19940-
// The C99 rule is modified by a gcc extension
19947+
// The C99 rule is modified by C23.
1994119948
QualType BestPromotionType;
1994219949

1994319950
bool Packed = Enum->hasAttr<PackedAttr>();
@@ -20031,7 +20038,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
2003120038
auto *ECD = cast_or_null<EnumConstantDecl>(D);
2003220039
if (!ECD) continue; // Already issued a diagnostic.
2003320040

20034-
// Standard C says the enumerators have int type, but we allow, as an
20041+
// C99 says the enumerators have int type, but we allow, as an
2003520042
// extension, the enumerators to be larger than int size. If each
2003620043
// enumerator value fits in an int, type it as an int, otherwise type it the
2003720044
// same as the enumerator decl itself. This means that in "enum { X = 1U }"
@@ -20045,9 +20052,14 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
2004520052
QualType NewTy;
2004620053
unsigned NewWidth;
2004720054
bool NewSign;
20048-
if (!getLangOpts().CPlusPlus &&
20049-
!Enum->isFixed() &&
20050-
isRepresentableIntegerValue(Context, InitVal, Context.IntTy)) {
20055+
if (!getLangOpts().CPlusPlus && !Enum->isFixed() &&
20056+
MembersRepresentableByInt) {
20057+
// C23 6.7.3.3.3p15:
20058+
// The enumeration member type for an enumerated type without fixed
20059+
// underlying type upon completion is:
20060+
// - int if all the values of the enumeration are representable as an
20061+
// int; or,
20062+
// - the enumerated type
2005120063
NewTy = Context.IntTy;
2005220064
NewWidth = IntWidth;
2005320065
NewSign = true;

clang/test/C/C23/n3029.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %clang_cc1 -verify=expected,all -triple x86_64-unknown-linux-gnu -fsyntax-only -std=c23 %s -Wpre-c23-compat
2+
// RUN: %clang_cc1 -verify=pedantic,all -triple x86_64-unknown-linux-gnu -fsyntax-only -std=c17 %s -pedantic
3+
4+
#include <limits.h>
5+
6+
#define GET_TYPE_INT(x) _Generic(x, \
7+
char: 1,\
8+
unsigned char: 2,\
9+
signed char: 3,\
10+
short: 4,\
11+
unsigned short: 5,\
12+
int: 6,\
13+
unsigned int: 7,\
14+
long: 8,\
15+
unsigned long: 9,\
16+
long long: 10,\
17+
unsigned long long: 11,\
18+
default: 0xFF\
19+
)\
20+
21+
enum x {
22+
a = INT_MAX,
23+
b = ULLONG_MAX, // expected-warning {{enumerator value which exceeds the range of 'int' is incompatible with C standards before C23}}
24+
// pedantic-warning@-1 {{enumerator value which exceeds the range of 'int' is a C23 extension}}
25+
a_type = GET_TYPE_INT(a),
26+
b_type = GET_TYPE_INT(b)
27+
};
28+
29+
_Static_assert(GET_TYPE_INT(a) == GET_TYPE_INT(b), "ok");
30+
31+
extern enum x e_a;
32+
extern __typeof(b) e_a;
33+
extern __typeof(a) e_a;
34+
35+
enum a {
36+
a0 = 0xFFFFFFFFFFFFFFFFULL // expected-warning {{enumerator value which exceeds the range of 'int' is incompatible with C standards before C23}}
37+
// pedantic-warning@-1 {{enumerator value which exceeds the range of 'int' is a C23 extension}}
38+
};
39+
40+
_Bool e (void) {
41+
return a0;
42+
}
43+
44+
int f (void) {
45+
return a0; // all-warning {{implicit conversion from 'unsigned long' to 'int' changes value from 18446744073709551615 to -1}}
46+
}
47+
48+
unsigned long g (void) {
49+
return a0;
50+
}
51+
52+
unsigned long long h (void) {
53+
return a0;
54+
}
55+
56+
enum big_enum {
57+
big_enum_a = LONG_MAX, // expected-warning {{enumerator value which exceeds the range of 'int' is incompatible with C standards before C23}}
58+
// pedantic-warning@-1 {{enumerator value which exceeds the range of 'int' is a C23 extension}}
59+
big_enum_b = a + 1, // expected-warning {{enumerator value which exceeds the range of 'int' is incompatible with C standards before C23}}
60+
// pedantic-warning@-1 {{enumerator value which exceeds the range of 'int' is a C23 extension}}
61+
big_enum_c = ULLONG_MAX // expected-warning {{enumerator value which exceeds the range of 'int' is incompatible with C standards before C23}}
62+
// pedantic-warning@-1 {{enumerator value which exceeds the range of 'int' is a C23 extension}}
63+
};
64+
65+
_Static_assert(GET_TYPE_INT(big_enum_a) == GET_TYPE_INT(big_enum_b), "ok");

clang/test/Misc/warning-flags.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This test serves two purposes:
1818

1919
The list of warnings below should NEVER grow. It should gradually shrink to 0.
2020

21-
CHECK: Warnings without flags (63):
21+
CHECK: Warnings without flags (62):
2222

2323
CHECK-NEXT: ext_expected_semi_decl_list
2424
CHECK-NEXT: ext_missing_whitespace_after_macro_name
@@ -46,7 +46,6 @@ CHECK-NEXT: warn_drv_amdgpu_cov6
4646
CHECK-NEXT: warn_drv_assuming_mfloat_abi_is
4747
CHECK-NEXT: warn_drv_clang_unsupported
4848
CHECK-NEXT: warn_drv_pch_not_first_include
49-
CHECK-NEXT: warn_enum_value_overflow
5049
CHECK-NEXT: warn_expected_qualified_after_typename
5150
CHECK-NEXT: warn_fe_backend_unsupported
5251
CHECK-NEXT: warn_fe_cc_log_diagnostics_failure
@@ -86,4 +85,4 @@ CHECK-NEXT: warn_weak_import
8685

8786
The list of warnings in -Wpedantic should NEVER grow.
8887

89-
CHECK: Number in -Wpedantic (not covered by other -W flags): 25
88+
CHECK: Number in -Wpedantic (not covered by other -W flags): 24

clang/test/Sema/enum.c

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
// RUN: %clang_cc1 -triple %itanium_abi_triple %s -fsyntax-only -verify -pedantic
2-
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fsyntax-only -std=c23 -verify -pedantic
1+
// RUN: %clang_cc1 -triple %itanium_abi_triple %s -fsyntax-only -verify=expected,pre-c23 -pedantic
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fsyntax-only -std=c23 -verify=expected -pedantic
33
enum e {A,
4-
B = 42LL << 32, // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
4+
B = 42LL << 32, // pre-c23-warning {{enumerator value which exceeds the range of 'int' is a C23 extension}}
55
C = -4, D = 12456 };
66

77
enum f { a = -2147483648, b = 2147483647 }; // ok.
88

99
enum g { // too negative
10-
c = -2147483649, // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
10+
c = -2147483649, // pre-c23-warning {{enumerator value which exceeds the range of 'int' is a C23 extension}}
1111
d = 2147483647 };
1212
enum h { e = -2147483648, // too pos
13-
f = 2147483648, // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
14-
i = 0xFFFF0000 // expected-warning {{too large}}
13+
f = 2147483648, // pre-c23-warning {{enumerator value which exceeds the range of 'int' is a C23 extension}}
14+
i = 0xFFFF0000 // pre-c23-warning {{too large}}
1515
};
1616

1717
// minll maxull
1818
enum x // expected-warning {{enumeration values exceed range of largest integer}}
19-
{ y = -9223372036854775807LL-1, // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
20-
z = 9223372036854775808ULL }; // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
19+
{ y = -9223372036854775807LL-1, // pre-c23-warning {{enumerator value which exceeds the range of 'int' is a C23 extension}}
20+
z = 9223372036854775808ULL }; // pre-c23-warning {{enumerator value which exceeds the range of 'int' is a C23 extension}}
2121

2222
int test(void) {
2323
return sizeof(enum e) ;
@@ -169,13 +169,16 @@ enum class GH42372_2 {
169169
One
170170
};
171171

172+
enum IncOverflow {
173+
V2 = __INT_MAX__,
174+
V3 // pre-c23-warning {{incremented enumerator value which exceeds the range of 'int' is a C23 extension}}
175+
};
176+
172177
#if __STDC_VERSION__ >= 202311L
173178
// FIXME: GCC picks __uint128_t as the underlying type for the enumeration
174179
// value and Clang picks unsigned long long.
175-
// FIXME: Clang does not yet implement WG14 N3029, so the warning about
176-
// restricting enumerator values to 'int' is not correct.
177180
enum GH59352 { // expected-warning {{enumeration values exceed range of largest integer}}
178-
BigVal = 66666666666666666666wb // expected-warning {{ISO C restricts enumerator values to range of 'int' (66666666666666666666 is too large)}}
181+
BigVal = 66666666666666666666wb
179182
};
180183
_Static_assert(BigVal == 66666666666666666666wb); /* expected-error {{static assertion failed due to requirement 'BigVal == 66666666666666666666wb'}}
181184
expected-note {{expression evaluates to '11326434445538011818 == 66666666666666666666'}}
@@ -191,4 +194,38 @@ _Static_assert(
191194
__uint128_t : 1
192195
)
193196
);
197+
198+
#include <limits.h>
199+
200+
void fooinc23() {
201+
enum E1 {
202+
V1 = INT_MAX
203+
} e1;
204+
205+
enum E2 {
206+
V2 = INT_MAX,
207+
V3
208+
} e2;
209+
210+
enum E3 {
211+
V4 = INT_MAX,
212+
V5 = LONG_MIN
213+
} e3;
214+
215+
enum E4 {
216+
V6 = 1u,
217+
V7 = 2wb
218+
} e4;
219+
220+
_Static_assert(_Generic(V1, int : 1));
221+
_Static_assert(_Generic(V2, int : 0, unsigned int : 1));
222+
_Static_assert(_Generic(V3, int : 0, unsigned int : 1));
223+
_Static_assert(_Generic(V4, int : 0, signed long : 1));
224+
_Static_assert(_Generic(V5, int : 0, signed long : 1));
225+
_Static_assert(_Generic(V6, int : 1));
226+
_Static_assert(_Generic(V7, int : 1));
227+
_Static_assert(_Generic((enum E4){}, unsigned int : 1));
228+
229+
}
230+
194231
#endif // __STDC_VERSION__ >= 202311L

clang/www/c_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ <h2 id="c2x">C23 implementation status</h2>
687687
<tr>
688688
<td>Improved normal enumerations</td>
689689
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3029.htm">N3029</a></td>
690-
<td class="unknown" align="center">Unknown</td>
690+
<td class="unreleased" align="center">Clang 20</td>
691691
</tr>
692692
<tr>
693693
<td>Relax requirements for va_start</td>

0 commit comments

Comments
 (0)