Author: Timothy Choi
Date: 2025-08-23T09:14:16Z
New Revision: 8b5503d9b1fca2eb9ceb7439100235ff0e53394b

URL: 
https://github.com/llvm/llvm-project/commit/8b5503d9b1fca2eb9ceb7439100235ff0e53394b
DIFF: 
https://github.com/llvm/llvm-project/commit/8b5503d9b1fca2eb9ceb7439100235ff0e53394b.diff

LOG: [Clang] Add diagnostic when scoped enumeration requires an explicit 
conversion for binary operations (#152698)

```diff
 main.cpp:4:8: error: invalid operands to binary expression ('E' and 'int')
     4 |   E::e + 0;
       |   ~~~~ ^ ~
+main.cpp:4:3: note: no implicit conversion for scoped enum; consider casting 
to underlying type
+    4 |   E::e + 0;
+      |   ^  
+      |   static_cast<int>( )
```

Resolves #24265

Added: 
    clang/test/FixIt/fixit-enum-scoped.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaExpr.cpp
    clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp
    clang/test/SemaCXX/enum-scoped.cpp
    clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 465c50784f27f..a941c6521d90e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -209,6 +209,7 @@ Improvements to Clang's diagnostics
   an override of a virtual method.
 - Fixed fix-it hint for fold expressions. Clang now correctly places the 
suggested right
   parenthesis when diagnosing malformed fold expressions. (#GH151787)
+- Added fix-it hint for when scoped enumerations require explicit conversions 
for binary operations. (#GH24265)
 
 - Fixed an issue where emitted format-signedness diagnostics were not 
associated with an appropriate
   diagnostic id. Besides being incorrect from an API standpoint, this was user 
visible, e.g.:

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f50d245965648..4c98e0f8b04ed 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4407,6 +4407,11 @@ def warn_impcast_
diff erent_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 12112eb1ad225..a78f5635f44da 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8065,8 +8065,8 @@ class Sema final : public SemaBase {
                                         ExprResult &RHS);
 
   QualType CheckMultiplyDivideOperands( // C99 6.5.5
-      ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign,
-      bool IsDivide);
+      ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
+      BinaryOperatorKind Opc);
   QualType CheckRemainderOperands( // C99 6.5.5
       ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
       bool IsCompAssign = false);
@@ -8075,7 +8075,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 911c80adfd539..caa04e73ab2ab 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10748,9 +10748,50 @@ 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) {
+  if (!LHS.isUsable() || !RHS.isUsable())
+    return;
+  const Expr *LHSExpr = LHS.get();
+  const Expr *RHSExpr = RHS.get();
+  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 (BinaryOperator::isAssignmentOp(Opc) && LHSIsScoped)
+    return;
+  if (!LHSIsScoped && !LHSType->isIntegralOrUnscopedEnumerationType())
+    return;
+  if (!RHSIsScoped && !RHSType->isIntegralOrUnscopedEnumerationType())
+    return;
+  auto DiagnosticHelper = [&S](const Expr *expr, const QualType type) {
+    SourceLocation BeginLoc = expr->getBeginLoc();
+    QualType IntType = type->castAs<EnumType>()
+                           ->getOriginalDecl()
+                           ->getDefinitionOrSelf()
+                           ->getIntegerType();
+    std::string InsertionString = "static_cast<" + IntType.getAsString() + 
">(";
+    S.Diag(BeginLoc, diag::note_no_implicit_conversion_for_scoped_enum)
+        << FixItHint::CreateInsertion(BeginLoc, InsertionString)
+        << FixItHint::CreateInsertion(expr->getEndLoc(), ")");
+  };
+  if (LHSIsScoped) {
+    DiagnosticHelper(LHSExpr, LHSType);
+  }
+  if (RHSIsScoped) {
+    DiagnosticHelper(RHSExpr, RHSType);
+  }
+}
+
 QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
                                            SourceLocation Loc,
-                                           bool IsCompAssign, bool IsDiv) {
+                                           BinaryOperatorKind Opc) {
+  bool IsCompAssign = Opc == BO_MulAssign || Opc == BO_DivAssign;
+  bool IsDiv = Opc == BO_Div || Opc == BO_DivAssign;
+
   checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false);
 
   QualType LHSTy = LHS.get()->getType();
@@ -10778,9 +10819,11 @@ 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()) {
+    QualType ResultTy = InvalidOperands(Loc, LHS, RHS);
+    diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+    return ResultTy;
+  }
   if (IsDiv) {
     DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc);
     DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv);
