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

Reply via email to