Skip to content

Commit 2dec83c

Browse files
[clang] Turn -Wenum-constexpr-conversion into a hard error (#102364)
The warning has been active for a few releases now, first only in user code, later in system headers, and finally as an error by default. Therefore, we believe it is now time to transition into a hard error, as required by the C++ Standard. The main affected C++ projects have by now fixed the error, or there's a pending patch for review that does it. Fixes #59036
1 parent 9e87061 commit 2dec83c

File tree

8 files changed

+71
-31
lines changed

8 files changed

+71
-31
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ C++ Specific Potentially Breaking Changes
4949
few users and can be written as ``__is_same(__remove_cv(T), decltype(nullptr))``,
5050
which GCC supports as well.
5151

52+
- Clang will now correctly diagnose as ill-formed a constant expression where an
53+
enum without a fixed underlying type is set to a value outside the range of
54+
the enumeration's values.
55+
56+
.. code-block:: c++
57+
58+
enum E { Zero, One, Two, Three, Four };
59+
constexpr E Val1 = (E)3; // Ok
60+
constexpr E Val2 = (E)7; // Ok
61+
constexpr E Val3 = (E)8; // Now ill-formed, out of the range [0, 7]
62+
constexpr E Val4 = (E)-1; // Now ill-formed, out of the range [0, 7]
63+
64+
Since Clang 16, it has been possible to suppress the diagnostic via
65+
`-Wno-enum-constexpr-conversion`, to allow for a transition period for users.
66+
Now, in Clang 20, **it is no longer possible to suppress the diagnostic**.
67+
5268
ABI Changes in This Version
5369
---------------------------
5470

@@ -143,6 +159,11 @@ Modified Compiler Flags
143159
Removed Compiler Flags
144160
-------------------------
145161

162+
- The compiler flag `-Wenum-constexpr-conversion` (and the `Wno-`, `Wno-error-`
163+
derivatives) is now removed, since it's no longer possible to suppress the
164+
diagnostic (see above). Users can expect an `unknown warning` diagnostic if
165+
it's still in use.
166+
146167
Attribute Changes in Clang
147168
--------------------------
148169

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,9 @@ def warn_integer_constant_overflow : Warning<
394394
def warn_fixedpoint_constant_overflow : Warning<
395395
"overflow in expression; result is %0 with type %1">,
396396
InGroup<DiagGroup<"fixed-point-overflow">>;
397-
def warn_constexpr_unscoped_enum_out_of_range : Warning<
397+
def note_constexpr_unscoped_enum_out_of_range : Note<
398398
"integer value %0 is outside the valid range of values [%1, %2] for the "
399-
"enumeration type %3">, DefaultError, ShowInSystemHeader, ShowInSystemMacro,
400-
InGroup<DiagGroup<"enum-constexpr-conversion">>;
399+
"enumeration type %3">;
401400

402401
// This is a temporary diagnostic, and shall be removed once our
403402
// implementation is complete, and like the preceding constexpr notes belongs

clang/lib/AST/ExprConstant.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14508,14 +14508,12 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
1450814508
if (ED->getNumNegativeBits() && ConstexprVar &&
1450914509
(Max.slt(Result.getInt().getSExtValue()) ||
1451014510
Min.sgt(Result.getInt().getSExtValue())))
14511-
Info.Ctx.getDiagnostics().Report(
14512-
E->getExprLoc(), diag::warn_constexpr_unscoped_enum_out_of_range)
14511+
Info.CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range)
1451314512
<< llvm::toString(Result.getInt(), 10) << Min.getSExtValue()
1451414513
<< Max.getSExtValue() << ED;
1451514514
else if (!ED->getNumNegativeBits() && ConstexprVar &&
1451614515
Max.ult(Result.getInt().getZExtValue()))
14517-
Info.Ctx.getDiagnostics().Report(
14518-
E->getExprLoc(), diag::warn_constexpr_unscoped_enum_out_of_range)
14516+
Info.CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range)
1451914517
<< llvm::toString(Result.getInt(), 10) << Min.getZExtValue()
1452014518
<< Max.getZExtValue() << ED;
1452114519
}

