llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Timothy Choi (tinnamchoi) <details> <summary>Changes</summary> Fixes #<!-- -->24265 --- Patch is 22.39 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/152698.diff 6 Files Affected: - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+5) - (modified) clang/include/clang/Sema/Sema.h (+1-1) - (modified) clang/lib/Sema/SemaExpr.cpp (+91-21) - (modified) clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp (+1) - (modified) clang/test/SemaCXX/enum-scoped.cpp (+105) - (modified) clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp (+2) ``````````diff diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9ce142e7b37cc..e69ab1b9893d9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4401,6 +4401,11 @@ def warn_impcast_different_enum_types : Warning< def warn_impcast_int_to_enum : Warning< "implicit conversion from %0 to enumeration type %1 is invalid in C++">, InGroup<ImplicitIntToEnumCast>, DefaultIgnore; + +def note_no_implicit_conversion_for_scoped_enum + : Note<"no implicit conversion for scoped enum; consider casting to " + "underlying type">; + def warn_impcast_bool_to_null_pointer : Warning< "initialization of pointer of type %0 to null from a constant boolean " "expression">, InGroup<BoolConversion>; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5211373367677..3fc8f14eded85 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8065,7 +8065,7 @@ class Sema final : public SemaBase { BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr); QualType CheckSubtractionOperands( // C99 6.5.6 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, - QualType *CompLHSTy = nullptr); + BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr); QualType CheckShiftOperands( // C99 6.5.7 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc, bool IsCompAssign = false); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 6793d6da85cb1..87a6f448ba581 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -10742,6 +10742,45 @@ static void DiagnoseBadDivideOrRemainderValues(Sema& S, ExprResult &LHS, << IsDiv << RHS.get()->getSourceRange()); } +static void diagnoseScopedEnums(Sema &S, const SourceLocation Loc, + const ExprResult &LHS, const ExprResult &RHS, + BinaryOperatorKind Opc) { + const Expr *LHSExpr = LHS.get(); + const Expr *RHSExpr = RHS.get(); + if (!LHSExpr || !RHSExpr) + return; + const QualType LHSType = LHSExpr->getType(); + const QualType RHSType = RHSExpr->getType(); + const bool LHSIsScoped = LHSType->isScopedEnumeralType(); + const bool RHSIsScoped = RHSType->isScopedEnumeralType(); + if (!LHSIsScoped && !RHSIsScoped) + return; + if (!LHSIsScoped && !LHSType->isIntegralOrUnscopedEnumerationType()) + return; + if (!RHSIsScoped && !RHSType->isIntegralOrUnscopedEnumerationType()) + return; + if (BinaryOperator::isAssignmentOp(Opc) && LHSIsScoped) + return; + if (LHSIsScoped) { + SourceLocation LHSBegin = LHSExpr->getBeginLoc(); + QualType LHSIntType = + LHSType->castAs<EnumType>()->getDecl()->getIntegerType(); + S.Diag(LHSBegin, diag::note_no_implicit_conversion_for_scoped_enum) + << FixItHint::CreateInsertion( + LHSBegin, "static_cast<" + LHSIntType.getAsString() + ">(") + << FixItHint::CreateInsertion(LHSExpr->getEndLoc(), ")"); + } + if (RHSIsScoped) { + SourceLocation RHSBegin = RHSExpr->getBeginLoc(); + QualType RHSIntType = + RHSType->castAs<EnumType>()->getDecl()->getIntegerType(); + S.Diag(RHSBegin, diag::note_no_implicit_conversion_for_scoped_enum) + << FixItHint::CreateInsertion( + RHSBegin, "static_cast<" + RHSIntType.getAsString() + ">(") + << FixItHint::CreateInsertion(RHSExpr->getEndLoc(), ")"); + } +} + QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign, bool IsDiv) { @@ -10772,9 +10811,14 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS, if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); - - if (compType.isNull() || !compType->isArithmeticType()) - return InvalidOperands(Loc, LHS, RHS); + if (compType.isNull() || !compType->isArithmeticType()) { + InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, + IsCompAssign ? IsDiv ? BO_DivAssign : BO_MulAssign + : IsDiv ? BO_Div + : BO_Mul); + return QualType(); + } if (IsDiv) { DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc); DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv); @@ -10837,8 +10881,12 @@ QualType Sema::CheckRemainderOperands( if (compType.isNull() || (!compType->isIntegerType() && - !(getLangOpts().HLSL && compType->isFloatingType()))) - return InvalidOperands(Loc, LHS, RHS); + !(getLangOpts().HLSL && compType->isFloatingType()))) { + InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, + IsCompAssign ? BO_RemAssign : BO_Rem); + return QualType(); + } DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */); return compType; } @@ -11194,7 +11242,9 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS, } else if (PExp->getType()->isObjCObjectPointerType()) { isObjCPointer = true; } else { - return InvalidOperands(Loc, LHS, RHS); + InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return QualType(); } } assert(PExp->getType()->isAnyPointerType()); @@ -11251,7 +11301,8 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS, // C99 6.5.6 QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, - QualType* CompLHSTy) { + BinaryOperatorKind Opc, + QualType *CompLHSTy) { checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); if (LHS.get()->getType()->isVectorType() || @@ -11396,7 +11447,9 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS, } } - return InvalidOperands(Loc, LHS, RHS); + InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return QualType(); } static bool isScopedEnumerationType(QualType T) { @@ -11744,8 +11797,11 @@ QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS, // Embedded-C 4.1.6.2.2: The LHS may also be fixed-point. if ((!LHSType->isFixedPointOrIntegerType() && !LHSType->hasIntegerRepresentation()) || - !RHSType->hasIntegerRepresentation()) - return InvalidOperands(Loc, LHS, RHS); + !RHSType->hasIntegerRepresentation()) { + InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return QualType(); + } // C++0x: Don't allow scoped enums. FIXME: Use something better than // hasIntegerRepresentation() above instead of this. @@ -12311,8 +12367,11 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, S.UsualArithmeticConversions(LHS, RHS, Loc, ArithConvKind::Comparison); if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); - if (Type.isNull()) - return S.InvalidOperands(Loc, LHS, RHS); + if (Type.isNull()) { + S.InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(S, Loc, LHS, RHS, BO_Cmp); + return QualType(); + } std::optional<ComparisonCategoryType> CCT = getComparisonCategoryForBuiltinCmp(Type); @@ -12344,8 +12403,11 @@ static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, S.UsualArithmeticConversions(LHS, RHS, Loc, ArithConvKind::Comparison); if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); - if (Type.isNull()) - return S.InvalidOperands(Loc, LHS, RHS); + if (Type.isNull()) { + S.InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(S, Loc, LHS, RHS, Opc); + return QualType(); + } assert(Type->isArithmeticType() || Type->isEnumeralType()); if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc)) @@ -13355,7 +13417,9 @@ inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS, if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType()) return compType; - return InvalidOperands(Loc, LHS, RHS); + InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return QualType(); } // C99 6.5.[13,14] @@ -13457,13 +13521,19 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, // C++ [expr.log.or]p1 // The operands are both contextually converted to type bool. ExprResult LHSRes = PerformContextuallyConvertToBool(LHS.get()); - if (LHSRes.isInvalid()) - return InvalidOperands(Loc, LHS, RHS); + if (LHSRes.isInvalid()) { + InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return QualType(); + } LHS = LHSRes; ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get()); - if (RHSRes.isInvalid()) - return InvalidOperands(Loc, LHS, RHS); + if (RHSRes.isInvalid()) { + InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return QualType(); + } RHS = RHSRes; // C++ [expr.log.and]p2 @@ -15069,7 +15139,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, break; case BO_Sub: ConvertHalfVec = true; - ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc); + ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc); break; case BO_Shl: case BO_Shr: @@ -15136,7 +15206,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, break; case BO_SubAssign: ConvertHalfVec = true; - CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy); + CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); diff --git a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp index d88d5beb4e99e..7a0cacc2e65be 100644 --- a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp +++ b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp @@ -13,6 +13,7 @@ enum class E { e }; template<typename T> int f(T t) { return ~t; } // expected-error {{invalid argument type}} template<typename T, typename U> int f(T t, U u) { return t % u; } // expected-error {{invalid operands to}} + // expected-note@-1 {{no implicit conversion for scoped enum}} int b1 = ~E::e; // expected-error {{invalid argument type}} int b2 = f(E::e); // expected-note {{in instantiation of}} diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp index 2d7b3c9557ebd..091afff024b4c 100644 --- a/clang/test/SemaCXX/enum-scoped.cpp +++ b/clang/test/SemaCXX/enum-scoped.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++11 -verify -triple x86_64-apple-darwin %s // RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++17 -verify -triple x86_64-apple-darwin %s +// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++20 -verify -triple x86_64-apple-darwin %s enum class E1 { Val1 = 1L @@ -128,7 +129,10 @@ namespace rdar9366066 { void f(X x) { x % X::value; // expected-error{{invalid operands to binary expression ('X' and 'rdar9366066::X')}} + // expected-note@-1{{no implicit conversion for scoped enum; consider casting to underlying type}} + // expected-note@-2{{no implicit conversion for scoped enum; consider casting to underlying type}} x % 8; // expected-error{{invalid operands to binary expression ('X' and 'int')}} + // expected-note@-1{{no implicit conversion for scoped enum; consider casting to underlying type}} } } @@ -325,8 +329,10 @@ namespace PR18044 { int E::*p; // expected-error {{does not point into a class}} using E::f; // expected-error {{no member named 'f'}} + #if __cplusplus < 202002L using E::a; // expected-warning {{using declaration naming a scoped enumerator is a C++20 extension}} E b = a; + #endif } namespace test11 { @@ -364,3 +370,102 @@ S<_Atomic(int)> s; // expected-warning {{'_Atomic' is a C11 extension}} static_assert(__is_same(__underlying_type(S<_Atomic(long long)>::OhBoy), long long), ""); // expected-warning {{'_Atomic' is a C11 extension}} // expected-note@-1 {{in instantiation of template class 'GH147736::S<_Atomic(long long)>' requested here}} } + +namespace GH24265 { + enum class E_int { e }; + enum class E_long : long { e }; + + void f() { + E_int::e + E_long::e; // expected-error {{invalid operands to binary expression ('GH24265::E_int' and 'GH24265::E_long')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + // expected-note@-2 {{no implicit conversion for scoped enum; consider casting to underlying type}} + E_int::e + 0; // expected-error {{invalid operands to binary expression ('GH24265::E_int' and 'int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + + 0 * E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 / E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 % E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 + E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 - E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 << E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 >> E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + + #if __cplusplus >= 202002L + 0 <=> E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + #endif + + 0 < E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 > E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 <= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 >= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 == E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 != E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 & E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 ^ E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 | E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 && E_int::e; // expected-error {{value of type 'GH24265::E_int' is not contextually convertible to 'bool'}} + // expected-error@-1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-2 {{no implicit conversion for scoped enum; consider casting to underlying type}} + 0 || E_int::e; // expected-error {{value of type 'GH24265::E_int' is not contextually convertible to 'bool'}} + // expected-error@-1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-2 {{no implicit conversion for scoped enum; consider casting to underlying type}} + + int a; + a *= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a /= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a %= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a += E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a -= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a <<= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a >>= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a &= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a ^= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + a |= E_int::e; // expected-error {{invalid operan... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/152698 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits