From 573abc0a5082b56ac50d72b6af3fa53cc7d0bca7 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 23 Apr 2020 10:16:41 -0400
Subject: [PATCH 073/237] FIXME: get str[0] working

---
 gcc/analyzer/analyzer.h       |  1 +
 gcc/analyzer/region-model2.cc | 87 ++++++++++++++++++++++++++---------
 gcc/analyzer/region-model2.h  | 38 ++++++++-------
 3 files changed, 88 insertions(+), 38 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index bc4bdbd355f..3f816f536c3 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -65,6 +65,7 @@ class region2;
   class map_region2;
   class function_region2;
   class symbolic_region2;
+  class element_region2;
   class field_region2;
   class string_region2;
 class region_model2_manager;
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index be3c5149586..a3b7bada0f6 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -1607,24 +1607,24 @@ region2::cmp_ptrs (const void *p1, const void *p2)
    - the possibility that the pointer could be NULL
    - the idea that successive values returned from malloc are non-equal
    - to be able to zero-fill for calloc.  */
-#if 0
+
 bool
 region2::non_null_p (const region_model2 &model) const
 {
-  /* Look through views to get at the underlying region2.  */
-  if (is_view_p ())
-    return model.get_region2 (m_parent_reg)->non_null_p (model);
-
+#if 0
+  /* FIXME: malloc is now simulated via a heap_allocated_region2.
+     That said, maybe we do want the possibilty of NULL for symbolic
+     regions too.  */
   /* Are we within a symbolic_region2?  If so, it could be NULL.  */
   if (const symbolic_region2 *sym_reg = dyn_cast_symbolic_region2 ())
     {
       if (sym_reg->m_possibly_null)
 	return false;
     }
+#endif
 
   return true;
 }
-#endif
 
 /* class map_region2 : public region2.  */
 
@@ -3832,21 +3832,13 @@ region_model2::on_gassign (const supernode */*snode*/,
       break;
 #endif
 
-    case MEM_REF:
-      {
-	svalue2 *ptr_sval = get_rvalue (rhs1, ctxt);
-	region2 *star_ptr_reg = deref_rvalue (ptr_sval, ctxt);
-	svalue2 *val = get_store_value (star_ptr_reg);
-	set_value (lhs_reg, val, ctxt);
-      }
-      break;
-
     /* Assignments of the form
 	set_value (lvalue (LHS), rvalue (EXPR))
        for various EXPR.
        We already have the lvalue for the LHS above, as "lhs_reg".  */
     case ADDR_EXPR: /* LHS = &RHS;  */
     case COMPONENT_REF: /* LHS = op0.op1;  */
+    case MEM_REF:
     case REAL_CST:
     case INTEGER_CST:
     case ARRAY_REF:
@@ -4900,6 +4892,32 @@ region_model2::get_store_value (region2 *reg)
   if (sval)
     return sval;
 
+  /* Special-case: read at a constant index within a STRING_CST.  */
+  if (element_region2 *element_reg = reg->dyn_cast_element_region2 ())
+    if (tree index_cst = element_reg->get_index ()->maybe_get_constant ())
+      if (string_region2 *str_reg
+	  = reg->get_parent_region ()->dyn_cast_string_region2 ())
+	{
+	  tree string_cst = str_reg->get_string_cst ();
+	  gcc_assert (TREE_CODE (string_cst) == STRING_CST);
+
+	  /* Adapted from fold_read_from_constant_string.  */
+	  scalar_int_mode char_mode;
+	  if (TREE_CODE (index_cst) == INTEGER_CST
+	      && compare_tree_int (index_cst,
+				   TREE_STRING_LENGTH (string_cst)) < 0
+	      && is_int_mode (TYPE_MODE (TREE_TYPE (TREE_TYPE (string_cst))),
+			      &char_mode)
+	      && GET_MODE_SIZE (char_mode) == 1)
+	    {
+	      tree char_cst
+		= build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)),
+				      (TREE_STRING_POINTER (string_cst)
+				       [TREE_INT_CST_LOW (index_cst)]));
+	      return m_mgr->get_or_create_constant_svalue2 (char_cst);
+	    }
+	}
+
   /* FIXME: symbolic value. */
   return m_mgr->get_or_create_initial_value (reg);
 }
@@ -5342,7 +5360,7 @@ region_model2::eval_condition_without_cm (svalue2 *lhs,
 	  return constant_svalue2::eval_condition (cst_lhs, op, cst_rhs);
 
       /* Handle comparison of a region_svalue2 against zero.  */
-#if 0
+
       if (region_svalue2 *ptr = lhs->dyn_cast_region_svalue2 ())
 	if (constant_svalue2 *cst_rhs = rhs->dyn_cast_constant_svalue2 ())
 	  if (zerop (cst_rhs->get_constant ()))
@@ -5369,7 +5387,6 @@ region_model2::eval_condition_without_cm (svalue2 *lhs,
 		    }
 		}
 	    }
-#endif
     }
 
   return tristate::TS_UNKNOWN;
@@ -7088,13 +7105,25 @@ test_tree_cmp_on_constants ()
 	}
 }
 
