From 33596e1d7fe829442ddbe6639c4ae0402036214b Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Sat, 26 Oct 2019 07:32:08 -0400
Subject: [PATCH 080/119] FIXME: WIP on revamp of region_model, with stronger
 type-checking and views

---
 gcc/analyzer/engine.cc       |   6 +-
 gcc/analyzer/region-model.cc | 595 +++++++++++++++++++++++++++++++++++++------
 gcc/analyzer/region-model.h  | 369 ++++++++++++++++++++++++---
 3 files changed, 855 insertions(+), 115 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 560cb1c..e38c672 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -898,9 +898,9 @@ exploded_node::on_stmt (exploded_graph &eg,
 	  tree t_arg = gimple_call_arg (call, 0);
 	  tristate t
 	    = state->m_region_model->eval_condition (t_arg,
-							NE_EXPR,
-							integer_zero_node,
-							&ctxt);
+						     NE_EXPR,
+						     integer_zero_node,
+						     &ctxt);
 	  warning_at (call->location, 0, "%s", t.as_string ());
 	}
       else if (is_named_call_p (call, "__analyzer_break", 0))
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 622e101..9a926dd 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -779,6 +779,7 @@ constant_svalue::get_child_sid (region *parent, region *child,
 				region_model &model,
 				region_model_context *ctxt)
 {
+#if 0
   /* FIXME: if it's a STRING_CST.  */
   if (TREE_CODE (m_cst_expr) == STRING_CST)
     {
@@ -802,6 +803,7 @@ constant_svalue::get_child_sid (region *parent, region *child,
 	    }
 	}
     }
+#endif
 
   /* FIXME: handle the all-zeroes case by returning an all-zeroes of the
      child type.
@@ -919,18 +921,26 @@ region_kind_to_str (enum region_kind kind)
     {
     default:
       gcc_unreachable ();
-    case RK_MAP:
-      return "map";
+    case RK_PRIMITIVE:
+      return "primitive";
+    case RK_STRUCT:
+      return "struct";
+    case RK_UNION:
+      return "union";
+    case RK_ARRAY:
+      return "array";
     case RK_FRAME:
       return "frame";
+    case RK_GLOBALS:
+      return "globals";
     case RK_STACK:
       return "stack";
+    case RK_HEAP:
+      return "heap";
     case RK_ROOT:
       return "root";
     case RK_SYMBOLIC:
       return "symbolic";
-    case RK_HEAP:
-      return "heap";
     }
 }
 
@@ -960,12 +970,40 @@ region::operator== (const region &other) const
     {
     default:
       gcc_unreachable ();
-    case RK_MAP:
+    case RK_PRIMITIVE:
       {
-	const map_region &this_sub
-	  = (const map_region &)*this;
-	const map_region &other_sub
-	  = (const map_region &)other;
+#if 1
+	return true;
+#else
+	const primitive_region &this_sub
+	  = (const primitive_region &)*this;
+	const primitive_region &other_sub
+	  = (const primitive_region &)other;
+	return this_sub.compare_fields (other_sub);
+#endif
+      }
+    case RK_STRUCT:
+      {
+	const struct_region &this_sub
+	  = (const struct_region &)*this;
+	const struct_region &other_sub
+	  = (const struct_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_UNION:
+      {
+	const union_region &this_sub
+	  = (const union_region &)*this;
+	const union_region &other_sub
+	  = (const union_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_ARRAY:
+      {
+	const array_region &this_sub
+	  = (const array_region &)*this;
+	const array_region &other_sub
+	  = (const array_region &)other;
 	return this_sub.compare_fields (other_sub);
       }
     case RK_FRAME:
@@ -976,6 +1014,14 @@ region::operator== (const region &other) const
 	  = (const frame_region &)other;
 	return this_sub.compare_fields (other_sub);
       }
+    case RK_GLOBALS:
+      {
+	const globals_region &this_sub
+	  = (const globals_region &)*this;
+	const globals_region &other_sub
+	  = (const globals_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
     case RK_STACK:
       {
 	const stack_region &this_sub
@@ -1025,7 +1071,7 @@ region::set_value (region_model &model, svalue_id rhs_sid)
 	rhs_sid = model.maybe_cast (m_type, rhs_sid);
 
       /* FIXME: we can't enable this until maybe_cast works fully.  */
-#if 0
+#if 1
       sval = model.get_svalue (rhs_sid);
       if (sval->get_type ())
 	gcc_assert (m_type == sval->get_type ());
@@ -1341,6 +1387,25 @@ PUSH_IGNORE_WFORMAT
 POP_IGNORE_WFORMAT
 }
 
+/* class primitive_region : public region.  */
+
+/* Implementation of region::clone vfunc for primitive_region.  */
+
+region *
+primitive_region::clone () const
+{
+  return new primitive_region (*this);
+}
+
+/* Implementation of region::walk_for_canonicalization vfunc for
+   primitive_region.  */
+
+void
+primitive_region::walk_for_canonicalization (canonicalization *c) const
+{
+  /* FIXME.  */
+}
+
 /* class map_region : public region.  */
 
 /* map_region's copy ctor.  */
@@ -1376,14 +1441,6 @@ map_region::compare_fields (const map_region &other) const
   return true;
 }
 
-/* Implementation of region::clone vfunc for map_region.  */
-
-region *
-map_region::clone () const
-{
-  return new map_region (*this);
-}
-
 /* Implementation of region::print_fields vfunc for map_region.  */
 
 void
@@ -1465,21 +1522,6 @@ POP_IGNORE_WFORMAT
     }
 }
 
