--- gcc/c-family/c-warn.cc.jj	2022-11-23 10:04:53.000000000 +0100
+++ gcc/c-family/c-warn.cc	2022-11-23 11:19:38.928113842 +0100
@@ -2344,13 +2344,33 @@ warn_for_sign_compare (location_t locati
      have all bits set that are set in the ~ operand when it is
      extended.  */
 
+  /* bits0 is the bit index of op0 extended to result_type, which will
+     be always 0 and so all bits above it.  If there is a BIT_NOT_EXPR
+     in that operand possibly sign or zero extended to op0 and then
+     possibly further sign or zero extended to result_type, bits0 will
+     be the precision of result type if all the extensions involved
+     if any are sign extensions, and will be the place of the innermost
+     zero extension otherwise.  We warn only if BIT_NOT_EXPR's operand is
+     zero extended from some even smaller precision, in that case after
+     BIT_NOT_EXPR some bits below bits0 will be guaranteed to be set.
+     Similarly for bits1.  */
+  int bits0 = TYPE_PRECISION (result_type);
+  if (TYPE_UNSIGNED (TREE_TYPE (op0)))
+    bits0 = TYPE_PRECISION (TREE_TYPE (op0));
   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));
+  else if (unsignedp0)
+    bits0 = TYPE_PRECISION (TREE_TYPE (arg0));
   op0 = arg0;
+  int bits1 = TYPE_PRECISION (result_type);
+  if (TYPE_UNSIGNED (TREE_TYPE (op1)))
+    bits1 = TYPE_PRECISION (TREE_TYPE (op1));
   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));
+  else if (unsignedp1)
+    bits1 = TYPE_PRECISION (TREE_TYPE (arg1));
   op1 = arg1;
 
   if ((TREE_CODE (op0) == BIT_NOT_EXPR)
@@ -2360,6 +2380,7 @@ warn_for_sign_compare (location_t locati
 	{
 	  std::swap (op0, op1);
 	  std::swap (unsignedp0, unsignedp1);
+	  std::swap (bits0, bits1);
 	}
 
       int unsignedp;
@@ -2378,16 +2399,8 @@ warn_for_sign_compare (location_t locati
 	      && bits < HOST_BITS_PER_WIDE_INT)
 	    {
 	      HOST_WIDE_INT mask = HOST_WIDE_INT_M1U << bits;
-	      if (unsignedp0)
-		{
-		  bits = TYPE_PRECISION (TREE_TYPE (op0));
-		  if (bits < TYPE_PRECISION (result_type)
-		      && bits < HOST_BITS_PER_WIDE_INT)
-		    mask &= ~(HOST_WIDE_INT_M1U << bits);
-		}
-	      bits = TYPE_PRECISION (result_type);
-	      if (bits < HOST_BITS_PER_WIDE_INT)
-		mask &= ~(HOST_WIDE_INT_M1U << bits);
+	      if (bits0 < HOST_BITS_PER_WIDE_INT)
+		mask &= ~(HOST_WIDE_INT_M1U << bits0);
 	      if ((mask & constant) != mask)
 		{
 		  if (constant == 0)
@@ -2405,24 +2418,7 @@ warn_for_sign_compare (location_t locati
 		< 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 (unsignedp0
-				     ? TREE_TYPE (op0)
-				     : result_type)))
+	       && TYPE_PRECISION (TREE_TYPE (op1)) < bits0)
 	warning_at (location, OPT_Wsign_compare,
 		    "comparison of promoted bitwise complement "
 		    "of an unsigned value with unsigned");
--- gcc/testsuite/c-c++-common/Wsign-compare-2.c.jj	2022-11-23 10:04:53.000000000 +0100
+++ gcc/testsuite/c-c++-common/Wsign-compare-2.c	2022-11-23 11:05:10.497862219 +0100
@@ -104,3 +104,9 @@ f17 (unsigned char x, unsigned short y)
 {
   return (unsigned short) (~(unsigned short) x) == y;		/* { dg-bogus "comparison of promoted bitwise complement of an unsigned value with unsigned" } */
 }
+
+int
+f18 (unsigned char x)
+{
+  return (unsigned int) (short) (~(unsigned short) x) == 0xffffff05ULL;	/* { dg-bogus "comparison of promoted bitwise complement of an unsigned value with constant" } */
+}
