[Andrew, this doesn't fix a PR per se.  It's just something I stumbled
upon in my other -fstrict-enums fixes.  It passes tests.  What do you
think?  Should we push it?]

Imagine an enum in an -fstrict-enums world:

        enum foo { zero, one, two, three };

Building the inverse of [0,3] currently yields VARYING (instead of
[4,+INF]).  This is because the setter sees 0 and 3 as the extremes of
the type, and per the current code, calculates the inverse to VARYING.
BTW, it really should be UNDEFINED, but this is legacy code I'm afraid
to touch:

      if (is_min && is_max)
        {
          /* We cannot deal with empty ranges, drop to varying.
             ???  This could be VR_UNDEFINED instead.  */
          set_varying (type);
          return;
        }

However, the extremes of a VARYING are the extremes of the underlying
type ([0, 0xff..ff]), even in the presence of -fstrict-enums.

This fixes the setter to use the extremes of the underlying type.

gcc/ChangeLog:

        * value-range.cc (irange::set): Use wi::{min,max}_value
        instead of TYPE_{MIN,MAX}_VALUE when building anti-ranges.
        (range_tests_strict_enum): New tests.
---
 gcc/value-range.cc | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index f83a824a982..bbd7d69a766 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -274,11 +274,13 @@ irange::set (tree min, tree max, value_range_kind kind)
   // Anti-ranges that can be represented as ranges should be so.
   if (kind == VR_ANTI_RANGE)
     {
-      /* For -fstrict-enums we may receive out-of-range ranges so consider
-         values < -INF and values > INF as -INF/INF as well.  */
-      bool is_min = vrp_val_is_min (min);
-      bool is_max = vrp_val_is_max (max);
       tree type = TREE_TYPE (min);
+      unsigned prec = TYPE_PRECISION (type);
+      signop sign = TYPE_SIGN (type);
+      wide_int type_max = wi::max_value (prec, sign);
+      wide_int type_min = wi::min_value (prec, sign);
+      bool is_min = wi::to_wide (min) == type_min;
+      bool is_max = wi::to_wide (max) == type_max;
 
       if (is_min && is_max)
        {
@@ -302,14 +304,14 @@ irange::set (tree min, tree max, value_range_kind kind)
         {
          tree one = build_int_cst (TREE_TYPE (max), 1);
          min = int_const_binop (PLUS_EXPR, max, one);
-         max = vrp_val_max (TREE_TYPE (max));
+         max = wide_int_to_tree (type, type_max);
          kind = VR_RANGE;
         }
       else if (is_max)
         {
          tree one = build_int_cst (TREE_TYPE (min), 1);
          max = int_const_binop (MINUS_EXPR, min, one);
-         min = vrp_val_min (TREE_TYPE (min));
+         min = wide_int_to_tree (type, type_min);
          kind = VR_RANGE;
         }
     }
@@ -2276,6 +2278,19 @@ range_tests_strict_enum ()
   ir1 = vr1;
   ASSERT_TRUE (ir1 == vr1);
   ASSERT_FALSE (ir1.varying_p ());
+
+  // Legacy inversion of [0, 3] should be [4, MAX].
+  vr1 = int_range<1> (build_int_cstu (rtype, 0), build_int_cstu (rtype, 3));
+  vr1.invert ();
+  tree type_max = wide_int_to_tree (rtype,
+                                   wi::max_value (TYPE_PRECISION (rtype),
+                                                  TYPE_SIGN (rtype)));
+  ASSERT_TRUE (vr1 == int_range<1> (build_int_cstu (rtype, 4), type_max));
+
+  // Multi-range inversion of [0, 3] should be [4, MAX].
+  ir1 = int_range<2> (build_int_cstu (rtype, 0), build_int_cstu (rtype, 3));
+  ir1.invert ();
+  ASSERT_TRUE (vr1 == int_range<2> (build_int_cstu (rtype, 4), type_max));
 }
 
 static void
-- 
2.26.2

Reply via email to