-/* FIXME.  */
-
-bool
-map_region::valid_key_p (tree key) const
-{
-  // TODO: split these, make a vfunc
-  return (TREE_CODE (key) == PARM_DECL
-	  || TREE_CODE (key) == VAR_DECL
-	  || TREE_CODE (key) == SSA_NAME
-	  || TREE_CODE (key) == RESULT_DECL
-	  || TREE_CODE (key) == FIELD_DECL
-	  || TREE_CODE (key) == INTEGER_CST);
-  //return true; // TODO
-}
-
 /* Look for a child region for KEY within this map_region.
    If it doesn't already exist, create a child map_region, using TYPE for
    its type.
@@ -1497,7 +1539,7 @@ map_region::get_or_create (region_model *model,
   region_id *slot = m_map.get (key);
   if (slot)
     return *slot;
-  region_id child_rid = model->add_region (new map_region (this_rid, type));
+  region_id child_rid = model->add_region_for_type (this_rid, type);
   m_map.put (key, child_rid);
   return child_rid;
 }
@@ -1739,6 +1781,47 @@ map_region::can_merge_p (const map_region *map_region_a,
 	{
 	  region_id rid_b = *slot_b;
 
+#if 1
+	  region *child_region_a = merger->get_region_a <region> (rid_a);
+	  region *child_region_b = merger->get_region_b <region> (rid_b);
+
+	  gcc_assert (child_region_a->get_type ()
+		      == child_region_b->get_type ());
+
+	  gcc_assert (child_region_a->get_kind ()
+		      == child_region_b->get_kind ());
+
+	  region_id child_merged_rid
+	    = merged_map_region->get_or_create (merger->m_merged_model,
+						merged_rid,
+						key_a,
+						child_region_a->get_type ());
+
+	  region *child_merged_region
+	    = merger->m_merged_model->get_region (child_merged_rid);
+
+	  /* TODO: consider values.  */
+	  svalue_id child_a_sid = child_region_a->get_value_direct ();
+	  svalue_id child_b_sid = child_region_b->get_value_direct ();
+	  svalue_id child_merged_sid;
+	  if (!merger->can_merge_values_p (child_a_sid, child_b_sid,
+					   &child_merged_sid))
+	    return false;
+	  if (!child_merged_sid.null_p ())
+	    child_merged_region->set_value (*merger->m_merged_model,
+					    child_merged_sid);
+
+#if 0
+	  /* Recurse.  */
+	  if (!can_merge_p (child_region_a,
+			    child_region_b,
+			    child_merged_region,
+			    child_merged_rid,
+			    merger))
+	    return false;
+#endif
+
+#else
 	  /* FIXME: assume that they too are map regions.
 	     We want to recurse.
 	     We also want to do something about values.  */
@@ -1777,6 +1860,7 @@ map_region::can_merge_p (const map_region *map_region_a,
 			    child_merged_rid,
 			    merger))
 	    return false;
+#endif
 	}
       else
 	{
@@ -1841,12 +1925,141 @@ map_region::get_value_by_name (tree identifier,
 	    region *region = model.get_region (rid);
 	    return region->get_value (const_cast<region_model &>(model),
 				      false, NULL);
-
 	  }
     }
   return svalue_id::null ();
 }
 
+/* class struct_or_union_region : public map_region.  */
+
+/* FIXME.  */
+
+bool
+struct_or_union_region::valid_key_p (tree key) const
+{
+  return TREE_CODE (key) == FIELD_DECL;
+}
+
+/* Compare the fields of this struct_or_union_region with OTHER, returning
+   true if they are equal.
+   For use by region::operator==.  */
+
+bool
+struct_or_union_region::compare_fields (const struct_or_union_region &other)
+  const
+{
+  return map_region::compare_fields (other);
+}
+
+/* class struct_region : public struct_or_union_region.  */
+
+/* Implementation of region::clone vfunc for struct_region.  */
+
+region *
+struct_region::clone () const
+{
+  return new struct_region (*this);
+}
+
+/* Compare the fields of this struct_region with OTHER, returning true
+   if they are equal.
+   For use by region::operator==.  */
+
+bool
+struct_region::compare_fields (const struct_region &other) const
+{
+  return struct_or_union_region::compare_fields (other);
+}
+
+/* class union_region : public struct_or_union_region.  */
+
+/* Implementation of region::clone vfunc for union_region.  */
+
+region *
+union_region::clone () const
+{
+  return new union_region (*this);
+}
+
+/* Compare the fields of this union_region with OTHER, returning true
+   if they are equal.
+   For use by region::operator==.  */
+
+bool
+union_region::compare_fields (const union_region &other) const
+{
+  return struct_or_union_region::compare_fields (other);
+}
+
+/* class array_region : public map_region.  */
+
+/* FIXME.  */
+
+array_region::array_region (const array_region &other)
+: map_region (other)
+{
+}
+
+/* FIXME.  */
+
+region_id
+array_region::get_element (region_model *model,
+			   region_id this_rid,
+			   svalue_id index_sid,
+			   region_model_context *ctxt)
+{
+  tree element_type = TREE_TYPE (get_type ());
+  svalue *index_sval = model->get_svalue (index_sid);
+  if (constant_svalue *cst_index_sval = index_sval->dyn_cast_constant_svalue ())
+    {
+      tree cst_index = cst_index_sval->get_constant ();
+      region_id element_rid
+	= get_or_create (model, this_rid, cst_index, element_type);
+      return element_rid;
+   }
+
+  // FIXME: integrate with offset-based add_view:
+  return model->add_view (this_rid, element_type);
+
+#if 0
+
+	// FIXME: if we have a constant, do this:
+	// FIXME; otherwise, make a view
+	// array_region::get_element (svalue_id index_sid)
+	region_id element_rid
+	  = array_reg->get_or_create (model, this_rid, index, element_type);
+	/* FIXME: ^^^ should be indexed by svalue, rather than by tree?
+	   What about unknown indices?  */
+	return element_rid;
+#endif
+}
+
+/* Implementation of region::clone vfunc for array_region.  */
+
+region *
+array_region::clone () const
+{
+  return new array_region (*this);
+}
+
+/* FIXME.  */
+
+bool
+array_region::valid_key_p (tree key) const
+{
+  return TREE_CODE (key) == INTEGER_CST;
+}
+
+/* Compare the fields of this array_region with OTHER, returning true
+   if they are equal.
+   For use by region::operator==.  */
+
+bool
+array_region::compare_fields (const array_region &other) const
+{
+  return map_region::compare_fields (other);
+}
+
 /* class frame_region : public map_region.  */
 
 IMPL_POOL_ALLOCATOR (frame_region)
@@ -1875,6 +2088,18 @@ frame_region::clone () const
   return new frame_region (*this);
 }
 
+/* FIXME.  */
+
+bool
+frame_region::valid_key_p (tree key) const
+{
+  // FIXME: could also check that VAR_DECLs are locals
+  return (TREE_CODE (key) == PARM_DECL
+	  || TREE_CODE (key) == VAR_DECL
+	  || TREE_CODE (key) == SSA_NAME
+	  || TREE_CODE (key) == RESULT_DECL);
+}
+
 /* Implementation of region::print_fields vfunc for frame_region.  */
 
 void
@@ -1896,6 +2121,34 @@ frame_region::add_to_hash (inchash::hash &hstate) const
   hstate.add_int (m_depth);
 }
 
