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");