@@ -10843,8 +10886,12 @@ QualType Sema::CheckRemainderOperands(
 
   if (compType.isNull() ||
       (!compType->isIntegerType() &&
-       !(getLangOpts().HLSL && compType->isFloatingType())))
-    return InvalidOperands(Loc, LHS, RHS);
+       !(getLangOpts().HLSL && compType->isFloatingType()))) {
+    QualType ResultTy = InvalidOperands(Loc, LHS, RHS);
+    diagnoseScopedEnums(*this, Loc, LHS, RHS,
+                        IsCompAssign ? BO_RemAssign : BO_Rem);
+    return ResultTy;
+  }
   DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */);
   return compType;
 }
@@ -11200,7 +11247,9 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, 
ExprResult &RHS,
     } else if (PExp->getType()->isObjCObjectPointerType()) {
       isObjCPointer = true;
     } else {
-      return InvalidOperands(Loc, LHS, RHS);
+      QualType ResultTy = InvalidOperands(Loc, LHS, RHS);
+      diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+      return ResultTy;
     }
   }
   assert(PExp->getType()->isAnyPointerType());
@@ -11257,7 +11306,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() ||
@@ -11402,7 +11452,9 @@ QualType Sema::CheckSubtractionOperands(ExprResult 
&LHS, ExprResult &RHS,
     }
   }
 
-  return InvalidOperands(Loc, LHS, RHS);
+  QualType ResultTy = InvalidOperands(Loc, LHS, RHS);
+  diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+  return ResultTy;
 }
 
 static bool isScopedEnumerationType(QualType T) {
@@ -11750,8 +11802,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()) {
+    QualType ResultTy = InvalidOperands(Loc, LHS, RHS);
+    diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+    return ResultTy;
+  }
 
   // C++0x: Don't allow scoped enums. FIXME: Use something better than
   // hasIntegerRepresentation() above instead of this.
@@ -12319,8 +12374,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()) {
+    QualType ResultTy = S.InvalidOperands(Loc, LHS, RHS);
+    diagnoseScopedEnums(S, Loc, LHS, RHS, BO_Cmp);
+    return ResultTy;
+  }
 
   std::optional<ComparisonCategoryType> CCT =
       getComparisonCategoryForBuiltinCmp(Type);
@@ -12352,8 +12410,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()) {
+    QualType ResultTy = S.InvalidOperands(Loc, LHS, RHS);
+    diagnoseScopedEnums(S, Loc, LHS, RHS, Opc);
+    return ResultTy;
+  }
   assert(Type->isArithmeticType() || Type->isEnumeralType());
 
   if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc))
@@ -13363,7 +13424,9 @@ inline QualType Sema::CheckBitwiseOperands(ExprResult 
&LHS, ExprResult &RHS,
 
   if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType())
     return compType;
-  return InvalidOperands(Loc, LHS, RHS);
+  QualType ResultTy = InvalidOperands(Loc, LHS, RHS);
+  diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+  return ResultTy;
 }
 
 // C99 6.5.[13,14]
@@ -13465,13 +13528,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()) {
+    QualType ResultTy = InvalidOperands(Loc, LHS, RHS);
+    diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+    return ResultTy;
+  }
   LHS = LHSRes;
 
   ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get());
-  if (RHSRes.isInvalid())
-    return InvalidOperands(Loc, LHS, RHS);
+  if (RHSRes.isInvalid()) {
+    QualType ResultTy = InvalidOperands(Loc, LHS, RHS);
+    diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+    return ResultTy;
+  }
   RHS = RHSRes;
 
   // C++ [expr.log.and]p2
@@ -15067,8 +15136,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation 
OpLoc,
   case BO_Mul:
   case BO_Div:
     ConvertHalfVec = true;
-    ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false,
-                                           Opc == BO_Div);
+    ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, Opc);
     break;
   case BO_Rem:
     ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc);
@@ -15079,7 +15147,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:
@@ -15123,8 +15191,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation 
OpLoc,
   case BO_MulAssign:
   case BO_DivAssign:
     ConvertHalfVec = true;
-    CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true,
-                                               Opc == BO_DivAssign);
+    CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, Opc);
     CompLHSTy = CompResultTy;
     if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
       ResultTy =