+/* class globals_region : public scope_region.  */
+
+/* Compare the fields of this globals_region with OTHER, returning true
+   if they are equal.
+   For use by region::operator==.  */
+
+bool
+globals_region::compare_fields (const globals_region &other) const
+{
+  return map_region::compare_fields (other);
+}
+
+/* Implementation of region::clone vfunc for globals_region.  */
+
+region *
+globals_region::clone () const
+{
+  return new globals_region (*this);
+}
+
+/* FIXME.  */
+
+bool
+globals_region::valid_key_p (tree key) const
+{
+  return TREE_CODE (key) == VAR_DECL;
+}
+
 /* class stack_region : public region.  */
 
 IMPL_POOL_ALLOCATOR (stack_region)
@@ -2009,7 +2262,7 @@ stack_region::pop_frame (region_model *model, bool purge, purge_stats *stats,
   svalue_id result_sid;
   tree fndecl = frame->get_function ()->decl;
   tree result = DECL_RESULT (fndecl);
-  if (result)
+  if (result && TREE_TYPE (result) != void_type_node)
     result_sid = model->get_rvalue (result, ctxt);
 
   /* Pop the frame RID.  */
@@ -2399,7 +2652,8 @@ root_region::ensure_stack_region (region_model *model)
   if (m_stack_rid.null_p ())
     {
       svalue_id uninit_sid
-	= model->add_svalue (new poisoned_svalue (POISON_KIND_UNINIT));
+	= model->add_svalue (new poisoned_svalue (POISON_KIND_UNINIT,
+						  NULL_TREE));
       m_stack_rid
 	= model->add_region (new stack_region (model->get_root_rid (),
 					       uninit_sid));
@@ -2423,16 +2677,16 @@ root_region::ensure_globals_region (region_model *model)
 {
   if (m_globals_rid.null_p ())
     m_globals_rid
-      = model->add_region (new map_region (model->get_root_rid (), NULL_TREE));
+      = model->add_region (new globals_region (model->get_root_rid ()));
   return m_globals_rid;
 }
 
 /* Return the globals region (which could be NULL).  */
 
-map_region *
+globals_region *
 root_region::get_globals_region (const region_model *model) const
 {
-  return model->get_region <map_region> (m_globals_rid);
+  return model->get_region <globals_region> (m_globals_rid);
 }
 
 /* Return the region_id of the heap region, creating it if doesn't
@@ -2444,7 +2698,8 @@ root_region::ensure_heap_region (region_model *model)
   if (m_heap_rid.null_p ())
     {
       svalue_id uninit_sid
-	= model->add_svalue (new poisoned_svalue (POISON_KIND_UNINIT));
+	= model->add_svalue (new poisoned_svalue (POISON_KIND_UNINIT,
+						  NULL_TREE));
       // FIXME: maybe share uninit values?
       m_heap_rid
 	= model->add_region (new heap_region (model->get_root_rid (),
@@ -2583,7 +2838,7 @@ IMPL_POOL_ALLOCATOR (symbolic_region)
 /* symbolic_region's copy ctor.  */
 
 symbolic_region::symbolic_region (const symbolic_region &other)
-: map_region (other)
+: region (other)
 {
 }
 
@@ -2606,6 +2861,15 @@ symbolic_region::clone () const
   return new symbolic_region (*this);
 }
 
+/* Implementation of region::walk_for_canonicalization vfunc for
+   symbolic_region.  */
+
+void
+symbolic_region::walk_for_canonicalization (canonicalization *c) const
+{
+  /* FIXME.  */
+}
+
 ////////////////////////////////////////////////////////////////////////////
 
 /* class region_model.  */
@@ -2955,6 +3219,14 @@ region_model::dump (bool summarize) const
   dump (stderr, summarize);
 }
 
+/* Dump RMODEL fully to stderr (i.e. without summarization).  */
+
+DEBUG_FUNCTION void
+region_model::debug () const
+{
+  dump (false);
+}
+
 /* Dump VEC to PP, in the form "{VEC elements}: LABEL".  */
 
 static void
@@ -3402,13 +3674,20 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 	tree ptr = rhs1;
 	tree offset = gimple_assign_rhs2 (assign);
 
-	region_id buffer_rid = deref_rvalue (ptr, ctxt);
-#if 0
+	//region_id buffer_rid = deref_rvalue (ptr, ctxt);
+#if 1
+	svalue_id ptr_sid = get_rvalue (ptr, ctxt);
 	svalue_id offset_sid = get_rvalue (offset, ctxt);
 	/* FIXME: offset is in bytes; we presumably want to
 	   map back to an index.  */
 #endif
+	region_id view_rid = add_view (ptr_sid, offset_sid, ctxt);
 
+	svalue_id element_ptr_sid
+	  = get_or_create_ptr_svalue (TREE_TYPE (ptr), view_rid);
+	set_value (lhs_rid, element_ptr_sid);
+
+#if 0
 	map_region *array_region = get_region<map_region> (buffer_rid);
 	tree element_type = TREE_TYPE (TREE_TYPE (ptr));
 #if 0
@@ -3425,6 +3704,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 	set_value (lhs_rid, element_ptr_sid);
 
 	//gcc_unreachable ();
+#endif
       }
       break;
 
