From 31332e4d2641006341a5a4f8be2e81b69ee430d6 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 15 May 2020 06:20:33 -0400
Subject: [PATCH 134/159] FIXME: handle gassign of STRING_CST

---
 gcc/analyzer/region-model2.cc           | 80 ++++++++++++++++++-------
 gcc/analyzer/store2.cc                  |  4 +-
 gcc/analyzer/store2.h                   |  2 +-
 gcc/testsuite/gcc.dg/analyzer/casts-1.c |  9 +++
 4 files changed, 69 insertions(+), 26 deletions(-)

diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 9b725e626d4..2caf5e421ea 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -2788,6 +2788,32 @@ region_model2_manager::get_or_create_binop (tree type, enum tree_code op,
 
 /* FIXME.  */
 
+static const svalue2 *
+maybe_get_char_from_string_cst (tree string_cst, tree byte_offset_cst,
+				region_model2_manager *mgr)
+{
+  gcc_assert (TREE_CODE (string_cst) == STRING_CST);
+
+  /* Adapted from fold_read_from_constant_string.  */
+  scalar_int_mode char_mode;
+  if (TREE_CODE (byte_offset_cst) == INTEGER_CST
+      && compare_tree_int (byte_offset_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 (byte_offset_cst)]));
+      return mgr->get_or_create_constant_svalue2 (char_cst);
+    }
+  return NULL;
+}
+
+/* FIXME.  */
+
 const svalue2 *
 region_model2_manager::get_or_create_sub_svalue2 (tree type,
 						  const svalue2 *parent_svalue2,
@@ -2803,12 +2829,23 @@ region_model2_manager::get_or_create_sub_svalue2 (tree type,
 	    {
 	      const svalue2 *cst_sval
 		= get_or_create_constant_svalue2 (cst);
-	      const svalue2 *cast_sval
-		= get_or_create_unaryop (type, NOP_EXPR, cst_sval);
-	      return cast_sval;
+	      return get_or_create_cast (type, cst_sval);
 	    }
     }
 
+  /* Handle getting individual chars from a STRING_CST.  */
+  if (tree cst = parent_svalue2->maybe_get_constant ())
+    if (TREE_CODE (cst) == STRING_CST)
+      if (const element_region2 *element_reg
+	    = subregion->dyn_cast_element_region2 ())
+	{
+	  const svalue2 *idx_sval = element_reg->get_index ();
+	  if (tree cst_idx = idx_sval->maybe_get_constant ())
+	    if (const svalue2 *char_sval
+		= maybe_get_char_from_string_cst (cst, cst_idx, this))
+	      return get_or_create_cast (type, char_sval);
+	}
+
   sub_svalue2::key_t key (type, parent_svalue2, subregion);
   if (sub_svalue2 **slot = m_sub_values_map.get (key))
     return *slot;
@@ -3779,6 +3816,17 @@ region_model2::on_gassign (const supernode */*snode*/,
       }
       break;
 
+    case STRING_CST:
+      {
+	/* e.g. "struct s2 x = {{'A', 'B', 'C', 'D'}};".  */
+	/* Add a default binding, rather than a direct one, so that array
+	   access will "inherit" the individual chars.  */
+	const svalue2 *rhs_sval = get_rvalue (rhs1, ctxt);
+	m_store.set_value (m_mgr->get_store2_manager(), lhs_reg, rhs_sval,
+			   BK_default);
+      }
+      break;
+
     /* Assignments of the form
 	set_value (lvalue (LHS), rvalue (EXPR))
        for various EXPR.
@@ -4840,8 +4888,6 @@ region_model2::get_lvalue_1 (path_var pv,
 	return m_mgr->get_offset_region (star_ptr,
 					 TREE_TYPE (expr),
 					 offset_sval, ctxt);
-	/* TODO: a selftest of casting a buffer to both an (int *)
-	   and a (coord *), and doing array accesses with both arrays.  */
       }
       break;
 
@@ -5114,23 +5160,10 @@ region_model2::get_store_value (const region2 *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 (byte_offset_cst) == INTEGER_CST
-	      && compare_tree_int (byte_offset_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 (byte_offset_cst)]));
-	      return m_mgr->get_or_create_constant_svalue2 (char_cst);
-	    }
+	  if (const svalue2 *char_sval
+		= maybe_get_char_from_string_cst (string_cst, byte_offset_cst,
+						  m_mgr))
+	    return char_sval;
 	}
 
   /* FIXME: symbolic value. */
@@ -5446,7 +5479,8 @@ region_model2::set_value (const region2 *lhs_reg, const svalue2 *rhs_sval,
   gcc_assert (lhs_reg);
   gcc_assert (rhs_sval);
 
-  m_store.set_value (m_mgr->get_store2_manager(), lhs_reg, rhs_sval);
+  m_store.set_value (m_mgr->get_store2_manager(), lhs_reg, rhs_sval,
+		     BK_direct);
 }
 
 /* Set the value of the region2 given by LHS to the value given
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index b6db23ffd6c..812ba8df038 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -833,13 +833,13 @@ store2::get_any_binding (store2_manager *mgr, const region2 *reg)
 
 void
 store2::set_value (store2_manager *mgr, const region2 *lhs_reg,
-		   const svalue2 *rhs_sval)
+		   const svalue2 *rhs_sval, enum binding_kind kind)
 {
   remove_overlapping_bindings (mgr, lhs_reg);
 
   const region2 *base_reg = lhs_reg->get_base_region ();
   binding_cluster2 *cluster = get_or_create_cluster (base_reg);
-  cluster->bind (mgr, lhs_reg, rhs_sval, BK_direct);
+  cluster->bind (mgr, lhs_reg, rhs_sval, kind);
   // TODO: various kinds of invalidation etc
 }
 
diff --git a/gcc/analyzer/store2.h b/gcc/analyzer/store2.h
index 952b274acc0..3ab0f80a0c6 100644
--- a/gcc/analyzer/store2.h
+++ b/gcc/analyzer/store2.h
@@ -292,7 +292,7 @@ public:
   const svalue2 *get_any_binding (store2_manager *mgr, const region2 *reg);
 
   void set_value (store2_manager *mgr, const region2 *lhs_reg,
-		  const svalue2 *rhs_sval);
+		  const svalue2 *rhs_sval, enum binding_kind kind);
   void clobber_region (store2_manager *mgr, const region2 *reg);
   void purge_region (store2_manager *mgr, const region2 *reg);
   void zero_fill_region (store2_manager *mgr, const region2 *reg);
diff --git a/gcc/testsuite/gcc.dg/analyzer/casts-1.c b/gcc/testsuite/gcc.dg/analyzer/casts-1.c
index 7c9fb56451c..39a0bb1fd06 100644
--- a/gcc/testsuite/gcc.dg/analyzer/casts-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/casts-1.c
@@ -29,3 +29,12 @@ void test_1 ()
   __analyzer_eval (((struct s2 *)&x)->arr[1] == '#'); /* { dg-warning "TRUE" } */
   __analyzer_eval (x.b == '#'); /* { dg-warning "TRUE" } */
 }
+
+void test_2 ()
+{
+  struct s2 x = {{'A', 'B', 'C', 'D'}};
+  __analyzer_eval (x.arr[0] == 'A'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (x.arr[1] == 'B'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (x.arr[2] == 'C'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (x.arr[3] == 'D'); /* { dg-warning "TRUE" } */
+}
-- 
2.21.0

