sberg created this revision.
sberg added reviewers: jordan_rose, rtrieu.
Herald added a project: clang.
sberg requested review of this revision.

...to C++ constant expression operands.

(I had been puzzled why Clang had not found <https://git.libreoffice.org/core/+/
55f3a4595891a8cc22272225d1c82419f28d4ef9%5E!/> "cid#1465512 Wrong operator used"
in the LibreOffice source code, only found by Coverity Scan.  At least building
LibreOffice with this change caused no false positives.)

In C, values of const-qualified variables are never constant expressions, so
nothing should change for C code.

To avoid potential further false positives, restrict this change only to the
"bitwise or with non-zero value" warnings while keeping all other
-Wtautological-bitwise-compare warnings as-is, at least for now.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D85287

Files:
  clang/lib/Analysis/CFG.cpp
  clang/test/Sema/warn-bitwise-compare.c
  clang/test/SemaCXX/warn-bitwise-compare.cpp

Index: clang/test/SemaCXX/warn-bitwise-compare.cpp
===================================================================
--- clang/test/SemaCXX/warn-bitwise-compare.cpp
+++ clang/test/SemaCXX/warn-bitwise-compare.cpp
@@ -1,6 +1,12 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wtautological-bitwise-compare %s
 // RUN: %clang_cc1 -fsyntax-only -verify -Wall -Wno-unused %s
 
+int f() { return 3; }
+
+const int const1 = 1;
+constexpr int const2 = 2;
+const int const3 = f();
+
 void test(int x) {
   bool b1 = (8 & x) == 3;
   // expected-warning@-1 {{bitwise comparison always evaluates to false}}
@@ -10,4 +16,15 @@
   // expected-warning@-1 {{bitwise or with non-zero value always evaluates to true}}
   bool b4 = !!(x | 5);
   // expected-warning@-1 {{bitwise or with non-zero value always evaluates to true}}
+  bool b5 = (x | const1);
+  // expected-warning@-1 {{bitwise or with non-zero value always evaluates to true}}
+  bool b6 = (x | const2);
+  // expected-warning@-1 {{bitwise or with non-zero value always evaluates to true}}
+  bool b7 = (x | const3);
+
+  // No warnings at least for now:
+  bool b8 = (x & const1) == 8;
+  bool b9 = (x | const1) == 4;
+  bool b10 = (x & const2) == 8;
+  bool b11 = (x | const2) == 4;
 }
Index: clang/test/Sema/warn-bitwise-compare.c
===================================================================
--- clang/test/Sema/warn-bitwise-compare.c
+++ clang/test/Sema/warn-bitwise-compare.c
@@ -8,6 +8,8 @@
   ONE,
 };
 
+const int const1 = 1;
+
 void f(int x) {
   if ((8 & x) == 3) {}  // expected-warning {{bitwise comparison always evaluates to false}}
   if ((x & 8) == 4) {}  // expected-warning {{bitwise comparison always evaluates to false}}
@@ -34,6 +36,9 @@
 
   if ((x & mydefine) == 8) {}
   if ((x | mydefine) == 4) {}
+
+  if ((x & const1) == 8) {}
+  if ((x | const1) == 4) {}
 }
 
 void g(int x) {
@@ -45,4 +50,6 @@
   if (x | ONE) {}  // expected-warning {{bitwise or with non-zero value always evaluates to true}}
 
   if (x | ZERO) {}
+
+  if (x | const1) {}
 }
Index: clang/lib/Analysis/CFG.cpp
===================================================================
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -93,15 +93,22 @@
   return isa<IntegerLiteral>(E);
 }
 
-/// Helper for tryNormalizeBinaryOperator. Attempts to extract an IntegerLiteral
-/// constant expression or EnumConstantDecl from the given Expr. If it fails,
+/// Helper for tryNormalizeBinaryOperator. Attempts to extract a suitable
+/// integer or enum constant expression from the given Expr. If it fails,
 /// returns nullptr.
