From 34fa5885599c00e2867024658c180920c3bbd3b0 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 29 Mar 2019 16:43:19 -0400
Subject: [PATCH 168/217] FIXME: handle comparisons against constants

---
 gcc/analyzer/constraints.cc                    | 89 ++++++++++++++++++++++----
 gcc/testsuite/gcc.dg/analyzer/conditionals.c   | 36 ++++-------
 gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c |  5 +-
 gcc/testsuite/gcc.dg/analyzer/switch.c         |  6 +-
 4 files changed, 90 insertions(+), 46 deletions(-)

diff --git a/gcc/analyzer/constraints.cc b/gcc/analyzer/constraints.cc
index 2bb07a7..a2c7ca3 100644
--- a/gcc/analyzer/constraints.cc
+++ b/gcc/analyzer/constraints.cc
@@ -733,7 +733,40 @@ path_state::get_or_add_equiv_class (path_var pv)
   equiv_class *new_ec = new equiv_class ();
   new_ec->add (pv);
   m_equiv_classes.safe_push (new_ec);
-  return equiv_class_id (m_equiv_classes.length () - 1);
+
+  equiv_class_id new_id (m_equiv_classes.length () - 1);
+
+  if (CONSTANT_CLASS_P (pv.m_tree))
+    {
+      /* If we have a new EC for a constant, add constraints comparing this
+	 to other constants we may have (so that we accumulate the transitive
+	 closure of all constraints on constants as the constants are
+	 added).  */
+      for (equiv_class_id other_id (0); other_id.m_idx < new_id.m_idx;
+	   other_id.m_idx++)
+	{
+	  const equiv_class &other_ec = other_id.get_obj (*this);
+	  if (other_ec.m_constant)
+	    {
+	      /* If we have two ECs, both with constants, the constants must be
+		 non-equal (or they would be in the same EC).
+		 Determine the direction of the inequality, and record that
+		 fact.
+		 These are "<" rather than "<=", and so can never lead to
+		 the merger of ECs.  */
+	      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);
+	      if (lt == boolean_true_node)
+		add_constraint_internal (new_id, CONSTRAINT_LT, other_id);
+	      else
+		add_constraint_internal (other_id, CONSTRAINT_LT, new_id);
+	    }
+	}
+    }
+
+  return new_id;
 }
 
 /* FIXME.  */
@@ -1145,7 +1178,7 @@ test_constraint_conditions ()
 /* FIXME.  */
 
 static void
-test_constraint_conditions_2 ()
+test_transitivity ()
 {
   path_var int_42 (build_int_cst (integer_type_node, 42), 0);
   path_var int_0 (build_int_cst (integer_type_node, 0), 0);
@@ -1302,28 +1335,53 @@ test_constraint_conditions_2 ()
 /* FIXME.  */
 
 static void
-test_constraint_conditions_3 ()
+test_constant_comparisons ()
 {
+  path_var int_3 (build_int_cst (integer_type_node, 3), 0);
+  path_var int_4 (build_int_cst (integer_type_node, 4), 0);
+  path_var int_5 (build_int_cst (integer_type_node, 5), 0);
+
   path_var int_1023 (build_int_cst (integer_type_node, 1023), 0);
   path_var int_1024 (build_int_cst (integer_type_node, 1024), 0);
 
   path_var a (build_decl (UNKNOWN_LOCATION, PARM_DECL,
 			  get_identifier ("a"),
 			  integer_type_node), 0);
+  path_var b (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("b"),
+			  integer_type_node), 0);
 
-  /* a >= 1024, then a <= 1023.  */
+  /* Given a >= 1024, then a <= 1023 should be impossible.  */
   {
     path_state ps;
     ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_1024);
+    ADD_UNSAT_CONSTRAINT (ps, a, LE_EXPR, int_1023);
+  }
 
-#if 0
-    range r = ps.get_range (ps.get_or_add_equiv_class (a));
-    ASSERT_EQ (r.m_lower_bound.m_constant, int_1024.m_tree);
-    ASSERT_TRUE (r.m_lower_bound.m_open);
-    ASSERT_EQ (r.m_upper_bound.m_constant, NULL_TREE);
-#endif
+  /* a > 4.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_TRUE (ps, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_TRUE (ps, a, NE_EXPR, int_3);
+    ASSERT_CONDITION_UNKNOWN (ps, a, NE_EXPR, int_5);
+  }
 
-    //ADD_UNSAT_CONSTRAINT (ps, a, LE_EXPR, int_1023);
+  /* a <= 4.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
+    ASSERT_CONDITION_FALSE (ps, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_FALSE (ps, a, GT_EXPR, int_5);
+    ASSERT_CONDITION_UNKNOWN (ps, a, NE_EXPR, int_3);
+  }
+
+  /* If "a > b" and "a == 3", then "b == 4" ought to be unsatisfiable.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, a, EQ_EXPR, int_3);
+    ADD_UNSAT_CONSTRAINT (ps, b, EQ_EXPR, int_4);
   }
 }
 
@@ -1404,8 +1462,11 @@ test_constraint_impl ()
     ADD_SAT_CONSTRAINT (ps, x, NE_EXPR, int_42);
 
     /* Assert various things about the insides of ps.  */