@@ -3826,12 +4106,11 @@ region_model::handle_phi (tree lhs, tree rhs, bool is_back_edge,
 /* Get the id of the region for PV within this region_model.  */
 
 region_id
-region_model::get_lvalue (path_var pv, region_model_context *ctxt)
+region_model::get_lvalue_1 (path_var pv, region_model_context *ctxt)
 {
   tree expr = pv.m_tree;
 
-  if (expr == NULL_TREE)
-    return region_id::null ();
+  gcc_assert (expr);
 
   switch (TREE_CODE (expr))
     {
@@ -3849,28 +4128,40 @@ region_model::get_lvalue (path_var pv, region_model_context *ctxt)
 #endif
 
 	region_id array_rid = get_lvalue (array, ctxt);
-#if 0
+#if 1
 	svalue_id index_sid = get_rvalue (index, ctxt);
 #endif
-	map_region *array_region = get_region<map_region> (array_rid);
+	array_region *array_reg = get_region<array_region> (array_rid);
+#if 1
+	return array_reg->get_element (this, array_rid, index_sid, ctxt);
+#else
 	tree element_type = TREE_TYPE (TREE_TYPE (array));
+
+	// FIXME: if we have a constant, do this:
+	// FIXME; otherwise, make a view
+	// array_region::get_element (svalue_id index_sid)
 	region_id element_rid
-	  = array_region->get_or_create (this, array_rid, index, element_type);
+	  = array_reg->get_or_create (this, array_rid, index, element_type);
 	/* FIXME: ^^^ should be indexed by svalue, rather than by tree?
 	   What about unknown indices?  */
 	return element_rid;
+#endif
       }
       break; // FIXME
 
     case MEM_REF:
       {
 	tree ptr = TREE_OPERAND (expr, 0);
-#if 0
 	tree offset = TREE_OPERAND (expr, 1);
 	// FIXME: what about nonzero offset?
-#endif
+	svalue_id ptr_sid = get_rvalue (ptr, ctxt);
+	svalue_id offset_sid = get_rvalue (offset, ctxt);
 
+#if 1
+	return add_view (ptr_sid, offset_sid, ctxt);
+#else
 	return deref_rvalue (ptr, ctxt);
+#endif
       }
       break; // FIXME
 
@@ -3915,15 +4206,15 @@ region_model::get_lvalue (path_var pv, region_model_context *ctxt)
 	tree obj = TREE_OPERAND (expr, 0);
 	tree field = TREE_OPERAND (expr, 1);
 	region_id obj_rid = get_lvalue (obj, ctxt);
+	region_id struct_rid = add_view (obj_rid, TREE_TYPE (obj));
 	return get_field_region (obj_rid, field);
       }
       break;
 
     case STRING_CST:
       {
-	// FIXME: should map to a unique, custom region, containing the constant
 	tree cst_type = TREE_TYPE (expr);
-	map_region *cst_region = new map_region (m_root_rid, cst_type);
+	array_region *cst_region = new array_region (m_root_rid, cst_type);
 	svalue_id cst_sid = get_or_create_constant_svalue (expr);
 	cst_region->set_value (*this, cst_sid);
 	region_id cst_rid = add_region (cst_region);
@@ -3933,6 +4224,19 @@ region_model::get_lvalue (path_var pv, region_model_context *ctxt)
     }
 }
 
+/* FIXME.  */
+
+region_id
+region_model::get_lvalue (path_var pv, region_model_context *ctxt)
+{
+  if (pv.m_tree == NULL_TREE)
+    return region_id::null ();
+
+  region_id result_rid = get_lvalue_1 (pv, ctxt);
+  gcc_assert (get_region (result_rid)->get_type () == TREE_TYPE (pv.m_tree));
+  return result_rid;
+}
+
 /* Get the region_id for EXPR within this region_model (assuming the most
    recent stack frame if it's a local).  */
 
@@ -3945,11 +4249,10 @@ region_model::get_lvalue (tree expr, region_model_context *ctxt)
 /* Get the value of PV within this region_model.  */
 
 svalue_id
-region_model::get_rvalue (path_var pv, region_model_context *ctxt)
+region_model::get_rvalue_1 (path_var pv, region_model_context *ctxt)
 {
   tree expr = pv.m_tree; // FIXME: take account of stack depth!
-  if (expr == NULL_TREE)
-    return svalue_id::null ();
+  gcc_assert (expr);
 
   switch (TREE_CODE (expr))
     {
@@ -3997,6 +4300,18 @@ region_model::get_rvalue (path_var pv, region_model_context *ctxt)
     }
 }
 
+/* FIXME.  */
+
+svalue_id
+region_model::get_rvalue (path_var pv, region_model_context *ctxt)
+{
+  if (pv.m_tree == NULL_TREE)
+    return svalue_id::null ();
+  svalue_id result_sid = get_rvalue_1 (pv, ctxt);
+  gcc_assert (get_svalue (result_sid)->get_type () == TREE_TYPE (pv.m_tree));
+  return result_sid;
+}
+
 /* Get the value of EXPR within this region_model (assuming the most
    recent stack frame if it's a local).  */
 
@@ -4075,12 +4390,22 @@ region_model::maybe_cast (tree dst_type, svalue_id sid)
   if (src_type == dst_type)
     return sid;
 
+  // FIXME:
+  /* What about casting e.g. constants?  */
+  /* Should we have uniqueness?  */
+  /* Should we add equality constraints?  */
+
   /* If we have "struct subclass *" and we want "struct base *",
      where the first field of struct subclass is a struct base,
      then return a pointer to the child region for that field.  */
   if (POINTER_TYPE_P (dst_type)
       && POINTER_TYPE_P (src_type))
     {
+#if 1
+      /* Pointer to region.  */
+      if (region_svalue *ptr_sval = sval->dyn_cast_region_svalue ())
+	return get_or_create_ptr_svalue (dst_type, ptr_sval->get_pointee ());
+#else
       if (TREE_CODE (TREE_TYPE (dst_type)) == RECORD_TYPE
 	  && TREE_CODE (TREE_TYPE (src_type)) == RECORD_TYPE)
 	{
@@ -4101,30 +4426,47 @@ region_model::maybe_cast (tree dst_type, svalue_id sid)
 		  }
 	      }
 	}
+#endif
     }
 
-  return sid;
+  /* Attempt to cast constants.  */
+  if (constant_svalue *cst_sval = sval->dyn_cast_constant_svalue ())
+    {
+      tree src_cst = cst_sval->get_constant ();
+      // FIXME: maybe introduce a helper for this?
+
+      tree dst = fold_build1 (NOP_EXPR, dst_type, src_cst);
+      if (CONSTANT_CLASS_P (dst))
+	return get_or_create_constant_svalue (dst);
+    }
+
+  /* Otherwise, return a new unknown value.  */
+  svalue *unknown_sval = new unknown_svalue (dst_type);
+  return add_svalue (unknown_sval);
 }
 
 /* Ensure that the region for OBJ_RID has a child region for FIELD;
    return the child region's region_id.  */
 
 region_id
-region_model::get_field_region (region_id obj_rid, tree field)
-{
-  map_region *obj_region = get_region<map_region> (obj_rid);
-  region_id child_rid = obj_region->get_or_create (this, obj_rid, field,
-						   TREE_TYPE (field));
+region_model::get_field_region (region_id struct_or_union_rid, tree field)
+{
+  // TODO: the imposition of a view should have happened already
+  //region_id view_rid = add_view (obj_rid, TREE_TYPE (field));
+  struct_or_union_region *sou_reg
+    = get_region<struct_or_union_region> (struct_or_union_rid);
+  region_id child_rid
+    = sou_reg->get_or_create (this, struct_or_union_rid, field,
+			      TREE_TYPE (field));
   return child_rid;
 }
 
-/* Get a region_id for referencing PTR, creating a region if need be, and
+/* Get a region_id for referencing PTR_SID, creating a region if need be, and
    potentially generating warnings via CTXT.  */
 
 region_id
-region_model::deref_rvalue (tree ptr, region_model_context *ctxt)
+region_model::deref_rvalue (svalue_id ptr_sid, region_model_context *ctxt)
 {
-  svalue_id ptr_sid = get_rvalue (ptr, ctxt);
   gcc_assert (!ptr_sid.null_p ());
   svalue *ptr_svalue = get_svalue (ptr_sid);
   gcc_assert (ptr_svalue);
@@ -4162,8 +4504,10 @@ region_model::deref_rvalue (tree ptr, region_model_context *ctxt)
       {
 	poisoned_svalue *poisoned_sval = as_a <poisoned_svalue *> (ptr_svalue);
 	enum poison_kind pkind = poisoned_sval->get_poison_kind ();
+#if 0
 	if (ctxt)
 	  ctxt->warn (new poisoned_value_diagnostic (ptr, pkind));
+#endif
 	goto create_symbolic_region;
       }
 
@@ -4183,8 +4527,13 @@ region_model::deref_rvalue (tree ptr, region_model_context *ctxt)
 	   We replace the meaning of the ID rather than simply writing
 	   to PTR's lvalue since there could be several places sharing
 	   the same unknown ptr value.  */
-	svalue *ptr_val = new region_svalue (TREE_TYPE (ptr), new_rid);
+	//gcc_unreachable ();
+	// TODO: do we need to add a view?
+#if 1
+	svalue *ptr_val
+	  = new region_svalue (ptr_svalue->get_type (), new_rid);
 	replace_svalue (ptr_sid, ptr_val);
+#endif
 
 	return new_rid;
       }
@@ -4196,6 +4545,16 @@ region_model::deref_rvalue (tree ptr, region_model_context *ctxt)
   return region_id::null ();
 }
 
+/* Get a region_id for referencing PTR, creating a region if need be, and
+   potentially generating warnings via CTXT.  */
+
+region_id
+region_model::deref_rvalue (tree ptr, region_model_context *ctxt)
+{
+  svalue_id ptr_sid = get_rvalue (ptr, ctxt);
+  return deref_rvalue (ptr_sid, ctxt);
+}
+
 /* Set the value of the region given by LHS_RID to the value given
    by RHS_SID.  */
 
@@ -4432,6 +4791,12 @@ region_model::add_new_malloc_region ()
   region_id heap_rid
     = get_root_region ()->ensure_heap_region (this);
   return add_region (new symbolic_region (heap_rid));
+
+#if 0
+#error
+  // FIXME: set type to void *: ptr_type_node
+  // or just have every symbolic_region be void *
+#endif
 }
 
 /* Attempt to return a tree that represents SID, or return NULL_TREE.
@@ -4487,7 +4852,7 @@ region_model::get_representative_path_var (region_id rid) const
   region *parent_reg = get_region (parent_rid);
   if (parent_reg)
     {
-      if (parent_reg->get_kind () == RK_MAP)
+      if (parent_reg->get_kind () == RK_STRUCT)
 	{
 	  map_region *parent_map_region = (map_region *)parent_reg;
 	  tree child_key = parent_map_region->get_tree_for_child_region (rid);
@@ -4899,6 +5264,7 @@ region_model::replace_svalue (svalue_id sid, svalue *new_sval)
   int idx = sid.as_int ();
 
   gcc_assert (m_svalues[idx]);
+  gcc_assert (m_svalues[idx]->get_type () == new_sval->get_type ());
   delete m_svalues[idx];
 
   m_svalues[idx] = new_sval;
@@ -4935,6 +5301,47 @@ region_model::get_region (region_id rid) const
   return m_regions[rid.as_int ()];
 }
 
+/* FIXME.  */
+
+static region *
+make_region_for_type (region_id parent_rid, tree type)
+{
+  gcc_assert (TYPE_P (type));
+
+  if (INTEGRAL_TYPE_P (type)
+      || SCALAR_FLOAT_TYPE_P (type)
+      || POINTER_TYPE_P (type))
+    return new primitive_region (parent_rid, type);
+
+  if (TREE_CODE (type) == RECORD_TYPE)
+    return new struct_region (parent_rid, type);
+
+  if (TREE_CODE (type) == ARRAY_TYPE)
+    return new array_region (parent_rid, type);
+
+  if (TREE_CODE (type) == UNION_TYPE)
+    return new union_region (parent_rid, type);
+
+  /* If we have a void *, make a new symbolic region.  */
+  if (type == void_type_node)
+    return new symbolic_region (parent_rid);
+
+  gcc_unreachable ();
+}
+
+// TODO: selftests for this?
+
+/* FIXME.  */
+
+region_id
+region_model::add_region_for_type (region_id parent_rid, tree type)
+{
+  gcc_assert (TYPE_P (type));
+
+  region *new_region = make_region_for_type (parent_rid, type);
+  return add_region (new_region);
+}
+
 /* Helper class for region_model::purge_unused_svalues.  */
 
 class restrict_to_used_svalues : public purge_criteria
@@ -5279,7 +5686,7 @@ region_model::poison_any_pointers_to_bad_regions (const region_id_set &
 	  if (bad_regions.region_p (ptr_dst))
 	    replace_svalue
 	      (svalue_id::from_int (i),
-	       new poisoned_svalue (pkind));
+	       new poisoned_svalue (pkind, sval->get_type ()));
       }
 }
 
@@ -5349,6 +5756,44 @@ region_model::get_value_by_name (const char *name) const
   return get_root_region ()->get_value_by_name (identifier, *this);
 }
 
+/* FIXME.  */
+/* might add a new region to MODEL, or return the given region.
+   (might be easier if we let regions know their own IDs).  */
+
+region_id
+region_model::add_view (svalue_id ptr_sid, svalue_id offset_sid,
+			region_model_context *ctxt)
+{
+  svalue *ptr_sval = get_svalue (ptr_sid);
+  tree ptr_type = ptr_sval->get_type ();
+  gcc_assert (ptr_type);
+  region_id raw_rid = deref_rvalue (ptr_sid, ctxt);
+
+  // TODO: what about offset_sid
+
+  return add_view (raw_rid, TREE_TYPE (ptr_type));
+}
+
+/* FIXME.  */
+
+region_id
+region_model::add_view (region_id raw_rid, tree type)
+{
+  region *raw_region = get_region (raw_rid);
+
+  // TODO: reuse existing views?
+
+  gcc_assert (TYPE_P (type));
+  if (type != raw_region->get_type ())
+    {
+      region_id view_rid = add_region_for_type (raw_rid, type);
+      // TODO: something to signify that this is a "view"
+      return view_rid;
+    }
+
+  return raw_rid;
+}
+
 ////////////////////////////////////////////////////////////////////////////
 
 /* struct model_merger.  */
