shafik created this revision. shafik added reviewers: aaron.ballman, erichkeane. Herald added a project: All. shafik requested review of this revision.
DR2338 <https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2338> clarified that it was undefined behavior to set the value outside the range of the enumerations values for an enum without a fixed underlying type. We should diagnose this with a constant expression context. This PR adds this diagnosis for C++20 since the DR status was CD5 and a test to verify the behavior. https://reviews.llvm.org/D130058 Files: clang/include/clang/Basic/DiagnosticASTKinds.td clang/lib/AST/ExprConstant.cpp clang/test/SemaCXX/constant-expression-cxx2a.cpp Index: clang/test/SemaCXX/constant-expression-cxx2a.cpp =================================================================== --- clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -1473,3 +1473,10 @@ } static_assert(g()); // expected-error {{constant expression}} expected-note {{in call}} } + +enum E { e1=-4, e2=4}; +void testValueInRangeOfEnumerationValues() { + constexpr E x1 = static_cast<E>(8); // expected-error {{must be initialized by a constant expression}} + // expected-note@-1 {{store of value outside of the range of unscoped enum, valid values -8 to 7}} + constexpr E x2 = static_cast<E>(-8); +} Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -13509,6 +13509,46 @@ return Info.Ctx.getTypeSize(DestType) == Info.Ctx.getTypeSize(SrcType); } + if (Info.Ctx.getLangOpts().CPlusPlus20) + if (const EnumType *ET = dyn_cast<EnumType>(DestType)) { + const EnumDecl *ED = ET->getDecl(); + // Check that the value is within the range of the enumeration values. + // + // This corressponds to [expr.static.cast]p10 which says: + // A value of integral or enumeration type can be explicitly converted + // to a complete enumeration type ... If the enumeration type does not + // have a fixed underlying type, the value is unchanged if the original + // value is within the range of the enumeration values ([dcl.enum]), and + // otherwise, the behavior is undefined. + // + // This was resolved as part of DR2338 which has CD5 status. + if (!ED->isFixed()) { + llvm::APInt Min; + llvm::APInt End; + + unsigned Bitwidth = Info.Ctx.getIntWidth(DestType); + unsigned NumNegativeBits = ED->getNumNegativeBits(); + unsigned NumPositiveBits = ED->getNumPositiveBits(); + + if (NumNegativeBits) { + unsigned NumBits = std::max(NumNegativeBits, NumPositiveBits + 1); + End = llvm::APInt(Bitwidth, 1) << (NumBits - 1); + Min = -End; + } else { + End = llvm::APInt(Bitwidth, 1) << NumPositiveBits; + Min = llvm::APInt::getZero(Bitwidth); + } + + if (NumNegativeBits && + (End.sle(Result.getInt()) || Min.sgt(Result.getInt()))) + CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range) + << Min.getSExtValue() << (End.getSExtValue() - 1); + else if (!NumNegativeBits && End.ule(Result.getInt())) + CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range) + << Min.getZExtValue() << (End.getZExtValue() - 1); + } + } + return Success(HandleIntToIntCast(Info, E, DestType, SrcType, Result.getInt()), E); } Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -366,6 +366,8 @@ "type %0 has unexpected layout">; def note_constexpr_unsupported_flexible_array : Note< "flexible array initialization is not yet supported">; +def note_constexpr_unscoped_enum_out_of_range : Note< + "store of value outside of the range of unscoped enum, valid values %0 to %1">; def err_experimental_clang_interp_failed : Error< "the experimental clang interpreter failed to evaluate an expression">;
Index: clang/test/SemaCXX/constant-expression-cxx2a.cpp =================================================================== --- clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -1473,3 +1473,10 @@ } static_assert(g()); // expected-error {{constant expression}} expected-note {{in call}} } + +enum E { e1=-4, e2=4}; +void testValueInRangeOfEnumerationValues() { + constexpr E x1 = static_cast<E>(8); // expected-error {{must be initialized by a constant expression}} + // expected-note@-1 {{store of value outside of the range of unscoped enum, valid values -8 to 7}} + constexpr E x2 = static_cast<E>(-8); +} Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -13509,6 +13509,46 @@ return Info.Ctx.getTypeSize(DestType) == Info.Ctx.getTypeSize(SrcType); } + if (Info.Ctx.getLangOpts().CPlusPlus20) + if (const EnumType *ET = dyn_cast<EnumType>(DestType)) { + const EnumDecl *ED = ET->getDecl(); + // Check that the value is within the range of the enumeration values. + // + // This corressponds to [expr.static.cast]p10 which says: + // A value of integral or enumeration type can be explicitly converted + // to a complete enumeration type ... If the enumeration type does not + // have a fixed underlying type, the value is unchanged if the original + // value is within the range of the enumeration values ([dcl.enum]), and + // otherwise, the behavior is undefined. + // + // This was resolved as part of DR2338 which has CD5 status. + if (!ED->isFixed()) { + llvm::APInt Min; + llvm::APInt End; + + unsigned Bitwidth = Info.Ctx.getIntWidth(DestType); + unsigned NumNegativeBits = ED->getNumNegativeBits(); + unsigned NumPositiveBits = ED->getNumPositiveBits(); + + if (NumNegativeBits) { + unsigned NumBits = std::max(NumNegativeBits, NumPositiveBits + 1); + End = llvm::APInt(Bitwidth, 1) << (NumBits - 1); + Min = -End; + } else { + End = llvm::APInt(Bitwidth, 1) << NumPositiveBits; + Min = llvm::APInt::getZero(Bitwidth); + } + + if (NumNegativeBits && + (End.sle(Result.getInt()) || Min.sgt(Result.getInt()))) + CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range) + << Min.getSExtValue() << (End.getSExtValue() - 1); + else if (!NumNegativeBits && End.ule(Result.getInt())) + CCEDiag(E, diag::note_constexpr_unscoped_enum_out_of_range) + << Min.getZExtValue() << (End.getZExtValue() - 1); + } + } + return Success(HandleIntToIntCast(Info, E, DestType, SrcType, Result.getInt()), E); } Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -366,6 +366,8 @@ "type %0 has unexpected layout">; def note_constexpr_unsupported_flexible_array : Note< "flexible array initialization is not yet supported">; +def note_constexpr_unscoped_enum_out_of_range : Note< + "store of value outside of the range of unscoped enum, valid values %0 to %1">; def err_experimental_clang_interp_failed : Error< "the experimental clang interpreter failed to evaluate an expression">;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits