Message ID | Y3yXEaqKdvWxf9v0@tucnak |
---|---|

State | New |

Headers | |

Series | c-family: Fix up -Wsign-compare BIT_NOT_EXPR handling [PR107465] | |

## Commit Message

--- 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