Bug 89126 - missing -Wtype-limits for int variables
Summary: missing -Wtype-limits for int variables
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 9.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks: Wtype-limits
  Show dependency treegraph
 
Reported: 2019-01-30 23:21 UTC by Martin Sebor
Modified: 2021-01-06 14:34 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 4.4.7, 4.8.5, 4.9.4, 5.4.0, 6.4.0, 7.3.0, 8.2.0, 9.0
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Sebor 2019-01-30 23:21:48 UTC
GCC issues -Wtype-limits warnings for relational expressions that either cannot be true or cannot be false due to the limits of the type of one of their operands.  This seems to work except for operands of type int where GCC fails to issue any warnings.

$ cat u.c && gcc -S -Wall -Wextra -Wpedantic u.c
int fchar (signed char x)
{ 
  if (x <= __SCHAR_MAX__)   // -Wtype-limits (good)
    return 1;

  if (x > __SCHAR_MAX__)    // -Wtype-limits (good)
    return 1;

  return 0;
}

int fshrt (short x)
{ 
  if (x <= __SHRT_MAX__)    // -Wtype-limits (good)
    return 1;

  if (x > __SHRT_MAX__)     // -Wtype-limits (good)
    return 1;
    
  return 0;
}

int fint (int x)
{
  if (x <= __INT_MAX__)     // missing -Wtype-limits
    return 1;

  if (x > __INT_MAX__)     // missing -Wtype-limits
    return 1;

  return 0;
}


int flong (int x)
{
  if (x <= __LONG_MAX__)    // -Wtype-limits (good)
    return 1;

  if (x > __LONG_MAX__)     // -Wtype-limits (good)
    return 1;

  return 0;
}

u.c: In function ‘fchar’:
u.c:3:9: warning: comparison is always true due to limited range of data type [-Wtype-limits]
    3 |   if (x <= __SCHAR_MAX__)   // -Wtype-limits (good)
      |         ^~
u.c:6:9: warning: comparison is always false due to limited range of data type [-Wtype-limits]
    6 |   if (x > __SCHAR_MAX__)    // -Wtype-limits (good)
      |         ^
u.c: In function ‘fshrt’:
u.c:14:9: warning: comparison is always true due to limited range of data type [-Wtype-limits]
   14 |   if (x <= __SHRT_MAX__)    // -Wtype-limits (good)
      |         ^~
u.c:17:9: warning: comparison is always false due to limited range of data type [-Wtype-limits]
   17 |   if (x > __SHRT_MAX__)     // -Wtype-limits (good)
      |         ^
u.c: In function ‘flong’:
u.c:37:9: warning: comparison is always true due to limited range of data type [-Wtype-limits]
   37 |   if (x <= __LONG_MAX__)    // -Wtype-limits (good)
      |         ^~
u.c:40:9: warning: comparison is always false due to limited range of data type [-Wtype-limits]
   40 |   if (x > __LONG_MAX__)     // -Wtype-limits (good)
      |         ^
Comment 1 Martin Sebor 2019-01-30 23:23:51 UTC
This never seems to have worked.  Interestingly, Clang has the same bug.
Comment 2 Martin Sebor 2019-01-30 23:31:39 UTC
In ILP32 it doesn't work for long either.
Comment 3 Martin Sebor 2019-02-01 03:29:47 UTC
The problem is in shorten_compare() in c-common.c which deals with these cases.  The comment above the block that handles this has this to say just above the conditional that guards the code.  The conditional fails in the fint case because both operands of the inequality have the same precision:

  /* If comparing an integer against a constant more bits wide,
     maybe we can deduce a value of 1 or 0 independent of the data.
     Or else truncate the constant now
     rather than extend the variable at run time.

     This is only interesting if the constant is the wider arg.
     Also, it is not safe if the constant is unsigned and the
     variable arg is signed, since in this case the variable
     would be sign-extended and then regarded as unsigned.
     Our technique fails in this case because the lowest/highest
     possible unsigned results don't follow naturally from the
     lowest/highest possible values of the variable operand.
     For just EQ_EXPR and NE_EXPR there is another technique that
     could be used: see if the constant can be faithfully represented
     in the other operand's type, by truncating it and reextending it
     and see if that preserves the constant's value.  */

  if (!real1 && !real2
      && TREE_CODE (TREE_TYPE (primop0)) != FIXED_POINT_TYPE
      && TREE_CODE (primop1) == INTEGER_CST
      && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr))
    {

Eventually, after the function returns the inequality expression without a warning, c_parser_condition() calls c_fully_fold() on it which folds it into a constant, without a warning.
Comment 4 Kewen Lin 2021-01-05 03:49:04 UTC
(In reply to Martin Sebor from comment #3)

> Eventually, after the function returns the inequality expression without a
> warning, c_parser_condition() calls c_fully_fold() on it which folds it into
> a constant, without a warning.

Happened to notice this PR when trying to answer the question [1].  The part doing the folding is from match.pd.

/* Non-equality compare simplifications from fold_binary  */
(for cmp (lt gt le ge)
 /* Comparisons with the highest or lowest possible integer of
    the specified precision will have known values.  */
 (simplify
  (cmp (convert?@2 @0) uniform_integer_cst_p@1)
...
     (if (wi::to_wide (cst) == max)
      (switch
       (if (cmp == GT_EXPR)
        { constant_boolean_node (false, type); })
       (if (cmp == GE_EXPR)
        (eq @2 @1))
       (if (cmp == LE_EXPR)
        { constant_boolean_node (true, type); })

I noticed there are some warning support in match.pd like fold_overflow_warning. Not sure whether we can add the similar supports there.  Probably hard to get LOC?

[1] https://gcc.gnu.org/pipermail/gcc-help/2021-January/139755.html