https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105189
--- Comment #3 from Jakub Jelinek <jakub at gcc dot gnu.org> --- I think the bug is in make_range_step. When we see (unsigned) foo () >= 0U, first make_range_step determines +[0U, -] range for (unsigned) foo (), that is correct (though equivalent to +[-, -] aka always true). But then we handle the NOP_EXPR in another make_range_step call, exp_type is unsigned int, arg0_type is int. There is code that handles signed exp_type and unsigned arg0_type and we punt when arg0_type is not integral, or has higher precision than exp_type (i.e. narrowing conversion) or if either of the bounds (if specified) doesn't fit into arg0_type. None of that is the case here, so we incorrectly change the range to foo () +[0, -] (but e.g. (unsigned) foo () +[-, -] would be handled correctly as foo () +[-, -]). At least for the TYPE_PRECISION equal case, the unsigned exp_type signed arg0_type case is IMHO valid as is only if high is non-NULL or both low and high are NULL (if high is non-NULL and higher than signed maximum, we already punt), because if high is NULL, the range includes the largest unsigned value, which is -1 in signed and when low is non-NULL, it is necessarily [0, largest_signed].