@@ -15146,7 +15213,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/FixIt/fixit-enum-scoped.cpp 
b/clang/test/FixIt/fixit-enum-scoped.cpp
new file mode 100644
index 0000000000000..b7e4138b7d216
--- /dev/null
+++ b/clang/test/FixIt/fixit-enum-scoped.cpp
@@ -0,0 +1,95 @@
+// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -std=c++20 
-triple x86_64-apple-darwin %s 2>&1 | FileCheck %s
+
+namespace GH24265 {
+  enum class E_int { e };
+  enum class E_long : long { e };
+
+  void f() {
+    E_int::e + E_long::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:5-[[@LINE]]:5}:"static_cast<int>("
+                          // CHECK: 
fix-it:{{.*}}:{[[@LINE-1]]:12-[[@LINE-1]]:12}:")"
+                          // CHECK: 
fix-it:{{.*}}:{[[@LINE-2]]:16-[[@LINE-2]]:16}:"static_cast<long>("
+                          // CHECK: 
fix-it:{{.*}}:{[[@LINE-3]]:24-[[@LINE-3]]:24}:")"
+    E_int::e + 0; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:5-[[@LINE]]:5}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:12-[[@LINE-1]]:12}:")"
+
+    0 * E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 / E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 % E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 + E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 - E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 << E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    0 >> E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    0 <=> E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:11-[[@LINE]]:11}:"static_cast<int>("
+                    // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:18-[[@LINE-1]]:18}:")"
+    0 < E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 > E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 <= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    0 >= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    0 == E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    0 != E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    0 & E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 ^ E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 | E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>("
+                  // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")"
+    0 && E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    0 || E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+
+    int a;
+    a *= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    a /= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    a %= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    a += E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    a -= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    a <<= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:11-[[@LINE]]:11}:"static_cast<int>("
+                    // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:18-[[@LINE-1]]:18}:")"
+    a >>= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:11-[[@LINE]]:11}:"static_cast<int>("
+                    // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:18-[[@LINE-1]]:18}:")"
+    a &= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    a ^= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+    a |= E_int::e; // CHECK: 
fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>("
+                   // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")"
+
+    // TODO: These do not have the diagnostic yet
+    E_int b;
+    b *= 0;
+    b /= 0;
+    b %= 0;
+    b += 0;
+    b -= 0;
+    b <<= 0;
+    b >>= 0;
+    b &= 0;
+    b ^= 0;
+    b |= 0;
+
+    a = E_int::e;
+    b = 0;
+
+    E_int c = 0;
+    int d = E_int::e;
+  }
+}

diff  --git a/clang/test/SemaCXX/enum-scoped.cpp 
b/clang/test/SemaCXX/enum-scoped.cpp
index bf43f2928466f..09f3206c84ccb 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++11 
-verify=expected,cxx11-17 -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++17 
-verify=expected,cxx11-17 -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,7 +329,7 @@ namespace PR18044 {
   int E::*p; // expected-error {{does not point into a class}}
   using E::f; // expected-error {{no member named 'f'}}
 
-  using E::a; // expected-warning {{using declaration naming a scoped 
enumerator is a C++20 extension}}
+  using E::a; // cxx11-17-warning {{using declaration naming a scoped 
enumerator is a C++20 extension}}
   E b = a;
 }
 
@@ -364,3 +368,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 operands to binary expression 
('int' and 'GH24265::E_int')}}
+                   // expected-note@-1 {{no implicit conversion for scoped 
enum; consider casting to underlying type}}
+
+    // TODO: These do not have the diagnostic yet
+    E_int b;
+    b *= 0; // expected-error {{invalid operands to binary expression ('E_int' 
and 'int')}}
+    b /= 0; // expected-error {{invalid operands to binary expression ('E_int' 
and 'int')}}
+    b %= 0; // expected-error {{invalid operands to binary expression ('E_int' 
and 'int')}}
+    b += 0; // expected-error {{invalid operands to binary expression ('E_int' 
and 'int')}}
+    b -= 0; // expected-error {{invalid operands to binary expression ('E_int' 
and 'int')}}
+    b <<= 0; // expected-error {{invalid operands to binary expression 
('E_int' and 'int')}}
+    b >>= 0; // expected-error {{invalid operands to binary expression 
('E_int' and 'int')}}
+    b &= 0; // expected-error {{invalid operands to binary expression ('E_int' 
and 'int')}}
+    b ^= 0; // expected-error {{invalid operands to binary expression ('E_int' 
and 'int')}}
+    b |= 0; // expected-error {{invalid operands to binary expression ('E_int' 
and 'int')}}
+
+    a = E_int::e; // expected-error {{assigning to 'int' from incompatible 
type 'GH24265::E_int'}}
+    b = 0; // expected-error {{assigning to 'E_int' from incompatible type 
'int'}}
+
+    E_int c = 0; // expected-error {{cannot initialize a variable of type 
'E_int' with an rvalue of type 'int'}}
+    int d = E_int::e; // expected-error {{cannot initialize a variable of type 
'int' with an rvalue of type 'GH24265::E_int'}}
+  }
+}

diff  --git a/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp 
b/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp
index 7101a153c6ebb..4257e53ce9af0 100644
--- a/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp
+++ b/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp
@@ -94,7 +94,9 @@ struct S5 {
 };
 
 int X5 = S5<char>::E1{} + '\0'; // expected-error{{invalid operands to binary 
expression}}
+                                // expected-note@-1{{no implicit conversion 
for scoped enum; consider casting to underlying type}}
 int Y5 = S5<char>::E2{} + '\0'; // expected-error{{invalid operands to binary 
expression}}
+                                // expected-note@-1{{no implicit conversion 
for scoped enum; consider casting to underlying type}}
 
 
 template <typename T>


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to