https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107465

--- Comment #7 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
Untested fix, just know it fixes the testcase.
But I need to see what changes does it cause in the C/C++ testsuites and
probably write more extensive testsuite coverage.
--- gcc/c/c-warn.cc.jj  2022-10-28 11:00:53.738247032 +0200
+++ gcc/c/c-warn.cc     2022-11-21 16:27:13.997773107 +0100
@@ -2344,42 +2344,40 @@ warn_for_sign_compare (location_t locati
      have all bits set that are set in the ~ operand when it is
      extended.  */

-  op0 = c_common_get_narrower (op0, &unsignedp0);
-  op1 = c_common_get_narrower (op1, &unsignedp1);
+  tree arg0 = c_common_get_narrower (op0, &unsignedp0);
+  if (TYPE_PRECISION (TREE_TYPE (arg0)) == TYPE_PRECISION (TREE_TYPE (op0)))
+    unsignedp0 = TYPE_UNSIGNED (TREE_TYPE (op0));
+  op0 = arg0;
+  tree arg1 = c_common_get_narrower (op1, &unsignedp1);
+  if (TYPE_PRECISION (TREE_TYPE (arg1)) == TYPE_PRECISION (TREE_TYPE (op1)))
+    unsignedp1 = TYPE_UNSIGNED (TREE_TYPE (op1));
+  op1 = arg1;

   if ((TREE_CODE (op0) == BIT_NOT_EXPR)
       ^ (TREE_CODE (op1) == BIT_NOT_EXPR))
     {
-      if (TREE_CODE (op0) == BIT_NOT_EXPR)
-       op0 = c_common_get_narrower (TREE_OPERAND (op0, 0), &unsignedp0);
       if (TREE_CODE (op1) == BIT_NOT_EXPR)
-       op1 = c_common_get_narrower (TREE_OPERAND (op1, 0), &unsignedp1);
-
-      if (tree_fits_shwi_p (op0) || tree_fits_shwi_p (op1))
        {
-         tree primop;
-         HOST_WIDE_INT constant, mask;
-         int unsignedp;
-         unsigned int bits;
+         std::swap (op0, op1);
+         std::swap (unsignedp0, unsignedp1);
+       }

-         if (tree_fits_shwi_p (op0))
-           {
-             primop = op1;
-             unsignedp = unsignedp1;
-             constant = tree_to_shwi (op0);
-           }
-         else
-           {
-             primop = op0;
-             unsignedp = unsignedp0;
-             constant = tree_to_shwi (op1);
-           }
+      int unsignedp;
+      arg0 = c_common_get_narrower (TREE_OPERAND (op0, 0), &unsignedp);

-         bits = TYPE_PRECISION (TREE_TYPE (primop));
-         if (bits < TYPE_PRECISION (result_type)
-             && bits < HOST_BITS_PER_LONG && unsignedp)
+      /* For these warnings, we need BIT_NOT_EXPR operand to be
+        zero extended from narrower type to BIT_NOT_EXPR's type.
+        In that case, all those bits above the narrower's type
+        are after BIT_NOT_EXPR set to 1.  */
+      if (tree_fits_shwi_p (op1))
+       {
+         HOST_WIDE_INT constant = tree_to_shwi (op1);
+         unsigned int bits = TYPE_PRECISION (TREE_TYPE (arg0));
+         if (unsignedp
+             && bits < TYPE_PRECISION (TREE_TYPE (op0))
+             && bits < HOST_BITS_PER_WIDE_INT)
            {
-             mask = HOST_WIDE_INT_M1U << bits;
+             HOST_WIDE_INT mask = HOST_WIDE_INT_M1U << bits;
              if ((mask & constant) != mask)
                {
                  if (constant == 0)
@@ -2393,11 +2391,28 @@ warn_for_sign_compare (location_t locati
                }
            }
        }
-      else if (unsignedp0 && unsignedp1
-              && (TYPE_PRECISION (TREE_TYPE (op0))
-                  < TYPE_PRECISION (result_type))
+      else if ((TYPE_PRECISION (TREE_TYPE (arg0))
+               < TYPE_PRECISION (TREE_TYPE (op0)))
+              && unsignedp
+              && unsignedp1
+              /* If unsignedp0, the BIT_NOT_EXPR result is
+                 zero extended, so say if op0 is unsigned char
+                 variable, BIT_NOT_EXPR is unsigned short and
+                 result type int and op0 has value 0x55, the
+                 int value will be 0xffaa, or for op0 0xaa it
+                 will be 0xff55.  In these cases, warn if
+                 op1 is unsigned and narrower than unsigned short.
+                 While if unsignedp0 is false, the BIT_NOT_EXPR
+                 result is sign extended and because of the
+                 above TYPE_PRECISION comparison we know the
+                 MSB of BIT_NOT_EXPR is set (perhaps with some
+                 further bits below it).  The sign extension will
+                 then ensure all bits above BIT_NOT_EXPR up to
+                 result_type's precision are set.  */
               && (TYPE_PRECISION (TREE_TYPE (op1))
-                  < TYPE_PRECISION (result_type)))
+                  < TYPE_PRECISION (unsignedp0
+                                    ? TREE_TYPE (op0)
+                                    : result_type)))
        warning_at (location, OPT_Wsign_compare,
                    "comparison of promoted bitwise complement "
                    "of an unsigned value with unsigned");

Reply via email to