From 0512e75e66a9e9999b9d79918eb40dd55d4d67a5 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 1 Apr 2019 16:42:25 -0400
Subject: [PATCH 172/217] FIXME: partial implementation of integral ranges with
 just one possible element

---
 gcc/analyzer/constraints.cc                  | 86 +++++++++++++++++++++++++++-
 gcc/testsuite/gcc.dg/analyzer/conditionals.c | 62 ++++++++++++++++++++
 2 files changed, 146 insertions(+), 2 deletions(-)

diff --git a/gcc/analyzer/constraints.cc b/gcc/analyzer/constraints.cc
index 6cef640..5017a03 100644
--- a/gcc/analyzer/constraints.cc
+++ b/gcc/analyzer/constraints.cc
@@ -606,6 +606,7 @@ path_state::add_constraint_internal (equiv_class_id lhs_id,
 				     enum constraint_op c_op,
 				     equiv_class_id rhs_id)
 {
+  /* Add the constraint.  */
   m_constraints.safe_push (constraint (lhs_id, c_op, rhs_id));
 
   if (c_op != CONSTRAINT_NE)
@@ -680,6 +681,29 @@ path_state::add_constraint_internal (equiv_class_id lhs_id,
 		}
 	    }
 	}
+
+      /* Tighten integral comparisons.  */
+      if (c_op == CONSTRAINT_LT
+	  && INTEGRAL_TYPE_P (TREE_TYPE (lhs.m_tree))
+	  && INTEGRAL_TYPE_P (TREE_TYPE (rhs.m_tree)))
+	{
+	  lhs_id = get_or_add_equiv_class (lhs);
+	  rhs_id = get_or_add_equiv_class (rhs);
+	  tree lhs_const = lhs_id.get_obj (*this).m_constant;
+	  tree rhs_const = rhs_id.get_obj (*this).m_constant;
+	  if (lhs_const && !rhs_const)
+	    {
+	      /* If we have "C < N" and N are of of integral type, then
+		 if we have "N <= C + 1", then we have "N == C + 1".  */
+	      tree lhs_plus_one = fold_build2 (PLUS_EXPR, TREE_TYPE (rhs.m_tree),
+					       lhs_const, integer_one_node);
+	      gcc_assert (CONSTANT_CLASS_P (lhs_plus_one));
+	      equiv_class_id ec_next_const (-1);
+	      if (get_equiv_class (path_var (lhs_plus_one, 0), &ec_next_const))
+		if (eval_condition (rhs_id, LE_EXPR, ec_next_const).is_true ())
+		  add_constraint (rhs_id, EQ_EXPR, ec_next_const);
+	    }
+	}
     }
 }
 
@@ -761,10 +785,11 @@ path_state::get_or_add_equiv_class (path_var pv)
 	      tree lt
 		= fold_build2 (LT_EXPR, boolean_type_node,
 			       new_ec->m_constant, other_ec.m_constant);
-	      gcc_assert (lt == boolean_true_node || lt == boolean_false_node);
+	      //gcc_assert (lt == boolean_true_node || lt == boolean_false_node);
+	      // not true for int vs float comparisons
 	      if (lt == boolean_true_node)
 		add_constraint_internal (new_id, CONSTRAINT_LT, other_id);
-	      else
+	      else if (lt == boolean_false_node)
 		add_constraint_internal (other_id, CONSTRAINT_LT, new_id);
 	    }
 	}
@@ -1387,6 +1412,63 @@ test_constant_comparisons ()
     ADD_SAT_CONSTRAINT (ps, a, EQ_EXPR, int_3);
     ADD_UNSAT_CONSTRAINT (ps, b, EQ_EXPR, int_4);
   }
+
+  /* Various tests of int ranges where there is only one possible candidate.  */
+  {
+    /* If "a <= 4" && "a > 3", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
+      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+
+    /* If "a > 3" && "a <= 4", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
+      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
+      // TODO:
+      //ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+    /* If "a > 3" && "a < 5", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
+      ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, int_5);
+      // TODO:
+      //ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+    /* If "a >= 4" && "a < 5", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, int_5);
+      // TODO:
+      //ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+    /* If "a >= 4" && "a <= 4", then "a == 4".  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
+      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+  }
+
+#if 0
+  /* As above, but for float.   */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, f, GT_EXPR, float_3);
+    ADD_SAT_CONSTRAINT (ps, f, LE_EXPR, float_4);
+    ASSERT_CONDITION_UNKNOWN (ps, f, EQ_EXPR, int_4);
+  }
+#endif
 }
 
 /* FIXME.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals.c b/gcc/testsuite/gcc.dg/analyzer/conditionals.c
index bb99ef1..99f141f 100644
--- a/gcc/testsuite/gcc.dg/analyzer/conditionals.c
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals.c
@@ -64,3 +64,65 @@ void test_2 (int i, int j, int k)
 	}
     }
 }
+
+void test_range_int_gt_lt (int i)
+{
+  if (i > 3)
+    if (i < 5)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
+
+void test_range_float_gt_lt (float f)
+{
+  if (f > 3)
+    if (f < 5)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_lt (int i)
+{
+  if (i >= 4)
+    if (i < 5)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
+
+void test_range_float_ge_lt (float f)
+{
+  if (f >= 4)
+    if (f < 5)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_gt_le (int i)
+{
+  if (i > 3)
+    if (i <= 4)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
+
+void test_range_float_gt_le (float f)
+{
+  if (f > 3)
+    if (f <= 4)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+      /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+}
+
+void test_range_int_ge_le (int i)
+{
+  if (i >= 4)
+    if (i <= 4)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
+
+void test_range_float_ge_le (float f)
+{
+  if (f >= 4)
+    if (f <= 4)
+      __analyzer_eval (f == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+}
-- 
1.8.5.3

