Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang] Turn -Wenum-constexpr-conversion into a hard error #102364

Merged
merged 2 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------------------------

Expand Down Expand Up @@ -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
--------------------------

Expand Down
5 changes: 2 additions & 3 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<DiagGroup<"fixed-point-overflow">>;
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<DiagGroup<"enum-constexpr-conversion">>;
carlosgalvezp marked this conversation as resolved.
Show resolved Hide resolved
"enumeration type %3">;

// This is a temporary diagnostic, and shall be removed once our
// implementation is complete, and like the preceding constexpr notes belongs
Expand Down
6 changes: 2 additions & 4 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
27 changes: 18 additions & 9 deletions clang/test/AST/Interp/cxx11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,49 +93,58 @@ E2 testDefaultArgForParam(E2 e2Param = (E2)-1) { // ok, not a constant expressio
void testValueInRangeOfEnumerationValues() {
constexpr E1 x1 = static_cast<E1>(-8);
constexpr E1 x2 = static_cast<E1>(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<E1>(8); // ok, not a constant expression context

constexpr E2 x3 = static_cast<E2>(-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<E2>(0);
constexpr E2 x5 = static_cast<E2>(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<E3>(-2048);
constexpr E3 x7 = static_cast<E3>(-8);
constexpr E3 x8 = static_cast<E3>(0);
constexpr E3 x9 = static_cast<E3>(8);
constexpr E3 x10 = static_cast<E3>(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<E4>(0);
constexpr E4 x12 = static_cast<E4>(1);
constexpr E4 x13 = static_cast<E4>(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<EEmpty>(0);
constexpr EEmpty x15 = static_cast<EEmpty>(1);
constexpr EEmpty x16 = static_cast<EEmpty>(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<EFixed>(100);
constexpr EScoped x18 = static_cast<EScoped>(100);

constexpr EMaxInt x19 = static_cast<EMaxInt>(__INT_MAX__-1);
constexpr EMaxInt x20 = static_cast<EMaxInt>((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<class T, unsigned size> struct Bitfield {
static constexpr T max = static_cast<T>((1 << size) - 1); // #enum
static constexpr T max = static_cast<T>((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<E2, 3> good;
Bitfield<E2, 4> bad; // both-error@#enum {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
Bitfield<E2, 4> bad; // both-note {{in instantiation}}
}

enum SortOrder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ enum SystemEnum
void testValueInRangeOfEnumerationValuesInSystemHeader()
{
constexpr SystemEnum x1 = static_cast<SystemEnum>(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<SystemEnum>(123); // ok, not a constant expression context
}
Expand Down
33 changes: 22 additions & 11 deletions clang/test/SemaCXX/constant-expression-cxx11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2460,52 +2460,62 @@ E2 testDefaultArgForParam(E2 e2Param = (E2)-1) { // ok, not a constant expressio
void testValueInRangeOfEnumerationValues() {
constexpr E1 x1 = static_cast<E1>(-8);
constexpr E1 x2 = static_cast<E1>(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<E1>(8); // ok, not a constant expression context

constexpr E2 x3 = static_cast<E2>(-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<E2>(0);
constexpr E2 x5 = static_cast<E2>(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<E3>(-2048);
constexpr E3 x7 = static_cast<E3>(-8);
constexpr E3 x8 = static_cast<E3>(0);
constexpr E3 x9 = static_cast<E3>(8);
constexpr E3 x10 = static_cast<E3>(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<E4>(0);
constexpr E4 x12 = static_cast<E4>(1);
constexpr E4 x13 = static_cast<E4>(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<EEmpty>(0);
constexpr EEmpty x15 = static_cast<EEmpty>(1);
constexpr EEmpty x16 = static_cast<EEmpty>(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<EFixed>(100);
constexpr EScoped x18 = static_cast<EScoped>(100);

constexpr EMaxInt x19 = static_cast<EMaxInt>(__INT_MAX__-1);
constexpr EMaxInt x20 = static_cast<EMaxInt>((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<class T, unsigned size> struct Bitfield {
static constexpr T max = static_cast<T>((1 << size) - 1); // #enum
static constexpr T max = static_cast<T>((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<E2, 3> good;
Bitfield<E2, 4> bad; // cxx11-error@#enum {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
Bitfield<E2, 4> bad; // cxx11-note {{in instantiation}}
}

enum SortOrder {
Expand All @@ -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'}}
3 changes: 2 additions & 1 deletion clang/test/SemaCXX/cxx2a-consteval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
}
}

Expand Down
Loading