-    ASSERT_EQ (ps.m_constraints.length (), 0);
+    ASSERT_EQ (ps.m_constraints.length (), 1);
     ASSERT_EQ (ps.m_equiv_classes.length (), 2);
+    ASSERT_EQ (ps.m_constraints[0].m_lhs, ps.get_or_add_equiv_class (int_0));
+    ASSERT_EQ (ps.m_constraints[0].m_rhs, ps.get_or_add_equiv_class (int_42));
+    ASSERT_EQ (ps.m_constraints[0].m_op, CONSTRAINT_LT);
   }
 
   // TODO: selftest for merging ecs "in the middle"
@@ -1459,8 +1520,8 @@ void
 analyzer_constraints_cc_tests ()
 {
   test_constraint_conditions ();
-  test_constraint_conditions_2 ();
-  test_constraint_conditions_3 ();
+  test_transitivity ();
+  test_constant_comparisons ();
   test_constraint_impl ();
   test_assignment ();
   test_stack_frames ();
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals.c b/gcc/testsuite/gcc.dg/analyzer/conditionals.c
index ffaf9bc..8cf5b18 100644
--- a/gcc/testsuite/gcc.dg/analyzer/conditionals.c
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals.c
@@ -4,15 +4,12 @@ void test (int i)
     {
       __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
       __analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */
-      __analyzer_eval (i > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+      __analyzer_eval (i > 3); /* { dg-warning "TRUE" } */
 
       __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */
-      __analyzer_eval (i != 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+      __analyzer_eval (i != 3); /* { dg-warning "TRUE" } */
 
-      __analyzer_eval (i == 3); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+      __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */
 
       __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */
       __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */
@@ -25,27 +22,18 @@ void test (int i)
     {
       __analyzer_eval (i > 4); /* { dg-warning "FALSE" } */
       __analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */
-      __analyzer_eval (i > 3); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+      __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */
 
-      __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */
-      __analyzer_eval (i != 3); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
-
-      __analyzer_eval (i == 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+      __analyzer_eval (i > 5); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */
 
-      __analyzer_eval (i != 4); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+      __analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */
 
-      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
-
-      __analyzer_eval (i == 5); /* { dg-warning "UNKNOWN" } */
-      __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i != 5); /* { dg-warning "TRUE" } */
       __analyzer_eval (i < 5); /* { dg-warning "TRUE" } */
-      __analyzer_eval (i <= 5); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */
     }
 }
-
-/* TODO: The various xfails here involve implications of conditionals.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
index 14a0aad..bdc5bb0 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
@@ -48,6 +48,5 @@ void test_3 (size_t sz)
   if (sz >= LIMIT)
     free (ptr); /* { dg-warning "free of 'ptr' which points to memory not on the heap" } */
 }
-/* { dg-bogus "leak of 'ptr'" "" { xfail *-*-* } .-1 } */
-/* TODO(xfail): it's showing a false leak report, due to constraints not
-   ruling out an infeasible path ("sz > 1024" and "sz <= 1023").  */
+/* { dg-bogus "leak of 'ptr'" } */
+/* This can't happen, as "sz > 1024" && "sz <= 1023" is impossible.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/switch.c b/gcc/testsuite/gcc.dg/analyzer/switch.c
index 06d1398..a55ba2b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/switch.c
+++ b/gcc/testsuite/gcc.dg/analyzer/switch.c
@@ -7,11 +7,7 @@ void test (int i)
       break;
 
     case 3 ... 5:
-      __analyzer_eval (i >= 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
-      /* TODO(xfail^^^): The above comes in as "i_6(D) > 2" and hence
-	 doesn't match the constraint i_6 >= 3 we already have
-	 (actually 3 <= i_6).  */
+      __analyzer_eval (i >= 3); /* { dg-warning "TRUE" } */
       __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */
       break;
 
-- 
1.8.5.3