@@ -5767,7 +6212,7 @@ test_dump ()
 		  "r0: {kind: `root', parent: null, sval: null}\n"
 		  "|-stack: r1: {kind: `stack', parent: r0, sval: sv0}\n"
 		  "|  |: sval: sv0: {poisoned: uninit}\n"
-		  "|-globals: r2: {kind: `map', parent: r0, sval: null, map: {}}\n"
+		  "|-globals: r2: {kind: `globals', parent: r0, sval: null, map: {}}\n"
 		  "`-heap: r3: {kind: `heap', parent: r0, sval: sv1}\n"
 		  "  |: sval: sv1: {poisoned: uninit}\n"
 		  "svalues:\n"
@@ -5829,8 +6274,8 @@ test_svalue_equality ()
   ASSERT_NE (cst_int_42->hash (), cst_int_0->hash ());
   ASSERT_NE (*cst_int_42, *cst_int_0);
 
-  svalue *uninit = new poisoned_svalue (POISON_KIND_UNINIT);
-  svalue *freed = new poisoned_svalue (POISON_KIND_FREED);
+  svalue *uninit = new poisoned_svalue (POISON_KIND_UNINIT, NULL_TREE);
+  svalue *freed = new poisoned_svalue (POISON_KIND_FREED, NULL_TREE);
 
   ASSERT_EQ (uninit->hash (), uninit->hash ());
   ASSERT_EQ (*uninit, *uninit);
@@ -5874,6 +6319,7 @@ test_svalue_equality ()
 static void
 test_region_equality ()
 {
+#if 0
   region *r0 = new map_region (region_id::from_int (3), integer_type_node);
   region *r1 = new map_region (region_id::from_int (4), integer_type_node);
 
@@ -5886,6 +6332,7 @@ test_region_equality ()
 
   delete r0;
   delete r1;
+#endif
 }
 
 /* A subclass of purge_criteria for selftests: purge all svalue_id instances.  */
@@ -6578,10 +7025,12 @@ test_state_merging ()
 				&merged_x_sval);
 
     /* We should get x == &y in the merged model.  */
+#if 0
     ASSERT_EQ (merged_x_sval->get_kind (), SK_REGION);
     region_svalue *merged_x_ptr = merged_x_sval->dyn_cast_region_svalue ();
     region_id merged_x_star_rid = merged_x_ptr->get_pointee ();
     ASSERT_EQ (merged_x_star_rid, merged.get_lvalue (y, NULL));
+#endif
   }
 
   /* Pointers: non-NULL ptrs to different globals: should be unknown.  */
@@ -6618,7 +7067,9 @@ test_state_merging ()
 
     /* The merged model ought to be identical (after canonicalization,
        at least).  */
+#if 0
     ASSERT_EQ (model0, merged);
+#endif
   }
 
   /* Two regions sharing the same unknown svalue should continue sharing
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 9877f39..15554f5 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -443,6 +443,20 @@ enum svalue_kind
   SK_SETJMP
 };
 
+/* svalue and its subclasses.
+
+   The class hierarchy looks like this (using indentation to show
+   inheritance, and with svalue_kinds shown for the concrete subclasses):
+
+   svalue
+     region_svalue (SK_REGION)
+     constant_svalue (SK_CONSTANT)
+     poisoned_svalue (SK_POISONED)
+     unknown_svalue (SK_UNKNOWN)
+     setjmp_svalue (SK_SETJMP)
+
+   FIXME: the subclass decls are out of order w.r.t. the enum.  */
+
 /* An abstract base class representing a value held by a region of memory.  */
 
 class svalue
@@ -679,12 +693,13 @@ public:
   static void *operator new (size_t sz);
   static void operator delete (void* ptr, size_t sz);
 
-  poisoned_svalue (enum poison_kind kind) : svalue (NULL), m_kind (kind) {}
+  poisoned_svalue (enum poison_kind kind, tree type)
+  : svalue (type), m_kind (kind) {}
 
   bool compare_fields (const poisoned_svalue &other) const;
 
   svalue *clone () const FINAL OVERRIDE
-  { return new poisoned_svalue (m_kind); }
+  { return new poisoned_svalue (m_kind, get_type ()); }
 
   enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_POISONED; }
 
@@ -752,8 +767,12 @@ public:
 
 enum region_kind
 {
-  RK_MAP,
+  RK_PRIMITIVE,
+  RK_STRUCT,
+  RK_UNION,
+  RK_ARRAY,
   RK_FRAME,
+  RK_GLOBALS,
   RK_STACK,
   RK_HEAP,
   RK_ROOT,
@@ -762,6 +781,26 @@ enum region_kind
 
 extern const char *region_kind_to_str (enum region_kind);
 
+/* Region and its subclasses.
+
+   The class hierarchy looks like this (using indentation to show
+   inheritance, and with region_kinds shown for the concrete subclasses):
+
+   region
+     primitive_region (RK_PRIMITIVE)
+     map_region
+       struct_or_union_region
+         struct_region (RK_STRUCT)
+         union_region (RK_UNION)
+       array_region (RK_ARRAY)
+       scope_region
+         frame_region (RK_FRAME)
+         globals_region (RK_GLOBALS)
+     stack_region (RK_STACK)
+     heap_region (RK_HEAP)
+     root_region (RK_ROOT)
+     symbolic_region (RK_SYMBOLIC).  */
+
 /* Abstract base class representing a chunk of memory.
 
    Regions form a tree-like hierarchy, with a root region at the base,
@@ -850,6 +889,30 @@ public:
   tree m_type;
 };
 
+template <>
+template <>
+inline bool
+is_a_helper <region *>::test (region *)
+{
+  return true;
+}
+
+/* Concrete region subclass for FIXME.  */
+
+class primitive_region : public region
+{
+public:
+  primitive_region (region_id parent_rid, tree type)
+  : region (parent_rid, svalue_id::null (), type)
+  {}
+
+  region *clone () const FINAL OVERRIDE;
+
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_PRIMITIVE; }
+
+  void walk_for_canonicalization (canonicalization *c) const FINAL OVERRIDE;
+};
+
 /* A region that has children identified by tree keys.
    For example a stack frame has subregions per local, and a region
    for a struct has subregions per field.  */
@@ -865,12 +928,6 @@ public:
   {}
   map_region (const map_region &other);
 
-  bool compare_fields (const map_region &other) const;
-
-  region *clone () const OVERRIDE;
-
-  enum region_kind get_kind () const OVERRIDE { return RK_MAP; }
-
   void dump_dot_to_pp (const region_model &model,
 		       region_id this_rid,
 		       pretty_printer *pp) const
@@ -882,8 +939,6 @@ public:
 			 pretty_printer *pp) const
     FINAL OVERRIDE;
 
-  bool valid_key_p (tree key) const;
-
   region_id get_or_create (region_model *model,
 			   region_id this_rid,
 			   tree expr, tree type);
@@ -905,6 +960,8 @@ public:
 
   void walk_for_canonicalization (canonicalization *c) const FINAL OVERRIDE;
 
+  virtual bool valid_key_p (tree key) const = 0;
+
   svalue_id get_value_by_name (tree identifier,
 			       const region_model &model) const;
 
@@ -913,6 +970,7 @@ public:
   size_t elements () const { return m_map.elements (); }
 
  protected:
+  bool compare_fields (const map_region &other) const;
   void add_to_hash (inchash::hash &hstate) const OVERRIDE;
   void print_fields (const region_model &model,
 		     region_id this_rid,
@@ -924,47 +982,235 @@ public:
   map_t m_map;
 };
 
-/* A region representing a function frame on the stack, to contain
-   the locals.  */
+template <>
+template <>
+inline bool
+is_a_helper <map_region *>::test (region *reg)
+{
+  return (reg->get_kind () == RK_STRUCT
+	  || reg->get_kind () == RK_ARRAY
+	  || reg->get_kind () == RK_FRAME
+	  || reg->get_kind () == RK_GLOBALS);
+}
+
+/* FIXME.  A region with fields.  */
 
-class frame_region : public map_region
+class struct_or_union_region : public map_region
 {
 public:
-  static void *operator new (size_t sz);
-  static void operator delete (void* ptr, size_t sz);
+  bool valid_key_p (tree key) const FINAL OVERRIDE;
 
-  frame_region (region_id parent_rid, function *fun, int depth)
-  : map_region (parent_rid, NULL_TREE), m_fun (fun), m_depth (depth)
+ protected:
+  struct_or_union_region (region_id parent_rid, tree type)
+  : map_region (parent_rid, type)
   {}
 
-  frame_region (const frame_region &other)
-  : map_region (other), m_fun (other.m_fun), m_depth (other.m_depth)
+  bool compare_fields (const struct_or_union_region &other) const;
+};
+
+template <>
+template <>
+inline bool
+is_a_helper <struct_or_union_region *>::test (region *reg)
+{
+  return (reg->get_kind () == RK_STRUCT
+	  || reg->get_kind () == RK_UNION);
+}
+
+/* Concrete region subclass.  A map_region representing a struct, using
+   FIELD_DECLs for its keys.  */
+
+class struct_region : public struct_or_union_region
+{
+public:
+  struct_region (region_id parent_rid, tree type)
+  : struct_or_union_region (parent_rid, type)
   {
+    gcc_assert (TREE_CODE (type) == RECORD_TYPE);
   }
 
-  bool compare_fields (const frame_region &other) const;
+  region *clone () const FINAL OVERRIDE;
+
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_STRUCT; }
+
+  bool compare_fields (const struct_region &other) const;
+};
+
+template <>
+template <>
+inline bool
+is_a_helper <struct_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_STRUCT;
+}
+
+/* Concrete region subclass.  A map_region representing a union, using
+   FIELD_DECLs for its keys.  */
+
+class union_region : public struct_or_union_region
+{
+public:
+  union_region (region_id parent_rid, tree type)
+  : struct_or_union_region (parent_rid, type)
+  {
+    gcc_assert (TREE_CODE (type) == UNION_TYPE);
+  }
 
   region *clone () const FINAL OVERRIDE;
 
-  enum region_kind get_kind () const FINAL OVERRIDE { return RK_FRAME; }
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_UNION; }
 
-  function *get_function () const { return m_fun; }
+  bool compare_fields (const union_region &other) const;
+};
 
-  int get_depth () const { return m_depth; }
+template <>
+template <>
+inline bool
+is_a_helper <union_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_UNION;
+}
 
- private:
-  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+/* Concrete region subclass.  A map_region representing an array, using
+   INTEGER_CSTs for its keys.  */
+
+class array_region : public map_region
+{
+public:
+  array_region (region_id parent_rid, tree type)
+  : map_region (parent_rid, type)
+  {
+    gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+  }
+  array_region (const array_region &other);
+
+  /* region vfuncs.  */
+  region *clone () const FINAL OVERRIDE;
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_ARRAY; }
+
+  /* map_region vfunc.  */
+  bool valid_key_p (tree key) const FINAL OVERRIDE;
+
+  region_id get_element (region_model *model,
+			 region_id this_rid,
+			 svalue_id index_sid,
+			 region_model_context *ctxt);
+
+  bool compare_fields (const array_region &other) const;
+};
+
+template <>
+template <>
+inline bool
+is_a_helper <array_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_ARRAY;
+}
+
+/* Abstract map_region subclass for accessing decls, used as a base class
+   for function frames and for the globals region.  */
+
+class scope_region : public map_region
+{
+ public:
+
+ protected:
+  scope_region (region_id parent_rid)
+  : map_region (parent_rid, NULL_TREE)
+  {}
+
+  scope_region (const scope_region &other)
+  : map_region (other)
+  {
+  }
+
+  bool compare_fields (const scope_region &other) const;
+};
+
+/* Concrete region subclass, representing a function frame on the stack,
+   to contain the locals.  */
+
+class frame_region : public scope_region
+{
+public:
+  static void *operator new (size_t sz);
+  static void operator delete (void* ptr, size_t sz);
+
+  frame_region (region_id parent_rid, function *fun, int depth)
+  : scope_region (parent_rid), m_fun (fun), m_depth (depth)
+  {}
+
+  frame_region (const frame_region &other)
+  : scope_region (other), m_fun (other.m_fun), m_depth (other.m_depth)
+  {
+  }
+
+  /* region vfuncs.  */
+  region *clone () const FINAL OVERRIDE;
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_FRAME; }
   void print_fields (const region_model &model,
 		     region_id this_rid,
 		     pretty_printer *pp) const
     FINAL OVERRIDE;
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+
+  /* map_region vfuncs.  */
+  bool valid_key_p (tree key) const FINAL OVERRIDE;
 
+  /* Accessors.  */
+  function *get_function () const { return m_fun; }
+  int get_depth () const { return m_depth; }
+
+  bool compare_fields (const frame_region &other) const;
+
+ private:
   function *m_fun;
   int m_depth;
 };
 
-/* A region containing all stack frames, and implicitly providing
-   a POISON_KIND_UNINIT value to all child regions by default.  */
+template <>
+template <>
+inline bool
+is_a_helper <frame_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_FRAME;
+}
+
+/* Concrete region subclass, to hold global variables (data and bss).  */
+
+class globals_region : public scope_region
+{
+ public:
+  globals_region (region_id parent_rid)
+  : scope_region (parent_rid)
+  {}
+
+  globals_region (const globals_region &other)
+  : scope_region (other)
+  {
+  }
+
+  /* region vfuncs.  */
+  region *clone () const FINAL OVERRIDE;
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_GLOBALS; }
+
+  /* map_region vfuncs.  */
+  bool valid_key_p (tree key) const FINAL OVERRIDE;
+
+  bool compare_fields (const globals_region &other) const;
+};
+
+template <>
+template <>
+inline bool
+is_a_helper <globals_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_GLOBALS;
+}
+
+/* Concrete region subclass representing a stack, containing all stack
+   frames, and implicitly providing a POISON_KIND_UNINIT value to all
+   child regions by default.  */
 
 class stack_region : public region
 {
@@ -1019,7 +1265,16 @@ public:
   auto_vec<region_id> m_frame_rids;
 };
 
-/* A region within which regions can be dynamically allocated.  */
+template <>
+template <>
+inline bool
+is_a_helper <stack_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_STACK;
+}
+
+/* Concrete region subclass: a region within which regions can be
+   dynamically allocated.  */
 
 class heap_region : public region
 {
@@ -1034,9 +1289,9 @@ public:
 
   bool compare_fields (const heap_region &other) const;
 
-  region *clone () const OVERRIDE;
+  region *clone () const FINAL OVERRIDE;
 
-  enum region_kind get_kind () const OVERRIDE { return RK_HEAP; }
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_HEAP; }
 
   static bool can_merge_p (const heap_region *heap_a, region_id heap_a_rid,
 			   const heap_region *heap_b, region_id heap_b_rid,
@@ -1047,8 +1302,16 @@ public:
 
 };
 
-/* The root region, containing all regions (either directly, or as
-   descendents).
+template <>
+template <>
+inline bool
+is_a_helper <heap_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_HEAP;
+}
+
+/* Concrete region subclass.  The root region, containing all regions
+   (either directly, or as descendents).
    Unique within a region_model.  */
 
 class root_region : public region
@@ -1084,7 +1347,7 @@ public:
 
   region_id ensure_globals_region (region_model *model);
   region_id get_globals_region_id () const { return m_globals_rid; }
-  map_region *get_globals_region (const region_model *model) const;
+  globals_region *get_globals_region (const region_model *model) const;
 
   region_id ensure_heap_region (region_model *model);
   heap_region *get_heap_region (const region_model *model) const;
@@ -1113,24 +1376,35 @@ private:
   region_id m_heap_rid;
 };
 
-/* A region to use when dereferencing an unknown pointer.  */
+template <>
+template <>
+inline bool
+is_a_helper <root_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_ROOT;
+}
+
+/* Concrete region subclass: a region to use when dereferencing an unknown
+   pointer.  */
 
-class symbolic_region : public map_region
+class symbolic_region : public region
 {
 public:
   static void *operator new (size_t sz);
   static void operator delete (void* ptr, size_t sz);
 
   symbolic_region (region_id parent_rid)
-  : map_region (parent_rid, NULL_TREE)
+  : region (parent_rid, svalue_id::null (), NULL_TREE)
   {}
   symbolic_region (const symbolic_region &other);
 
   bool compare_fields (const symbolic_region &other) const;
 
-  region *clone () const OVERRIDE;
+  region *clone () const FINAL OVERRIDE;
+
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_SYMBOLIC; }
 
-  enum region_kind get_kind () const OVERRIDE { return RK_SYMBOLIC; }
+  void walk_for_canonicalization (canonicalization *c) const FINAL OVERRIDE;
 };
 
 ////////////////////////////////////////////////////////////////////////////
@@ -1178,6 +1452,8 @@ class region_model
   void dump (FILE *fp, bool summarize) const;
   void dump (bool summarize) const;
 
+  void debug () const;
+
   void validate () const;
 
   void canonicalize (region_model_context *ctxt);
@@ -1221,13 +1497,18 @@ class region_model
 
   region_id add_region (region *r);
 
+  region_id add_region_for_type (region_id parent_rid, tree type);
+
   svalue *get_svalue (svalue_id sval_id) const;
   region *get_region (region_id rid) const;
 
   template <typename Subclass>
   Subclass *get_region (region_id rid) const
   {
-    return (Subclass *)get_region (rid); // TODO: checking
+    region *result = get_region (rid);
+    if (result)
+      gcc_assert (is_a<Subclass *> (result));
+    return (Subclass *)result;
   }
 
   region_id get_lvalue (path_var pv, region_model_context *ctxt);
@@ -1243,6 +1524,7 @@ class region_model
 
   region_id get_field_region (region_id rid, tree field);
 
+  region_id deref_rvalue (svalue_id ptr_sid, region_model_context *ctxt);
   region_id deref_rvalue (tree ptr, region_model_context *ctxt);
 
   void set_value (region_id lhs_rid, svalue_id rhs_sid);
@@ -1300,7 +1582,14 @@ class region_model
 
   svalue_id get_value_by_name (const char *name) const;
 
+  region_id add_view (svalue_id ptr_sid, svalue_id offset_sid,
+		      region_model_context *ctxt);
+  region_id add_view (region_id raw_rid, tree type);
+
  private:
+  region_id get_lvalue_1 (path_var pv, region_model_context *ctxt);
+  svalue_id get_rvalue_1 (path_var pv, region_model_context *ctxt);
+
   void add_any_constraints_from_ssa_def_stmt (tree lhs,
 					      enum tree_code op,
 					      tree rhs,
-- 
1.8.5.3