-/* Implementation detail of the ASSERT_CONDITION_* macros.  */
+/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
+
+void
+assert_condition2 (const location &loc,
+		   region_model2 &model,
+		   svalue2 *lhs, tree_code op, svalue2 *rhs,
+		   tristate expected)
+{
+  tristate actual = model.eval_condition (lhs, op, rhs);
+  ASSERT_EQ_AT (loc, actual, expected);
+}
+
+/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
 
 void
-assert_condition (const location &loc,
-		  region_model2 &model,
-		  tree lhs, tree_code op, tree rhs,
-		  tristate expected)
+assert_condition2 (const location &loc,
+		   region_model2 &model,
+		   tree lhs, tree_code op, tree rhs,
+		   tristate expected)
 {
   tristate actual = model.eval_condition (lhs, op, rhs, NULL);
   ASSERT_EQ_AT (loc, actual, expected);
@@ -7362,7 +7391,21 @@ test_unique_constants ()
 	     model.get_rvalue (int_42, &ctxt));
   ASSERT_NE (model.get_rvalue (int_0, &ctxt), model.get_rvalue (int_42, &ctxt));
   ASSERT_EQ (ctxt.get_num_diagnostics (), 0);
+
+  /* A "(const int)42" will be a different tree from "(int)42)"...  */
+  tree const_int_type_node
+    = build_qualified_type (integer_type_node, TYPE_QUAL_CONST);
+  tree const_int_42 = build_int_cst (const_int_type_node, 42);
+  ASSERT_NE (int_42, const_int_42);
+  /* It should have a different const_svalue2.  */
+  svalue2 *int_42_sval = model.get_rvalue (int_42, &ctxt);
+  svalue2 *const_int_42_sval = model.get_rvalue (const_int_42, &ctxt);
+  ASSERT_NE (int_42_sval, const_int_42_sval);
+  /* But they should compare as equal.  */
+  ASSERT_CONDITION_TRUE2 (model, int_42_sval, EQ_EXPR, const_int_42_sval);
+  ASSERT_CONDITION_FALSE2 (model, int_42_sval, NE_EXPR, const_int_42_sval);
 }
+
 #if 0
 /* Check that operator== and hashing works as expected for the
    various svalue2 subclasses.  */
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 18c47397821..23d52ae63b1 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -812,6 +812,7 @@ public:
   virtual function_region2 *dyn_cast_function_region2 () { return NULL; }
   virtual symbolic_region2 *dyn_cast_symbolic_region2 () { return NULL; }
   virtual const symbolic_region2 *dyn_cast_symbolic_region2 () const { return NULL; }
+  virtual element_region2 *dyn_cast_element_region2 () { return NULL; }
   virtual string_region2 *dyn_cast_string_region2 () { return NULL; }
 
   region2 *get_parent_region () const { return m_parent; }
@@ -1454,6 +1455,7 @@ public:
   bool compare_fields (const element_region2 &other) const;
 
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ELEMENT; }
+  element_region2 *dyn_cast_element_region2 () FINAL OVERRIDE { return this; }
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 
@@ -2170,40 +2172,44 @@ private:
     ASSERT_FALSE (sat);				\
   SELFTEST_END_STMT
 
-/* Implementation detail of the ASSERT_CONDITION_* macros.  */
+/* Implementation detail of the ASSERT_CONDITION_*2 macros.  */
 
-void assert_condition (const location &loc,
-		       region_model2 &model,
-		       tree lhs, tree_code op, tree rhs,
-		       tristate expected);
+void assert_condition2 (const location &loc,
+			region_model2 &model,
+			svalue2 *lhs, tree_code op, svalue2 *rhs,
+			tristate expected);
+
+void assert_condition2 (const location &loc,
+			region_model2 &model,
+			tree lhs, tree_code op, tree rhs,
+			tristate expected);
 
 /* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
    as "true".  */
-#if 0
-#define ASSERT_CONDITION_TRUE(REGION_MODEL2, LHS, OP, RHS) \
+
+#define ASSERT_CONDITION_TRUE2(REGION_MODEL2, LHS, OP, RHS)		\
   SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
-		    tristate (tristate::TS_TRUE));		\
+  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
+		     tristate (tristate::TS_TRUE));			\
   SELFTEST_END_STMT
 
 /* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
    as "false".  */
 
-#define ASSERT_CONDITION_FALSE(REGION_MODEL2, LHS, OP, RHS) \
+#define ASSERT_CONDITION_FALSE2(REGION_MODEL2, LHS, OP, RHS) \
   SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
-		    tristate (tristate::TS_FALSE));		\
+  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
+		     tristate (tristate::TS_FALSE));			\
   SELFTEST_END_STMT
 
 /* Assert that REGION_MODEL2 evaluates the condition "LHS OP RHS"
    as "unknown".  */
 
-#define ASSERT_CONDITION_UNKNOWN(REGION_MODEL2, LHS, OP, RHS) \
+#define ASSERT_CONDITION_UNKNOWN2(REGION_MODEL2, LHS, OP, RHS) \
   SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
-		    tristate (tristate::TS_UNKNOWN));		\
+  assert_condition2 (SELFTEST_LOCATION, REGION_MODEL2, LHS, OP, RHS,	\
+		     tristate (tristate::TS_UNKNOWN));			\
   SELFTEST_END_STMT
-#endif
 
 } /* end of namespace selftest.  */
 
-- 
2.26.2