clang/lib/AST/Interp/Interp.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -924,12 +924,12 @@ void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
924924
if (ED->getNumNegativeBits() &&
925925
(Max.slt(Value.getSExtValue()) || Min.sgt(Value.getSExtValue()))) {
926926
const SourceLocation &Loc = S.Current->getLocation(OpPC);
927-
S.report(Loc, diag::warn_constexpr_unscoped_enum_out_of_range)
927+
S.CCEDiag(Loc, diag::note_constexpr_unscoped_enum_out_of_range)
928928
<< llvm::toString(Value, 10) << Min.getSExtValue() << Max.getSExtValue()
929929
<< ED;
930930
} else if (!ED->getNumNegativeBits() && Max.ult(Value.getZExtValue())) {
931931
const SourceLocation &Loc = S.Current->getLocation(OpPC);
932-
S.report(Loc, diag::warn_constexpr_unscoped_enum_out_of_range)
932+
S.CCEDiag(Loc, diag::note_constexpr_unscoped_enum_out_of_range)
933933
<< llvm::toString(Value, 10) << Min.getZExtValue() << Max.getZExtValue()
934934
<< ED;
935935
}

clang/test/AST/Interp/cxx11.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,49 +93,58 @@ E2 testDefaultArgForParam(E2 e2Param = (E2)-1) { // ok, not a constant expressio
9393
void testValueInRangeOfEnumerationValues() {
9494
constexpr E1 x1 = static_cast<E1>(-8);
9595
constexpr E1 x2 = static_cast<E1>(8);
96-
// both-error@-1 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}}
96+
// both-error@-1 {{constexpr variable 'x2' must be initialized by a constant expression}}
97+
// both-note@-2 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}}
9798
E1 x2b = static_cast<E1>(8); // ok, not a constant expression context
9899

99100
constexpr E2 x3 = static_cast<E2>(-8);
100-
// both-error@-1 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
101+
// both-error@-1 {{constexpr variable 'x3' must be initialized by a constant expression}}
102+
// both-note@-2 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
101103
constexpr E2 x4 = static_cast<E2>(0);
102104
constexpr E2 x5 = static_cast<E2>(8);
103-
// both-error@-1 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
105+
// both-error@-1 {{constexpr variable 'x5' must be initialized by a constant expression}}
106+
// both-note@-2 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
104107

105108
constexpr E3 x6 = static_cast<E3>(-2048);
106109
constexpr E3 x7 = static_cast<E3>(-8);
107110
constexpr E3 x8 = static_cast<E3>(0);
108111
constexpr E3 x9 = static_cast<E3>(8);
109112
constexpr E3 x10 = static_cast<E3>(2048);
110-
// both-error@-1 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}}
113+
// both-error@-1 {{constexpr variable 'x10' must be initialized by a constant expression}}
114+
// both-note@-2 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}}
111115

112116
constexpr E4 x11 = static_cast<E4>(0);
113117
constexpr E4 x12 = static_cast<E4>(1);
114118
constexpr E4 x13 = static_cast<E4>(2);
115-
// both-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}}
119+
// both-error@-1 {{constexpr variable 'x13' must be initialized by a constant expression}}
120+
// both-note@-2 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}}
116121

117122
constexpr EEmpty x14 = static_cast<EEmpty>(0);
118123
constexpr EEmpty x15 = static_cast<EEmpty>(1);
119124
constexpr EEmpty x16 = static_cast<EEmpty>(2);
120-
// both-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}}
125+
// both-error@-1 {{constexpr variable 'x16' must be initialized by a constant expression}}
126+
// both-note@-2 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}}
121127

122128
constexpr EFixed x17 = static_cast<EFixed>(100);
123129
constexpr EScoped x18 = static_cast<EScoped>(100);
124130

125131
constexpr EMaxInt x19 = static_cast<EMaxInt>(__INT_MAX__-1);
126132
constexpr EMaxInt x20 = static_cast<EMaxInt>((long)__INT_MAX__+1);
127-
// both-error@-1 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}}
133+
// both-error@-1 {{constexpr variable 'x20' must be initialized by a constant expression}}
134+
// both-note@-2 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}}
128135

129136
const NumberType neg_one = (NumberType) ((NumberType) 0 - (NumberType) 1); // ok, not a constant expression context
130137
}
131138

132139
template<class T, unsigned size> struct Bitfield {
133-
static constexpr T max = static_cast<T>((1 << size) - 1); // #enum
140+
static constexpr T max = static_cast<T>((1 << size) - 1);
141+
// both-error@-1 {{constexpr variable 'max' must be initialized by a constant expression}}
142+
// both-note@-2 {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
134143
};
135144

136145
void testValueInRangeOfEnumerationValuesViaTemplate() {
137146
Bitfield<E2, 3> good;
138-
Bitfield<E2, 4> bad; // both-error@#enum {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
147+
Bitfield<E2, 4> bad; // both-note {{in instantiation}}
139148
}
140149

141150
enum SortOrder {

clang/test/SemaCXX/Inputs/enum-constexpr-conversion-system-header.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ enum SystemEnum
1010
void testValueInRangeOfEnumerationValuesInSystemHeader()
1111
{
1212
constexpr SystemEnum x1 = static_cast<SystemEnum>(123);
13-
// expected-error@-1 {{integer value 123 is outside the valid range of values [0, 1] for the enumeration type 'SystemEnum'}}
13+
// expected-error@-1 {{constexpr variable 'x1' must be initialized by a constant expression}}
14+
// expected-note@-2 {{integer value 123 is outside the valid range of values [0, 1] for the enumeration type 'SystemEnum'}}
1415

1516
const SystemEnum x2 = static_cast<SystemEnum>(123); // ok, not a constant expression context
1617
}

clang/test/SemaCXX/constant-expression-cxx11.cpp

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,52 +2460,62 @@ E2 testDefaultArgForParam(E2 e2Param = (E2)-1) { // ok, not a constant expressio
24602460
void testValueInRangeOfEnumerationValues() {
24612461
constexpr E1 x1 = static_cast<E1>(-8);
24622462
constexpr E1 x2 = static_cast<E1>(8);
2463-
// expected-error@-1 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}}
2463+
// expected-error@-1 {{constexpr variable 'x2' must be initialized by a constant expression}}
2464+
// expected-note@-2 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}}
24642465
E1 x2b = static_cast<E1>(8); // ok, not a constant expression context
24652466

24662467
constexpr E2 x3 = static_cast<E2>(-8);
2467-
// expected-error@-1 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
2468+
// expected-error@-1 {{constexpr variable 'x3' must be initialized by a constant expression}}
2469+
// expected-note@-2 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
24682470
constexpr E2 x4 = static_cast<E2>(0);
24692471
constexpr E2 x5 = static_cast<E2>(8);
2470-
// expected-error@-1 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
2472+
// expected-error@-1 {{constexpr variable 'x5' must be initialized by a constant expression}}
2473+
// expected-note@-2 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
24712474

24722475
constexpr E3 x6 = static_cast<E3>(-2048);
24732476
constexpr E3 x7 = static_cast<E3>(-8);
24742477
constexpr E3 x8 = static_cast<E3>(0);
24752478
constexpr E3 x9 = static_cast<E3>(8);
24762479
constexpr E3 x10 = static_cast<E3>(2048);
2477-
// expected-error@-1 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}}
2480+
// expected-error@-1 {{constexpr variable 'x10' must be initialized by a constant expression}}
2481+
// expected-note@-2 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}}
24782482

24792483
constexpr E4 x11 = static_cast<E4>(0);
24802484
constexpr E4 x12 = static_cast<E4>(1);
24812485
constexpr E4 x13 = static_cast<E4>(2);
2482-
// expected-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}}
2486+
// expected-error@-1 {{constexpr variable 'x13' must be initialized by a constant expression}}
2487+
// expected-note@-2 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}}
24832488

24842489
constexpr EEmpty x14 = static_cast<EEmpty>(0);
24852490
constexpr EEmpty x15 = static_cast<EEmpty>(1);
24862491
constexpr EEmpty x16 = static_cast<EEmpty>(2);
2487-
// expected-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}}
2492+
// expected-error@-1 {{constexpr variable 'x16' must be initialized by a constant expression}}
2493+
// expected-note@-2 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}}
24882494

24892495
constexpr EFixed x17 = static_cast<EFixed>(100);
24902496
constexpr EScoped x18 = static_cast<EScoped>(100);
24912497

24922498
constexpr EMaxInt x19 = static_cast<EMaxInt>(__INT_MAX__-1);
24932499
constexpr EMaxInt x20 = static_cast<EMaxInt>((long)__INT_MAX__+1);
2494-
// expected-error@-1 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}}
2500+
// expected-error@-1 {{constexpr variable 'x20' must be initialized by a constant expression}}
2501+
// expected-note@-2 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}}
24952502

24962503
const NumberType neg_one = (NumberType) ((NumberType) 0 - (NumberType) 1); // ok, not a constant expression context
24972504

24982505
CONSTEXPR_CAST_TO_SYSTEM_ENUM_OUTSIDE_OF_RANGE;
2499-
// expected-error@-1 {{integer value 123 is outside the valid range of values [0, 1] for the enumeration type 'SystemEnum'}}
2506+
// expected-error@-1 {{constexpr variable 'system_enum' must be initialized by a constant expression}}
2507+
// expected-note@-2 {{integer value 123 is outside the valid range of values [0, 1] for the enumeration type 'SystemEnum'}}
25002508
}
25012509

25022510
template<class T, unsigned size> struct Bitfield {
2503-
static constexpr T max = static_cast<T>((1 << size) - 1); // #enum
2511+
static constexpr T max = static_cast<T>((1 << size) - 1);
2512+
// cxx11-error@-1 {{constexpr variable 'max' must be initialized by a constant expression}}
2513+
// cxx11-note@-2 {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
25042514
};
25052515

25062516
void testValueInRangeOfEnumerationValuesViaTemplate() {
25072517
Bitfield<E2, 3> good;
2508-
Bitfield<E2, 4> bad; // cxx11-error@#enum {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
2518+
Bitfield<E2, 4> bad; // cxx11-note {{in instantiation}}
25092519
}
25102520

25112521
enum SortOrder {
@@ -2526,4 +2536,5 @@ void A::f(SortOrder order) {
25262536
GH50055::E2 GlobalInitNotCE1 = (GH50055::E2)-1; // ok, not a constant expression context
25272537
GH50055::E2 GlobalInitNotCE2 = GH50055::testDefaultArgForParam(); // ok, not a constant expression context
25282538
constexpr GH50055::E2 GlobalInitCE = (GH50055::E2)-1;
2529-
// expected-error@-1 {{integer value -1 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
2539+
// expected-error@-1 {{constexpr variable 'GlobalInitCE' must be initialized by a constant expression}}
2540+
// expected-note@-2 {{integer value -1 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}

clang/test/SemaCXX/cxx2a-consteval.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,12 +920,13 @@ consteval int aConstevalFunction() { // expected-error {{consteval function neve
920920
namespace GH50055 {
921921
enum E {e1=0, e2=1};
922922
consteval int testDefaultArgForParam(E eParam = (E)-1) {
923-
// expected-error@-1 {{integer value -1 is outside the valid range of values [0, 1] for the enumeration type 'E'}}
923+
// expected-note@-1 {{integer value -1 is outside the valid range of values [0, 1] for the enumeration type 'E'}}
924924
return (int)eParam;
925925
}
926926

927927
int test() {
928928
return testDefaultArgForParam() + testDefaultArgForParam((E)1);
929+
// expected-error@-1 {{call to consteval function 'GH50055::testDefaultArgForParam' is not a constant expression}}
929930
}
930931
}
931932

0 commit comments

Comments
 (0)