-static const Expr *tryTransformToIntOrEnumConstant(const Expr *E) {
+static const Expr *tryTransformToIntOrEnumConstant(const Expr *E,
+                                                   ASTContext &Ctx) {
   E = E->IgnoreParens();
   if (IsIntegerLiteralConstantExpr(E))
     return E;
-  if (auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
-    return isa<EnumConstantDecl>(DR->getDecl()) ? DR : nullptr;
+  if (auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) {
+    auto *D = DR->getDecl();
+    if (isa<EnumConstantDecl>(D))
+      return DR;
+    if (auto *VD = dyn_cast<VarDecl>(D))
+      if (VD->isUsableInConstantExpressions(Ctx))
+        return DR;
+  }
   return nullptr;
 }
 
@@ -111,11 +118,11 @@
 /// If this fails, at least one of the returned DeclRefExpr or Expr will be
 /// null.
 static std::tuple<const Expr *, BinaryOperatorKind, const Expr *>
-tryNormalizeBinaryOperator(const BinaryOperator *B) {
+tryNormalizeBinaryOperator(const BinaryOperator *B, ASTContext &Ctx) {
   BinaryOperatorKind Op = B->getOpcode();
 
   const Expr *MaybeDecl = B->getLHS();
-  const Expr *Constant = tryTransformToIntOrEnumConstant(B->getRHS());
+  const Expr *Constant = tryTransformToIntOrEnumConstant(B->getRHS(), Ctx);
   // Expr looked like `0 == Foo` instead of `Foo == 0`
   if (Constant == nullptr) {
     // Flip the operator
@@ -129,7 +136,7 @@
       Op = BO_GE;
 
     MaybeDecl = B->getRHS();
-    Constant = tryTransformToIntOrEnumConstant(B->getLHS());
+    Constant = tryTransformToIntOrEnumConstant(B->getLHS(), Ctx);
   }
 
   return std::make_tuple(MaybeDecl, Op, Constant);
@@ -140,7 +147,7 @@
 /// literals.
 ///
 /// It's an error to pass this arguments that are not either IntegerLiterals
-/// or DeclRefExprs (that have decls of type EnumConstantDecl)
+/// or DeclRefExprs
 static bool areExprTypesCompatible(const Expr *E1, const Expr *E2) {
   // User intent isn't clear if they're mixing int literals with enum
   // constants.
@@ -151,18 +158,21 @@
   if (!isa<DeclRefExpr>(E1))
     return true;
 
-  // IntegerLiterals are handled above and only EnumConstantDecls are expected
+  // IntegerLiterals are handled above and only certain Decls are expected
   // beyond this point
   assert(isa<DeclRefExpr>(E1) && isa<DeclRefExpr>(E2));
   auto *Decl1 = cast<DeclRefExpr>(E1)->getDecl();
   auto *Decl2 = cast<DeclRefExpr>(E2)->getDecl();
 
-  assert(isa<EnumConstantDecl>(Decl1) && isa<EnumConstantDecl>(Decl2));
-  const DeclContext *DC1 = Decl1->getDeclContext();
-  const DeclContext *DC2 = Decl2->getDeclContext();
+  if (isa<EnumConstantDecl>(Decl1) && isa<EnumConstantDecl>(Decl2)) {
+    const DeclContext *DC1 = Decl1->getDeclContext();
+    const DeclContext *DC2 = Decl2->getDeclContext();
+
+    assert(isa<EnumDecl>(DC1) && isa<EnumDecl>(DC2));
+    return DC1 == DC2;
+  }
 
-  assert(isa<EnumDecl>(DC1) && isa<EnumDecl>(DC2));
-  return DC1 == DC2;
+  return false;
 }
 
 namespace {
@@ -1046,7 +1056,8 @@
     const Expr *DeclExpr1;
     const Expr *NumExpr1;
     BinaryOperatorKind BO1;
-    std::tie(DeclExpr1, BO1, NumExpr1) = tryNormalizeBinaryOperator(LHS);
+    std::tie(DeclExpr1, BO1, NumExpr1) =
+        tryNormalizeBinaryOperator(LHS, *Context);
 
     if (!DeclExpr1 || !NumExpr1)
       return {};
@@ -1054,7 +1065,8 @@
     const Expr *DeclExpr2;
     const Expr *NumExpr2;
     BinaryOperatorKind BO2;
-    std::tie(DeclExpr2, BO2, NumExpr2) = tryNormalizeBinaryOperator(RHS);
+    std::tie(DeclExpr2, BO2, NumExpr2) =
+        tryNormalizeBinaryOperator(RHS, *Context);
 
     if (!DeclExpr2 || !NumExpr2)
       return {};
@@ -1140,10 +1152,10 @@
 
   /// A bitwise-or with a non-zero constant always evaluates to true.
   TryResult checkIncorrectBitwiseOrOperator(const BinaryOperator *B) {
-    const Expr *LHSConstant =
-        tryTransformToIntOrEnumConstant(B->getLHS()->IgnoreParenImpCasts());
-    const Expr *RHSConstant =
-        tryTransformToIntOrEnumConstant(B->getRHS()->IgnoreParenImpCasts());
+    const Expr *LHSConstant = tryTransformToIntOrEnumConstant(
+        B->getLHS()->IgnoreParenImpCasts(), *Context);
+    const Expr *RHSConstant = tryTransformToIntOrEnumConstant(
+        B->getRHS()->IgnoreParenImpCasts(), *Context);
 
     if ((LHSConstant && RHSConstant) || (!LHSConstant && !RHSConstant))
       return {};
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to