diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index d322da81723a5..32f159f2b0f35 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -49,6 +49,22 @@ C++ Specific Potentially Breaking Changes few users and can be written as ``__is_same(__remove_cv(T), decltype(nullptr))``, which GCC supports as well. +- Clang will now correctly diagnose as ill-formed a constant expression where an + enum without a fixed underlying type is set to a value outside the range of + the enumeration's values. + + .. code-block:: c++ + + enum E { Zero, One, Two, Three, Four }; + constexpr E Val1 = (E)3; // Ok + constexpr E Val2 = (E)7; // Ok + constexpr E Val3 = (E)8; // Now ill-formed, out of the range [0, 7] + constexpr E Val4 = (E)-1; // Now ill-formed, out of the range [0, 7] + + Since Clang 16, it has been possible to suppress the diagnostic via + `-Wno-enum-constexpr-conversion`, to allow for a transition period for users. + Now, in Clang 20, **it is no longer possible to suppress the diagnostic**. + ABI Changes in This Version --------------------------- @@ -119,6 +135,11 @@ Modified Compiler Flags Removed Compiler Flags ------------------------- +- The compiler flag `-Wenum-constexpr-conversion` (and the `Wno-`, `Wno-error-` + derivatives) is now removed, since it's no longer possible to suppress the + diagnostic (see above). Users can expect an `unknown warning` diagnostic if + it's still in use. + Attribute Changes in Clang -------------------------- diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index a024f9b2a9f8c..af53cf651e51a 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -410,10 +410,9 @@ def warn_integer_constant_overflow : Warning< def warn_fixedpoint_constant_overflow : Warning< "overflow in expression; result is %0 with type %1">, InGroup>; -def warn_constexpr_unscoped_enum_out_of_range : Warning< +def note_constexpr_unscoped_enum_out_of_range : Note< "integer value %0 is outside the valid range of values [%1, %2] for the " - "enumeration type %3">, DefaultError, ShowInSystemHeader, ShowInSystemMacro, - InGroup>; + "enumeration type %3">; // This is a temporary diagnostic, and shall be removed once our // implementation is complete, and like the preceding constexpr notes belongs diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d4b9095c7509b..f2b8161f4a901 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14406,14 +14406,12 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { if (ED->getNumNegativeBits() && ConstexprVar && (Max.slt(Result.getInt().getSExtValue()) || Min.sgt(Result.getInt().getSExtValue()))) - Info.Ctx.getDiagnostics().Report( - E->getExprLoc(), diag::warn_constexpr_unscoped_enum_out_of_range) + Info.CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range) << llvm::toString(Result.getInt(), 10) << Min.getSExtValue() << Max.getSExtValue() << ED; else if (!ED->getNumNegativeBits() && ConstexprVar && Max.ult(Result.getInt().getZExtValue())) - Info.Ctx.getDiagnostics().Report( - E->getExprLoc(), diag::warn_constexpr_unscoped_enum_out_of_range) + Info.CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range) << llvm::toString(Result.getInt(), 10) << Min.getZExtValue() << Max.getZExtValue() << ED; } diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index 0f72b860ddad7..f2440f698b22b 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -924,12 +924,12 @@ void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED, if (ED->getNumNegativeBits() && (Max.slt(Value.getSExtValue()) || Min.sgt(Value.getSExtValue()))) { const SourceLocation &Loc = S.Current->getLocation(OpPC); - S.report(Loc, diag::warn_constexpr_unscoped_enum_out_of_range) + S.CCEDiag(Loc, diag::note_constexpr_unscoped_enum_out_of_range) << llvm::toString(Value, 10) << Min.getSExtValue() << Max.getSExtValue() << ED; } else if (!ED->getNumNegativeBits() && Max.ult(Value.getZExtValue())) { const SourceLocation &Loc = S.Current->getLocation(OpPC); - S.report(Loc, diag::warn_constexpr_unscoped_enum_out_of_range) + S.CCEDiag(Loc, diag::note_constexpr_unscoped_enum_out_of_range) << llvm::toString(Value, 10) << Min.getZExtValue() << Max.getZExtValue() << ED; } diff --git a/clang/test/AST/Interp/cxx11.cpp b/clang/test/AST/Interp/cxx11.cpp index cf2dfba079ef7..481e3da9289ef 100644 --- a/clang/test/AST/Interp/cxx11.cpp +++ b/clang/test/AST/Interp/cxx11.cpp @@ -93,49 +93,58 @@ E2 testDefaultArgForParam(E2 e2Param = (E2)-1) { // ok, not a constant expressio void testValueInRangeOfEnumerationValues() { constexpr E1 x1 = static_cast(-8); constexpr E1 x2 = static_cast(8); - // both-error@-1 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}} + // both-error@-1 {{constexpr variable 'x2' must be initialized by a constant expression}} + // both-note@-2 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}} E1 x2b = static_cast(8); // ok, not a constant expression context constexpr E2 x3 = static_cast(-8); - // both-error@-1 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} + // both-error@-1 {{constexpr variable 'x3' must be initialized by a constant expression}} + // both-note@-2 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} constexpr E2 x4 = static_cast(0); constexpr E2 x5 = static_cast(8); - // both-error@-1 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} + // both-error@-1 {{constexpr variable 'x5' must be initialized by a constant expression}} + // both-note@-2 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} constexpr E3 x6 = static_cast(-2048); constexpr E3 x7 = static_cast(-8); constexpr E3 x8 = static_cast(0); constexpr E3 x9 = static_cast(8); constexpr E3 x10 = static_cast(2048); - // both-error@-1 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}} + // both-error@-1 {{constexpr variable 'x10' must be initialized by a constant expression}} + // both-note@-2 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}} constexpr E4 x11 = static_cast(0); constexpr E4 x12 = static_cast(1); constexpr E4 x13 = static_cast(2); - // both-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}} + // both-error@-1 {{constexpr variable 'x13' must be initialized by a constant expression}} + // both-note@-2 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}} constexpr EEmpty x14 = static_cast(0); constexpr EEmpty x15 = static_cast(1); constexpr EEmpty x16 = static_cast(2); - // both-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}} + // both-error@-1 {{constexpr variable 'x16' must be initialized by a constant expression}} + // both-note@-2 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}} constexpr EFixed x17 = static_cast(100); constexpr EScoped x18 = static_cast(100); constexpr EMaxInt x19 = static_cast(__INT_MAX__-1); constexpr EMaxInt x20 = static_cast((long)__INT_MAX__+1); - // both-error@-1 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}} + // both-error@-1 {{constexpr variable 'x20' must be initialized by a constant expression}} + // both-note@-2 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}} const NumberType neg_one = (NumberType) ((NumberType) 0 - (NumberType) 1); // ok, not a constant expression context } template struct Bitfield { - static constexpr T max = static_cast((1 << size) - 1); // #enum + static constexpr T max = static_cast((1 << size) - 1); + // both-error@-1 {{constexpr variable 'max' must be initialized by a constant expression}} + // both-note@-2 {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} }; void testValueInRangeOfEnumerationValuesViaTemplate() { Bitfield good; - Bitfield bad; // both-error@#enum {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} + Bitfield bad; // both-note {{in instantiation}} } enum SortOrder { diff --git a/clang/test/SemaCXX/Inputs/enum-constexpr-conversion-system-header.h b/clang/test/SemaCXX/Inputs/enum-constexpr-conversion-system-header.h index 0850f3405eed3..7d35d5b71d1df 100644 --- a/clang/test/SemaCXX/Inputs/enum-constexpr-conversion-system-header.h +++ b/clang/test/SemaCXX/Inputs/enum-constexpr-conversion-system-header.h @@ -10,7 +10,8 @@ enum SystemEnum void testValueInRangeOfEnumerationValuesInSystemHeader() { constexpr SystemEnum x1 = static_cast(123); - // expected-error@-1 {{integer value 123 is outside the valid range of values [0, 1] for the enumeration type 'SystemEnum'}} + // expected-error@-1 {{constexpr variable 'x1' must be initialized by a constant expression}} + // expected-note@-2 {{integer value 123 is outside the valid range of values [0, 1] for the enumeration type 'SystemEnum'}} const SystemEnum x2 = static_cast(123); // ok, not a constant expression context } diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 6df8a4740d6cc..d888887bd8c6f 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -2460,52 +2460,62 @@ E2 testDefaultArgForParam(E2 e2Param = (E2)-1) { // ok, not a constant expressio void testValueInRangeOfEnumerationValues() { constexpr E1 x1 = static_cast(-8); constexpr E1 x2 = static_cast(8); - // expected-error@-1 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}} + // expected-error@-1 {{constexpr variable 'x2' must be initialized by a constant expression}} + // expected-note@-2 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}} E1 x2b = static_cast(8); // ok, not a constant expression context constexpr E2 x3 = static_cast(-8); - // expected-error@-1 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} + // expected-error@-1 {{constexpr variable 'x3' must be initialized by a constant expression}} + // expected-note@-2 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} constexpr E2 x4 = static_cast(0); constexpr E2 x5 = static_cast(8); - // expected-error@-1 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} + // expected-error@-1 {{constexpr variable 'x5' must be initialized by a constant expression}} + // expected-note@-2 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} constexpr E3 x6 = static_cast(-2048); constexpr E3 x7 = static_cast(-8); constexpr E3 x8 = static_cast(0); constexpr E3 x9 = static_cast(8); constexpr E3 x10 = static_cast(2048); - // expected-error@-1 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}} + // expected-error@-1 {{constexpr variable 'x10' must be initialized by a constant expression}} + // expected-note@-2 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}} constexpr E4 x11 = static_cast(0); constexpr E4 x12 = static_cast(1); constexpr E4 x13 = static_cast(2); - // expected-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}} + // expected-error@-1 {{constexpr variable 'x13' must be initialized by a constant expression}} + // expected-note@-2 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}} constexpr EEmpty x14 = static_cast(0); constexpr EEmpty x15 = static_cast(1); constexpr EEmpty x16 = static_cast(2); - // expected-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}} + // expected-error@-1 {{constexpr variable 'x16' must be initialized by a constant expression}} + // expected-note@-2 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}} constexpr EFixed x17 = static_cast(100); constexpr EScoped x18 = static_cast(100); constexpr EMaxInt x19 = static_cast(__INT_MAX__-1); constexpr EMaxInt x20 = static_cast((long)__INT_MAX__+1); - // expected-error@-1 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}} + // expected-error@-1 {{constexpr variable 'x20' must be initialized by a constant expression}} + // expected-note@-2 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}} const NumberType neg_one = (NumberType) ((NumberType) 0 - (NumberType) 1); // ok, not a constant expression context CONSTEXPR_CAST_TO_SYSTEM_ENUM_OUTSIDE_OF_RANGE; - // expected-error@-1 {{integer value 123 is outside the valid range of values [0, 1] for the enumeration type 'SystemEnum'}} + // expected-error@-1 {{constexpr variable 'system_enum' must be initialized by a constant expression}} + // expected-note@-2 {{integer value 123 is outside the valid range of values [0, 1] for the enumeration type 'SystemEnum'}} } template struct Bitfield { - static constexpr T max = static_cast((1 << size) - 1); // #enum + static constexpr T max = static_cast((1 << size) - 1); + // cxx11-error@-1 {{constexpr variable 'max' must be initialized by a constant expression}} + // cxx11-note@-2 {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} }; void testValueInRangeOfEnumerationValuesViaTemplate() { Bitfield good; - Bitfield bad; // cxx11-error@#enum {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} + Bitfield bad; // cxx11-note {{in instantiation}} } enum SortOrder { @@ -2526,4 +2536,5 @@ void A::f(SortOrder order) { GH50055::E2 GlobalInitNotCE1 = (GH50055::E2)-1; // ok, not a constant expression context GH50055::E2 GlobalInitNotCE2 = GH50055::testDefaultArgForParam(); // ok, not a constant expression context constexpr GH50055::E2 GlobalInitCE = (GH50055::E2)-1; -// expected-error@-1 {{integer value -1 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} +// expected-error@-1 {{constexpr variable 'GlobalInitCE' must be initialized by a constant expression}} +// expected-note@-2 {{integer value -1 is outside the valid range of values [0, 7] for the enumeration type 'E2'}} diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp index ba80e57f81424..6b0609a26c588 100644 --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -920,12 +920,13 @@ consteval int aConstevalFunction() { // expected-error {{consteval function neve namespace GH50055 { enum E {e1=0, e2=1}; consteval int testDefaultArgForParam(E eParam = (E)-1) { -// expected-error@-1 {{integer value -1 is outside the valid range of values [0, 1] for the enumeration type 'E'}} +// expected-note@-1 {{integer value -1 is outside the valid range of values [0, 1] for the enumeration type 'E'}} return (int)eParam; } int test() { return testDefaultArgForParam() + testDefaultArgForParam((E)1); + // expected-error@-1 {{call to consteval function 'GH50055::testDefaultArgForParam' is not a constant expression}} } }