--- gcc/c-family/c-warn.cc.jj	2022-10-28 11:00:53.738247032 +0200
+++ gcc/c-family/c-warn.cc	2022-11-21 19:29:42.351885444 +0100
@@ -2344,42 +2344,50 @@ 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 (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 ((mask & constant) != mask)
 		{
 		  if (constant == 0)
@@ -2393,11 +2401,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");
--- gcc/testsuite/c-c++-common/Wsign-compare-2.c.jj	2022-11-21 19:40:24.968468682 +0100
+++ gcc/testsuite/c-c++-common/Wsign-compare-2.c	2022-11-21 20:15:04.706019401 +0100
@@ -0,0 +1,106 @@
+/* { dg-do compile { target { ilp32 || lp64 } } } */
+/* { dg-options "-Wsign-compare" } */
+
+int
+f1 (unsigned char x)
+{
+  return (unsigned short) (~(unsigned short) x) == 0;		/* { dg-warning "promoted bitwise complement of an unsigned value is always nonzero" "" { target c } } */
+}
+
+int
+f2 (unsigned char x)
+{
+  return (unsigned short) (~(unsigned short) x) == 5;		/* { dg-warning "comparison of promoted bitwise complement of an unsigned value with constant" "" { target c }  } */
+}
+
+int
+f3 (unsigned char x)
+{
+  return (unsigned int) (~(unsigned short) x) == 0xffff0005U;	/* { dg-warning "comparison of promoted bitwise complement of an unsigned value with constant" } */
+}
+
+int
+f4 (unsigned char x)
+{
+  return (unsigned int) (~(unsigned short) x) == 0xffff0005ULL;	/* { dg-warning "comparison of promoted bitwise complement of an unsigned value with constant" } */
+}
+
+int
+f5 (unsigned char x)
+{
+  return (unsigned int) (~(unsigned short) x) == 0xffffff05U;	/* { dg-bogus "comparison of promoted bitwise complement of an unsigned value with constant" } */
+}
+
+int
+f6 (unsigned char x)
+{
+  return (unsigned int) (~(unsigned short) x) == 0xffffff05ULL;	/* { dg-bogus "comparison of promoted bitwise complement of an unsigned value with constant" } */
+}
+
+int
+f7 (unsigned char x)
+{
+  return (unsigned long long) (~(unsigned short) x) == 0xffffffffffffff05ULL;	/* { dg-bogus "comparison of promoted bitwise complement of an unsigned value with constant" } */
+}
+
+typedef unsigned short T;
+
+int
+f8 (T x)
+{
+  return (unsigned short) (~(unsigned short) x) == 0;		/* { dg-bogus "promoted bitwise complement of an unsigned value is always nonzero" } */
+}
+
+int
+f9 (T x)
+{
+  return (unsigned short) (~(unsigned short) x) == 5;		/* { dg-bogus "comparison of promoted bitwise complement of an unsigned value with constant" } */
+}
+
+int
+f10 (T x, unsigned char y)
+{
+  return (unsigned short) (~(unsigned short) x) == y;		/* { dg-bogus "comparison of promoted bitwise complement of an unsigned value with unsigned" } */
+}
+
+int
+f11 (T x, unsigned char y)
+{
+  return (unsigned short) (~(unsigned short) x) == y;		/* { dg-bogus "comparison of promoted bitwise complement of an unsigned value with unsigned" } */
+}
+
+int
+f12 (unsigned char x, unsigned char y)
+{
+  return (unsigned short) (~(unsigned short) x) == y;		/* { dg-warning "comparison of promoted bitwise complement of an unsigned value with unsigned" "" { target c } } */
+}
+
+int
+f13 (unsigned char x, unsigned char y)
+{
+  return (unsigned short) (~(unsigned short) x) == y;		/* { dg-warning "comparison of promoted bitwise complement of an unsigned value with unsigned" "" { target c } } */
+}
+
+int
+f14 (unsigned char x, unsigned int y)
+{
+  return (unsigned long long) (~x) == y;			/* { dg-warning "comparison of promoted bitwise complement of an unsigned value with unsigned" } */
+}
+
+int
+f15 (unsigned short x, unsigned int y)
+{
+  return (long long) (~x) == y;					/* { dg-warning "comparison of promoted bitwise complement of an unsigned value with unsigned" } */
+}
+
+int
+f16 (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
+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" } */
+}
--- gcc/testsuite/c-c++-common/pr107465.c.jj	2022-11-21 16:31:01.052460446 +0100
+++ gcc/testsuite/c-c++-common/pr107465.c	2022-11-21 16:30:45.512687167 +0100
@@ -0,0 +1,22 @@
+/* PR c/107465 */
+/* { dg-do compile } */
+/* { dg-options "-Wsign-compare" } */
+
+void baz (void);
+typedef unsigned short T;
+
+#if __SIZEOF_SHORT__ * __CHAR_BIT__ == 16
+void
+foo (unsigned short x)
+{
+  if (!(x ^ 0xFFFF))
+    baz ();
+}
+
+void
+bar (T x)
+{
+  if (!(x ^ 0xFFFF))	/* { dg-bogus "promoted bitwise complement of an unsigned value is always nonzero" } */
+    baz ();
+}
+#endif
