Update out-of-bounds diagrams to show existing string values,
and the initial write index within a string buffer.

For example, given the out-of-bounds write in strcat in:

void test (void)
{
  char buf[10];
  strcpy (buf, "hello");
  strcat (buf, " world!");
}

the diagram improves from:

                           ┌─────┬─────┬────┬────┬────┐┌─────┬─────┬─────┐
                           │ [0] │ [1] │[2] │[3] │[4] ││ [5] │ [6] │ [7] │
                           ├─────┼─────┼────┼────┼────┤├─────┼─────┼─────┤
                           │ ' ' │ 'w' │'o' │'r' │'l' ││ 'd' │ '!' │ NUL │
                           ├─────┴─────┴────┴────┴────┴┴─────┴─────┴─────┤
                           │      string literal (type: 'char[8]')       │
                           └─────────────────────────────────────────────┘
                              │     │    │    │    │      │     │     │
                              │     │    │    │    │      │     │     │
                              v     v    v    v    v      v     v     v
  ┌─────┬────────────────────────────────────────┬────┐┌─────────────────┐
  │ [0] │                  ...                   │[9] ││                 │
  ├─────┴────────────────────────────────────────┴────┤│after valid range│
  │             'buf' (type: 'char[10]')              ││                 │
  └───────────────────────────────────────────────────┘└─────────────────┘
  ├─────────────────────────┬─────────────────────────┤├────────┬────────┤
                            │                                   │
                  ╭─────────┴────────╮                ╭─────────┴─────────╮
                  │capacity: 10 bytes│                │overflow of 3 bytes│
                  ╰──────────────────╯                ╰───────────────────╯

to:

                             ┌────┬────┬────┬────┬────┐┌─────┬─────┬─────┐
                             │[0] │[1] │[2] │[3] │[4] ││ [5] │ [6] │ [7] │
                             ├────┼────┼────┼────┼────┤├─────┼─────┼─────┤
                             │' ' │'w' │'o' │'r' │'l' ││ 'd' │ '!' │ NUL │
                             ├────┴────┴────┴────┴────┴┴─────┴─────┴─────┤
                             │     string literal (type: 'char[8]')      │
                             └───────────────────────────────────────────┘
                               │    │    │    │    │      │     │     │
                               │    │    │    │    │      │     │     │
                               v    v    v    v    v      v     v     v
  ┌─────┬────────────────────┬────┬──────────────┬────┐┌─────────────────┐
  │ [0] │        ...         │[5] │     ...      │[9] ││                 │
  ├─────┼────┬────┬────┬────┬┼────┼──────────────┴────┘│                 │
  │ 'h' │'e' │'l' │'l' │'o' ││NUL │                    │after valid range│
  ├─────┴────┴────┴────┴────┴┴────┴───────────────────┐│                 │
  │             'buf' (type: 'char[10]')              ││                 │
  └───────────────────────────────────────────────────┘└─────────────────┘
  ├─────────────────────────┬─────────────────────────┤├────────┬────────┤
                            │                                   │
                  ╭─────────┴────────╮                ╭─────────┴─────────╮
                  │capacity: 10 bytes│                │overflow of 3 bytes│
                  ╰──────────────────╯                ╰───────────────────╯

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r14-4477-gb365e9d57ad445.

gcc/analyzer/ChangeLog:
        PR analyzer/111155
        * access-diagram.cc (boundaries::boundaries): Add logger param
        (boundaries::add): Add logging.
        (boundaries::get_hard_boundaries_in_range): New.
        (boundaries::m_logger): New field.
        (boundaries::get_table_x_for_offset): Make public.
        (class svalue_spatial_item): New.
        (class compound_svalue_spatial_item): New.
        (add_ellipsis_to_gaps): New.
        (valid_region_spatial_item::valid_region_spatial_item): Add theme
        param.  Initialize m_boundaries, m_existing_sval, and
        m_existing_sval_spatial_item.
        (valid_region_spatial_item::add_boundaries): Set m_boundaries.
        Add boundaries for any m_existing_sval_spatial_item.
        (valid_region_spatial_item::add_array_elements_to_table): Rewrite
        creation of min/max index in terms of
        maybe_add_array_index_to_table.  Rewrite ellipsis code using
        add_ellipsis_to_gaps. Add index values for any hard boundaries
        within the valid region.
        (valid_region_spatial_item::maybe_add_array_index_to_table): New,
        based on code formerly in add_array_elements_to_table.
        (valid_region_spatial_item::make_table): Make use of
        m_existing_sval_spatial_item, if any.
        (valid_region_spatial_item::m_boundaries): New field.
        (valid_region_spatial_item::m_existing_sval): New field.
        (valid_region_spatial_item::m_existing_sval_spatial_item): New
        field.
        (class svalue_spatial_item): Rename to...
        (class written_svalue_spatial_item): ...this.
        (class string_region_spatial_item): Rename to..
        (class string_literal_spatial_item): ...this.  Add "kind".
        (string_literal_spatial_item::add_boundaries): Use m_kind to
        determine kind of boundary.  Update for renaming of m_actual_bits
        to m_bits.
        (string_literal_spatial_item::make_table): Likewise.  Support not
        displaying a row for byte indexes, and not displaying a row for
        the type.
        (string_literal_spatial_item::add_column_for_byte): Make byte index
        row optional.
        (svalue_spatial_item::make): Convert to...
        (make_written_svalue_spatial_item): ...this.
        (make_existing_svalue_spatial_item): New.
        (access_diagram_impl::access_diagram_impl): Pass theme to
        m_valid_region_spatial_item ctor.  Update for renaming of
        m_svalue_spatial_item.
        (access_diagram_impl::find_boundaries): Pass logger to boundaries.
        Update for renaming of...
        (access_diagram_impl::m_svalue_spatial_item): Rename to...
        (access_diagram_impl::m_written_svalue_spatial_item): ...this.

gcc/testsuite/ChangeLog:
        PR analyzer/111155
        * c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c: New test.
        * c-c++-common/analyzer/out-of-bounds-diagram-strcat.c: New test.
        * gcc.dg/analyzer/out-of-bounds-diagram-17.c: Update expected
        result to show the existing content of "buf" and the index at
        which the write starts.
        * gcc.dg/analyzer/out-of-bounds-diagram-18.c: Likewise.
        * gcc.dg/analyzer/out-of-bounds-diagram-19.c: Likewise.
        * gcc.dg/analyzer/out-of-bounds-diagram-6.c: Update expected
        output.

gcc/ChangeLog:
        PR analyzer/111155
        * text-art/table.cc (table::maybe_set_cell_span): New.
        (table::add_other_table): New.
        * text-art/table.h (class table::cell_placement): Add class table
        as a friend.
        (table::add_rows): New.
        (table::add_row): Reimplement in terms of add_rows.
        (table::maybe_set_cell_span): New decl.
        (table::add_other_table): New decl.
        * text-art/types.h (operator+): New operator for rect + coord.
---
 gcc/analyzer/access-diagram.cc                | 430 ++++++++++++++----
 .../analyzer/out-of-bounds-diagram-strcat-2.c |  74 +++
 .../analyzer/out-of-bounds-diagram-strcat.c   |  66 +++
 .../analyzer/out-of-bounds-diagram-17.c       |  28 +-
 .../analyzer/out-of-bounds-diagram-18.c       |  54 ++-
 .../analyzer/out-of-bounds-diagram-19.c       |  42 +-
 .../gcc.dg/analyzer/out-of-bounds-diagram-6.c |  68 +--
 gcc/text-art/table.cc                         |  35 ++
 gcc/text-art/table.h                          |  21 +-
 gcc/text-art/types.h                          |   7 +
 10 files changed, 644 insertions(+), 181 deletions(-)
 create mode 100644 
gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c
 create mode 100644 
gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat.c

diff --git a/gcc/analyzer/access-diagram.cc b/gcc/analyzer/access-diagram.cc
index a51d594b5b2..2197ec63f53 100644
--- a/gcc/analyzer/access-diagram.cc
+++ b/gcc/analyzer/access-diagram.cc
@@ -630,8 +630,8 @@ class boundaries
 public:
   enum class kind { HARD, SOFT};
 
-  boundaries (const region &base_reg)
-  : m_base_reg (base_reg)
+  boundaries (const region &base_reg, logger *logger)
+  : m_base_reg (base_reg), m_logger (logger)
   {
   }
 
@@ -646,6 +646,15 @@ public:
   {
     add (range.m_start, kind);
     add (range.m_next, kind);
+    if (m_logger)
+      {
+       m_logger->start_log_line ();
+       m_logger->log_partial ("added access_range: ");
+       range.dump_to_pp (m_logger->get_printer (), true);
+       m_logger->log_partial (" (%s)",
+                              (kind == kind::HARD) ? "HARD" : "soft");
+       m_logger->end_log_line ();
+      }
   }
 
   void add (const region &reg, region_model_manager *mgr, enum kind kind)
@@ -714,8 +723,30 @@ public:
     return m_all_offsets.size ();
   }
 
+  std::vector<region_offset>
+  get_hard_boundaries_in_range (byte_offset_t min_offset,
+                               byte_offset_t max_offset) const
+  {
+    std::vector<region_offset> result;
+    for (auto &offset : m_hard_offsets)
+      {
+       if (!offset.concrete_p ())
+         continue;
+       byte_offset_t byte;
+       if (!offset.get_concrete_byte_offset (&byte))
+         continue;
+       if (byte < min_offset)
+         continue;
+       if (byte > max_offset)
+         continue;
+       result.push_back (offset);
+      }
+    return result;
+  }
+
 private:
   const region &m_base_reg;
+  logger *m_logger;
   std::set<region_offset> m_all_offsets;
   std::set<region_offset> m_hard_offsets;
 };
@@ -1085,7 +1116,6 @@ public:
     logger.dec_indent ();
   }
 
-private:
   int get_table_x_for_offset (region_offset offset) const
   {
     auto slot = m_table_x_for_offset.find (offset);
@@ -1097,6 +1127,7 @@ private:
     return slot->second;
   }
 
+private:
   int get_table_x_for_prev_offset (region_offset offset) const
   {
     auto slot = m_table_x_for_prev_offset.find (offset);
@@ -1132,6 +1163,124 @@ public:
                            style_manager &sm) const = 0;
 };
 
+/* A spatial_item that involves showing an svalue at a particular offset.  */
+
+class svalue_spatial_item : public spatial_item
+{
+public:
+  enum class kind
+  {
+     WRITTEN,
+     EXISTING
+  };
+protected:
+  svalue_spatial_item (const svalue &sval,
+                      access_range bits,
+                      enum kind kind)
+  : m_sval (sval), m_bits (bits), m_kind (kind)
+  {
+  }
+
+  const svalue &m_sval;
+  access_range m_bits;
+  enum kind m_kind;
+};
+
+static std::unique_ptr<spatial_item>
+make_existing_svalue_spatial_item (const svalue *sval,
+                                  const access_range &bits,
+                                  const theme &theme);
+
+class compound_svalue_spatial_item : public svalue_spatial_item
+{
+public:
+  compound_svalue_spatial_item (const compound_svalue &sval,
+                               const access_range &bits,
+                               enum kind kind,
+                               const theme &theme)
+  : svalue_spatial_item (sval, bits, kind),
+    m_compound_sval (sval)
+  {
+    const binding_map &map = m_compound_sval.get_map ();
+    auto_vec <const binding_key *> binding_keys;
+    for (auto iter : map)
+      {
+       const binding_key *key = iter.first;
+       const svalue *bound_sval = iter.second;
+       if (const concrete_binding *concrete_key
+             = key->dyn_cast_concrete_binding ())
+         {
+           access_range range (nullptr,
+                               concrete_key->get_bit_range ());
+           if (std::unique_ptr<spatial_item> child
+                 = make_existing_svalue_spatial_item (bound_sval,
+                                                      range,
+                                                      theme))
+             m_children.push_back (std::move (child));
+         }
+      }
+  }
+
+  void add_boundaries (boundaries &out, logger *logger) const final override
+  {
+    LOG_SCOPE (logger);
+    for (auto &iter : m_children)
+      iter->add_boundaries (out, logger);
+  }
+
+  table make_table (const bit_to_table_map &btm,
+                   style_manager &sm) const final override
+  {
+    std::vector<table> child_tables;
+    int max_rows = 0;
+    for (auto &iter : m_children)
+      {
+       table child_table (iter->make_table (btm, sm));
+       max_rows = MAX (max_rows, child_table.get_size ().h);
+       child_tables.push_back (std::move (child_table));
+      }
+    table t (table::size_t (btm.get_num_columns (), max_rows));
+    for (auto &&child_table : child_tables)
+      t.add_other_table (std::move (child_table),
+                        table::coord_t (0, 0));
+    return t;
+  }
+
+private:
+  const compound_svalue &m_compound_sval;
+  std::vector<std::unique_ptr<spatial_item>> m_children;
+};
+
+/* Loop through the TABLE_X_RANGE columns of T, adding
+   cells containing "..." in any unoccupied ranges of table cell.  */
+
+static void
+add_ellipsis_to_gaps (table &t,
+                     style_manager &sm,
+                     const table::range_t &table_x_range,
+                     const table::range_t &table_y_range)
+{
+  int table_x = table_x_range.get_min ();
+  while (table_x < table_x_range.get_next ())
+    {
+      /* Find a run of unoccupied table cells.  */
+      const int start_table_x = table_x;
+      while (table_x < table_x_range.get_next ()
+            && !t.get_placement_at (table::coord_t (table_x,
+                                                    table_y_range.get_min ())))
+       table_x++;
+      const table::range_t unoccupied_x_range (start_table_x, table_x);
+      if (unoccupied_x_range.get_size () > 0)
+       t.set_cell_span (table::rect_t (unoccupied_x_range, table_y_range),
+                        styled_string (sm, "..."));
+      /* Skip occupied table cells.  */
+      while (table_x < table_x_range.get_next ()
+            && t.get_placement_at (table::coord_t (table_x,
+                                                   table_y_range.get_min ())))
+       table_x++;
+    }
+}
+
 /* Subclass of spatial_item for visualizing the region of memory
    that's valid to access relative to the base region of region accessed in
    the operation.  */
@@ -1140,14 +1289,23 @@ class valid_region_spatial_item : public spatial_item
 {
 public:
   valid_region_spatial_item (const access_operation &op,
-                            diagnostic_event_id_t region_creation_event_id)
+                            diagnostic_event_id_t region_creation_event_id,
+                            const theme &theme)
   : m_op (op),
-    m_region_creation_event_id (region_creation_event_id)
-  {}
+    m_region_creation_event_id (region_creation_event_id),
+    m_boundaries (nullptr),
+    m_existing_sval (op.m_model.get_store_value (op.m_base_region, nullptr)),
+    m_existing_sval_spatial_item
+      (make_existing_svalue_spatial_item (m_existing_sval,
+                                         op.get_valid_bits (),
+                                         theme))
+  {
+  }
 
   void add_boundaries (boundaries &out, logger *logger) const final override
   {
     LOG_SCOPE (logger);
+    m_boundaries = &out;
     access_range valid_bits = m_op.get_valid_bits ();
     if (logger)
       {
@@ -1158,6 +1316,18 @@ public:
       }
     out.add (valid_bits, boundaries::kind::HARD);
 
+    if (m_existing_sval_spatial_item)
+      {
+       if (logger)
+         {
+           logger->start_log_line ();
+           logger->log_partial ("existing svalue: ");
+           m_existing_sval->dump_to_pp (logger->get_printer (), true);
+           logger->end_log_line ();
+         }
+       m_existing_sval_spatial_item->add_boundaries (out, logger);
+      }
+
     /* Support for showing first and final element in array types.  */
     if (tree base_type = m_op.m_base_region->get_type ())
       if (TREE_CODE (base_type) == ARRAY_TYPE)
@@ -1193,65 +1363,102 @@ public:
   {
     tree base_type = m_op.m_base_region->get_type ();
     gcc_assert (TREE_CODE (base_type) == ARRAY_TYPE);
+    gcc_assert (m_boundaries != nullptr);
 
     tree domain = TYPE_DOMAIN (base_type);
     if (!(TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain)))
       return;
 
-    region_model_manager * const mgr = m_op.get_manager ();
     const int table_y = 0;
     const int table_h = 1;
     const table::range_t table_y_range (table_y, table_y + table_h);
 
     t.add_row ();
-    const svalue *min_idx_sval
-      = mgr->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain));
-    const region *min_element = mgr->get_element_region (m_op.m_base_region,
-                                                        TREE_TYPE (base_type),
-                                                        min_idx_sval);
-    const access_range min_element_range (*min_element, mgr);
-    const table::range_t min_element_x_range
-      = btm.get_table_x_for_range (min_element_range);
-
-    t.set_cell_span (table::rect_t (min_element_x_range,
-                                   table_y_range),
-                    fmt_styled_string (sm, "[%E]",
-                                       TYPE_MIN_VALUE (domain)));
-
-    const svalue *max_idx_sval
-      = mgr->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain));
-    const region *max_element = mgr->get_element_region (m_op.m_base_region,
+
+    const table::range_t min_x_range
+      = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
+                                       TYPE_MIN_VALUE (domain));
+    const table::range_t max_x_range
+      = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
+                                       TYPE_MAX_VALUE (domain));
+
+    if (TREE_TYPE (base_type) == char_type_node)
+      {
+       /* For a char array,: if there are any hard boundaries in
+          m_boundaries that are *within* the valid region,
+          then show those index values.  */
+       std::vector<region_offset> hard_boundaries
+         = m_boundaries->get_hard_boundaries_in_range
+             (tree_to_shwi (TYPE_MIN_VALUE (domain)),
+              tree_to_shwi (TYPE_MAX_VALUE (domain)));
+       for (auto &offset : hard_boundaries)
+         {
+           const int table_x = btm.get_table_x_for_offset (offset);
+           if (!offset.concrete_p ())
+             continue;
+           byte_offset_t byte;
+           if (!offset.get_concrete_byte_offset (&byte))
+             continue;
+           table::range_t table_x_range (table_x, table_x + 1);
+           t.maybe_set_cell_span (table::rect_t (table_x_range,
+                                                 table_y_range),
+                                  fmt_styled_string (sm, "[%wi]",
+                                                     byte.to_shwi ()));
+         }
+      }
+
+    add_ellipsis_to_gaps (t, sm,
+                         table::range_t (min_x_range.get_next (),
+                                         max_x_range.get_min ()),
+                         table_y_range);
+  }
+
+  table::range_t
+  maybe_add_array_index_to_table (table &t,
+                                 const bit_to_table_map &btm,
+                                 style_manager &sm,
+                                 const table::range_t table_y_range,
+                                 tree idx_cst) const
+  {
+    region_model_manager * const mgr = m_op.get_manager ();
+    tree base_type = m_op.m_base_region->get_type ();
+    const svalue *idx_sval
+      = mgr->get_or_create_constant_svalue (idx_cst);
+    const region *element_reg = mgr->get_element_region (m_op.m_base_region,
                                                         TREE_TYPE (base_type),
-                                                        max_idx_sval);
-    if (min_element == max_element)
-      return; // 1-element array
+                                                        idx_sval);
+    const access_range element_range (*element_reg, mgr);
+    const table::range_t element_x_range
+      = btm.get_table_x_for_range (element_range);
 
-    const access_range max_element_range (*max_element, mgr);
-    const table::range_t max_element_x_range
-      = btm.get_table_x_for_range (max_element_range);
-    t.set_cell_span (table::rect_t (max_element_x_range,
-                                   table_y_range),
-                    fmt_styled_string (sm, "[%E]",
-                                       TYPE_MAX_VALUE (domain)));
+    t.maybe_set_cell_span (table::rect_t (element_x_range,
+                                         table_y_range),
+                          fmt_styled_string (sm, "[%E]", idx_cst));
 
-    const table::range_t other_elements_x_range (min_element_x_range.next,
-                                                max_element_x_range.start);
-    if (other_elements_x_range.get_size () > 0)
-      t.set_cell_span (table::rect_t (other_elements_x_range, table_y_range),
-                      styled_string (sm, "..."));
+    return element_x_range;
   }
 
   table make_table (const bit_to_table_map &btm,
                    style_manager &sm) const final override
   {
-    table t (table::size_t (btm.get_num_columns (), 1));
+    table t (table::size_t (btm.get_num_columns (), 0));
 
     if (tree base_type = m_op.m_base_region->get_type ())
       if (TREE_CODE (base_type) == ARRAY_TYPE)
        add_array_elements_to_table (t, btm, sm);
 
+    /* Make use of m_existing_sval_spatial_item, if any.  */
+    if (m_existing_sval_spatial_item)
+      {
+       table table_for_existing
+         = m_existing_sval_spatial_item->make_table (btm, sm);
+       const int table_y = t.add_rows (table_for_existing.get_size ().h);
+       t.add_other_table (std::move (table_for_existing),
+                          table::coord_t (0, table_y));
+      }
+
     access_range valid_bits = m_op.get_valid_bits ();
-    const int table_y = t.get_size ().h - 1;
+    const int table_y = t.add_row ();
     const int table_h = 1;
     table::rect_t rect = btm.get_table_rect (valid_bits, table_y, table_h);
     styled_string s;
@@ -1306,6 +1513,9 @@ public:
 private:
   const access_operation &m_op;
   diagnostic_event_id_t m_region_creation_event_id;
+  mutable const boundaries *m_boundaries;
+  const svalue *m_existing_sval;
+  std::unique_ptr<spatial_item> m_existing_sval_spatial_item;
 };
 
 /* Subclass of spatial_item for visualizing the region of memory
@@ -1362,15 +1572,10 @@ private:
    to the accessed region.
    Can be subclassed to give visualizations of specific kinds of svalue.  */
 
-class svalue_spatial_item : public spatial_item
+class written_svalue_spatial_item : public spatial_item
 {
 public:
-  static std::unique_ptr<svalue_spatial_item> make (const access_operation &op,
-                                                   const svalue &sval,
-                                                   access_range actual_bits,
-                                                   const theme &theme);
-
-  svalue_spatial_item (const access_operation &op,
+  written_svalue_spatial_item (const access_operation &op,
                       const svalue &sval,
                       access_range actual_bits)
   : m_op (op), m_sval (sval), m_actual_bits (actual_bits)
@@ -1479,15 +1684,15 @@ protected:
      └──────────────────────────────────────────────────────────────────────┘
 */
 
-class string_region_spatial_item : public svalue_spatial_item
+class string_literal_spatial_item : public svalue_spatial_item
 {
 public:
-  string_region_spatial_item (const access_operation &op,
-                             const svalue &sval,
-                             access_range actual_bits,
-                             const string_region &string_reg,
-                             const theme &theme)
-  : svalue_spatial_item (op, sval, actual_bits),
+  string_literal_spatial_item (const svalue &sval,
+                              access_range actual_bits,
+                              const string_region &string_reg,
+                              const theme &theme,
+                              enum kind kind)
+  : svalue_spatial_item (sval, actual_bits, kind),
     m_string_reg (string_reg),
     m_theme (theme),
     m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold),
@@ -1501,16 +1706,18 @@ public:
   void add_boundaries (boundaries &out, logger *logger) const override
   {
     LOG_SCOPE (logger);
-    out.add (m_actual_bits, boundaries::kind::HARD);
+    out.add (m_bits, m_kind == svalue_spatial_item::kind::WRITTEN
+            ? boundaries::kind::HARD
+            : boundaries::kind::SOFT);
 
     tree string_cst = get_string_cst ();
     /* TREE_STRING_LENGTH is sizeof, not strlen.  */
     if (m_show_full_string)
-      out.add_all_bytes_in_range (m_actual_bits);
+      out.add_all_bytes_in_range (m_bits);
     else
       {
        byte_range bytes (0, 0);
-       bool valid = m_actual_bits.as_concrete_byte_range (&bytes);
+       bool valid = m_bits.as_concrete_byte_range (&bytes);
        gcc_assert (valid);
        byte_range head_of_string (bytes.get_start_byte_offset (),
                                   m_ellipsis_head_len);
@@ -1532,11 +1739,13 @@ public:
   {
     table t (table::size_t (btm.get_num_columns (), 0));
 
-    const int byte_idx_table_y = t.add_row ();
+    const int byte_idx_table_y = (m_kind == svalue_spatial_item::kind::WRITTEN
+                                 ? t.add_row ()
+                                 : -1);
     const int byte_val_table_y = t.add_row ();
 
     byte_range bytes (0, 0);
-    bool valid = m_actual_bits.as_concrete_byte_range (&bytes);
+    bool valid = m_bits.as_concrete_byte_range (&bytes);
     gcc_assert (valid);
     tree string_cst = get_string_cst ();
     if (m_show_full_string)
@@ -1616,14 +1825,17 @@ public:
                               byte_idx,
                               byte_idx_table_y, byte_val_table_y);
 
-       /* Ellipsis (two rows high).  */
+       /* Ellipsis.  */
        const byte_range ellipsis_bytes
          (m_ellipsis_head_len + bytes.get_start_byte_offset (),
           TREE_STRING_LENGTH (string_cst)
           - (m_ellipsis_head_len + m_ellipsis_tail_len));
        const table::rect_t table_rect
-         = btm.get_table_rect (&m_string_reg, ellipsis_bytes,
-                               byte_idx_table_y, 2);
+         = ((byte_idx_table_y != -1)
+            ? btm.get_table_rect (&m_string_reg, ellipsis_bytes,
+                                  byte_idx_table_y, 2)
+            : btm.get_table_rect (&m_string_reg, ellipsis_bytes,
+                                  byte_val_table_y, 1));
        t.set_cell_span(table_rect, styled_string (sm, "..."));
 
        /* Tail of string.  */
@@ -1637,12 +1849,15 @@ public:
                               byte_idx_table_y, byte_val_table_y);
       }
 
-    const int summary_table_y = t.add_row ();
-    t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes,
-                                        summary_table_y, 1),
-                    fmt_styled_string (sm,
-                                       _("string literal (type: %qT)"),
-                                       TREE_TYPE (string_cst)));
+    if (m_kind == svalue_spatial_item::kind::WRITTEN)
+      {
+       const int summary_table_y = t.add_row ();
+       t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes,
+                                            summary_table_y, 1),
+                        fmt_styled_string (sm,
+                                           _("string literal (type: %qT)"),
+                                           TREE_TYPE (string_cst)));
+      }
 
     return t;
   }
@@ -1687,7 +1902,7 @@ private:
     gcc_assert (byte_idx_within_string < TREE_STRING_LENGTH (string_cst));
 
     const byte_range bytes (byte_idx_within_cluster, 1);
-    if (1) // show_byte_indices
+    if (byte_idx_table_y != -1)
       {
        const table::rect_t idx_table_rect
          = btm.get_table_rect (&m_string_reg, bytes, byte_idx_table_y, 1);
@@ -1729,18 +1944,54 @@ private:
   const bool m_show_utf8;
 };
 
-std::unique_ptr<svalue_spatial_item>
-svalue_spatial_item::make (const access_operation &op,
-                          const svalue &sval,
-                          access_range actual_bits,
-                          const theme &theme)
+static std::unique_ptr<spatial_item>
+make_written_svalue_spatial_item (const access_operation &op,
+                                 const svalue &sval,
+                                 access_range actual_bits,
+                                 const theme &theme)
 {
   if (const initial_svalue *initial_sval = sval.dyn_cast_initial_svalue ())
     if (const string_region *string_reg
        = initial_sval->get_region ()->dyn_cast_string_region ())
-      return make_unique <string_region_spatial_item> (op, sval, actual_bits,
-                                                      *string_reg, theme);
-  return make_unique <svalue_spatial_item> (op, sval, actual_bits);
+      return make_unique <string_literal_spatial_item>
+       (sval, actual_bits,
+        *string_reg, theme,
+        svalue_spatial_item::kind::WRITTEN);
+  return make_unique <written_svalue_spatial_item> (op, sval, actual_bits);
+}
+
+static std::unique_ptr<spatial_item>
+make_existing_svalue_spatial_item (const svalue *sval,
+                                  const access_range &bits,
+                                  const theme &theme)
+{
+  if (!sval)
+    return nullptr;
+
+  switch (sval->get_kind ())
+    {
+    default:
+      return nullptr;
+
+    case SK_INITIAL:
+      {
+       const initial_svalue *initial_sval = (const initial_svalue *)sval;
+       if (const string_region *string_reg
+           = initial_sval->get_region ()->dyn_cast_string_region ())
+         return make_unique <string_literal_spatial_item>
+           (*sval, bits,
+            *string_reg, theme,
+            svalue_spatial_item::kind::EXISTING);
+       return nullptr;
+      }
+
+    case SK_COMPOUND:
+      return make_unique<compound_svalue_spatial_item>
+       (*((const compound_svalue *)sval),
+        bits,
+        svalue_spatial_item::kind::EXISTING,
+        theme);
+    }
 }
 
 /* Widget subclass implementing access diagrams.  */
@@ -1759,7 +2010,7 @@ public:
     m_theme (theme),
     m_logger (logger),
     m_invalid (false),
-    m_valid_region_spatial_item (op, region_creation_event_id),
+    m_valid_region_spatial_item (op, region_creation_event_id, theme),
     m_accessed_region_spatial_item (op),
     m_btm (),
     m_calc_req_size_called (false)
@@ -1800,10 +2051,11 @@ public:
     if (op.m_sval_hint)
       {
        access_range actual_bits = m_op.get_actual_bits ();
-       m_svalue_spatial_item = svalue_spatial_item::make (m_op,
-                                                          *op.m_sval_hint,
-                                                          actual_bits,
-                                                          m_theme);
+       m_written_svalue_spatial_item
+         = make_written_svalue_spatial_item (m_op,
+                                             *op.m_sval_hint,
+                                             actual_bits,
+                                             m_theme);
       }
 
     /* Two passes:
@@ -1856,9 +2108,9 @@ public:
        add_aligned_child_table (std::move (t_headings));
       }
 
-    if (m_svalue_spatial_item)
+    if (m_written_svalue_spatial_item)
       {
-       table t_sval (m_svalue_spatial_item->make_table (m_btm, m_sm));
+       table t_sval (m_written_svalue_spatial_item->make_table (m_btm, m_sm));
        add_aligned_child_table (std::move (t_sval));
       }
     else
@@ -1942,12 +2194,12 @@ private:
   find_boundaries () const
   {
     std::unique_ptr<boundaries> result
-      = make_unique<boundaries> (*m_op.m_base_region);
+      = make_unique<boundaries> (*m_op.m_base_region, m_logger);
 
     m_valid_region_spatial_item.add_boundaries (*result, m_logger);
     m_accessed_region_spatial_item.add_boundaries (*result, m_logger);
-    if (m_svalue_spatial_item)
-      m_svalue_spatial_item->add_boundaries (*result, m_logger);
+    if (m_written_svalue_spatial_item)
+      m_written_svalue_spatial_item->add_boundaries (*result, m_logger);
 
     return result;
   }
@@ -2324,7 +2576,7 @@ private:
 
   valid_region_spatial_item m_valid_region_spatial_item;
   accessed_region_spatial_item m_accessed_region_spatial_item;
-  std::unique_ptr<svalue_spatial_item> m_svalue_spatial_item;
+  std::unique_ptr<spatial_item> m_written_svalue_spatial_item;
 
   std::unique_ptr<boundaries> m_boundaries;
 
diff --git 
a/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c 
b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c
new file mode 100644
index 00000000000..b129518d759
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c
@@ -0,0 +1,74 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+/* { dg-skip-if "" { powerpc-ibm-aix* } } */
+
+#include <string.h>
+
+#define LOREM_IPSUM \
+  "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod" \
+  " tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim" \
+  " veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea" \
+  " commodo consequat. Duis aute irure dolor in reprehenderit in voluptate" \
+  " velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint" \
+  " occaecat cupidatat non proident, sunt in culpa qui officia deserunt" \
+  " mollit anim id est laborum."
+
+void test (void)
+{
+  char buf[500];
+  strcpy (buf, LOREM_IPSUM);
+  strcat (buf, LOREM_IPSUM); /* { dg-warning "stack-based buffer overflow" } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+                                                   
┌─────┬───┬───┬───┬───┬───┬────────┬─────┬─────┬─────┬─────┬─────┬─────┐
+                                                   │ [0] │[1]│[2]│[3]│[4]│[5]│ 
       │[440]│[441]│[442]│[443]│[444]│[445]│
+                                                   ├─────┼───┼───┼───┼───┼───┤ 
 ...   ├─────┼─────┼─────┼─────┼─────┼─────┤
+                                                   │ 'L' │'o'│'r'│'e'│'m'│' '│ 
       │ 'o' │ 'r' │ 'u' │ 'm' │ '.' │ NUL │
+                                                   
├─────┴───┴───┴───┴───┴───┴────────┴─────┴─────┴─────┴─────┴─────┴─────┤
+                                                   │                  string 
literal (type: 'char[446]')                  │
+                                                   
└──────────────────────────────────────────────────────────────────────┘
+                                                      │    │   │   │   │   │ │ 
  │   │   │     │     │     │     │     │
+                                                      │    │   │   │   │   │ │ 
  │   │   │     │     │     │     │     │
+                                                      v    v   v   v   v   v v 
  v   v   v     v     v     v     v     v
+  
┌───┬────────────────────────────────────────────┬─────┬────────────────────┬─────┐┌────────────────────────────────────┐
+  │[0]│                    ...                     │[445]│        ...         
│[499]││                                    │
+  
├───┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬┼─────┼────────────────────┴─────┘│
                                    │
+  │'L'│'o'│'r'│'e'│'m'│' '│...│'o'│'r'│'u'│'m'│'.'││ NUL │                     
      │         after valid range          │
+  
├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴┴─────┴──────────────────────────┐│
                                    │
+  │                            'buf' (type: 'char[500]')                       
     ││                                    │
+  
└─────────────────────────────────────────────────────────────────────────────────┘└────────────────────────────────────┘
+  
├────────────────────────────────────────┬────────────────────────────────────────┤├─────────────────┬──────────────────┤
+                                           │                                   
                        │
+                                 ╭─────────┴─────────╮                         
             ╭──────────┴──────────╮
+                                 │capacity: 500 bytes│                         
             │overflow of 391 bytes│
+                                 ╰───────────────────╯                         
             ╰─────────────────────╯
+
+   { dg-end-multiline-output "" { target c } } */
+
+/* { dg-begin-multiline-output "" }
+
+                                                   
┌─────┬───┬───┬───┬───┬───┬────────┬─────┬─────┬─────┬─────┬─────┬─────┐
+                                                   │ [0] │[1]│[2]│[3]│[4]│[5]│ 
       │[440]│[441]│[442]│[443]│[444]│[445]│
+                                                   ├─────┼───┼───┼───┼───┼───┤ 
 ...   ├─────┼─────┼─────┼─────┼─────┼─────┤
+                                                   │ 'L' │'o'│'r'│'e'│'m'│' '│ 
       │ 'o' │ 'r' │ 'u' │ 'm' │ '.' │ NUL │
+                                                   
├─────┴───┴───┴───┴───┴───┴────────┴─────┴─────┴─────┴─────┴─────┴─────┤
+                                                   │               string 
literal (type: 'const char[446]')               │
+                                                   
└──────────────────────────────────────────────────────────────────────┘
+                                                      │    │   │   │   │   │ │ 
  │   │   │     │     │     │     │     │
+                                                      │    │   │   │   │   │ │ 
  │   │   │     │     │     │     │     │
+                                                      v    v   v   v   v   v v 
  v   v   v     v     v     v     v     v
+  
┌───┬────────────────────────────────────────────┬─────┬────────────────────┬─────┐┌────────────────────────────────────┐
+  │[0]│                    ...                     │[445]│        ...         
│[499]││                                    │
+  
├───┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬┼─────┼────────────────────┴─────┘│
                                    │
+  │'L'│'o'│'r'│'e'│'m'│' '│...│'o'│'r'│'u'│'m'│'.'││ NUL │                     
      │         after valid range          │
+  
├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴┴─────┴──────────────────────────┐│
                                    │
+  │                      'char buf [500]' (type: 'char[500]')                  
     ││                                    │
+  
└─────────────────────────────────────────────────────────────────────────────────┘└────────────────────────────────────┘
+  
├────────────────────────────────────────┬────────────────────────────────────────┤├─────────────────┬──────────────────┤
+                                           │                                   
                        │
+                                 ╭─────────┴─────────╮                         
             ╭──────────┴──────────╮
+                                 │capacity: 500 bytes│                         
             │overflow of 391 bytes│
+                                 ╰───────────────────╯                         
             ╰─────────────────────╯
+
+   { dg-end-multiline-output "" { target c++ } } */
diff --git a/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat.c 
b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat.c
new file mode 100644
index 00000000000..53c128a4fa1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat.c
@@ -0,0 +1,66 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode 
-Wno-stringop-overflow" } */
+/* { dg-skip-if "" { powerpc-ibm-aix* } } */
+
+#include <string.h>
+
+void test (void)
+{
+  char buf[10];
+  strcpy (buf, "foo");
+  strcat (buf, " bar");
+  strcat (buf, " baz!"); /* { dg-warning "stack-based buffer overflow" } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+                                      ┌────┬────┬────┐ ┌────┬────┬───────┐
+                                      │[0] │[1] │[2] │ │[3] │[4] │  [5]  │
+                                      ├────┼────┼────┤ ├────┼────┼───────┤
+                                      │' ' │'b' │'a' │ │'z' │'!' │  NUL  │
+                                      ├────┴────┴────┴─┴────┴────┴───────┤
+                                      │ string literal (type: 'char[6]') │
+                                      └──────────────────────────────────┘
+                                        │    │    │      │    │      │
+                                        │    │    │      │    │      │
+                                        v    v    v      v    v      v
+  ┌─────┬─────────────────────────────┬────┬────┬────┐ ┌─────────────────┐
+  │ [0] │             ...             │[7] │... │[9] │ │                 │
+  └─────┴────────┬────┬────┬────┬────┬┼────┼────┴────┘ │                 │
+                 │' ' │'b' │'a' │'r' ││NUL │           │after valid range│
+  ┌──────────────┴────┴────┴────┴────┴┴────┴─────────┐ │                 │
+  │             'buf' (type: 'char[10]')             │ │                 │
+  └──────────────────────────────────────────────────┘ └─────────────────┘
+  ├────────────────────────┬─────────────────────────┤ ├────────┬────────┤
+                           │                                    │
+                 ╭─────────┴────────╮                 ╭─────────┴─────────╮
+                 │capacity: 10 bytes│                 │overflow of 3 bytes│
+                 ╰──────────────────╯                 ╰───────────────────╯
+
+   { dg-end-multiline-output "" { target c } } */
+
+/* { dg-begin-multiline-output "" }
+
+                                  ┌─────┬─────┬─────┐  ┌─────┬─────┬─────┐
+                                  │ [0] │ [1] │ [2] │  │ [3] │ [4] │ [5] │
+                                  ├─────┼─────┼─────┤  ├─────┼─────┼─────┤
+                                  │ ' ' │ 'b' │ 'a' │  │ 'z' │ '!' │ NUL │
+                                  ├─────┴─────┴─────┴──┴─────┴─────┴─────┤
+                                  │string literal (type: 'const char[6]')│
+                                  └──────────────────────────────────────┘
+                                     │     │     │        │     │     │
+                                     │     │     │        │     │     │
+                                     v     v     v        v     v     v
+  ┌────┬──────────────────────────┬─────┬─────┬─────┐  ┌─────────────────┐
+  │[0] │           ...            │ [7] │ ... │ [9] │  │                 │
+  └────┴───────┬────┬────┬───┬───┬┼─────┼─────┴─────┘  │                 │
+               │' ' │'b' │'a'│'r'││ NUL │              │after valid range│
+  ┌────────────┴────┴────┴───┴───┴┴─────┴───────────┐  │                 │
+  │       'char buf [10]' (type: 'char[10]')        │  │                 │
+  └─────────────────────────────────────────────────┘  └─────────────────┘
+  ├────────────────────────┬────────────────────────┤  ├────────┬────────┤
+                           │                                    │
+                 ╭─────────┴────────╮                 ╭─────────┴─────────╮
+                 │capacity: 10 bytes│                 │overflow of 3 bytes│
+                 ╰──────────────────╯                 ╰───────────────────╯
+
+   { dg-end-multiline-output "" { target c++ } } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-17.c 
b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-17.c
index 6920e8c776f..d46159e56d6 100644
--- a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-17.c
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-17.c
@@ -11,19 +11,21 @@ void test (void)
 }
 
 /* { dg-begin-multiline-output "" }
-                           ┌─────┬─────┬────┬────┬────┐┌─────┬─────┬─────┐
-                           │ [0] │ [1] │[2] │[3] │[4] ││ [5] │ [6] │ [7] │
-                           ├─────┼─────┼────┼────┼────┤├─────┼─────┼─────┤
-                           │ ' ' │ 'w' │'o' │'r' │'l' ││ 'd' │ '!' │ NUL │
-                           ├─────┴─────┴────┴────┴────┴┴─────┴─────┴─────┤
-                           │      string literal (type: 'char[8]')       │
-                           └─────────────────────────────────────────────┘
-                              │     │    │    │    │      │     │     │
-                              │     │    │    │    │      │     │     │
-                              v     v    v    v    v      v     v     v
-  ┌─────┬────────────────────────────────────────┬────┐┌─────────────────┐
-  │ [0] │                  ...                   │[9] ││                 │
-  ├─────┴────────────────────────────────────────┴────┤│after valid range│
+                             ┌────┬────┬────┬────┬────┐┌─────┬─────┬─────┐
+                             │[0] │[1] │[2] │[3] │[4] ││ [5] │ [6] │ [7] │
+                             ├────┼────┼────┼────┼────┤├─────┼─────┼─────┤
+                             │' ' │'w' │'o' │'r' │'l' ││ 'd' │ '!' │ NUL │
+                             ├────┴────┴────┴────┴────┴┴─────┴─────┴─────┤
+                             │     string literal (type: 'char[8]')      │
+                             └───────────────────────────────────────────┘
+                               │    │    │    │    │      │     │     │
+                               │    │    │    │    │      │     │     │
+                               v    v    v    v    v      v     v     v
+  ┌─────┬────────────────────┬────┬──────────────┬────┐┌─────────────────┐
+  │ [0] │        ...         │[5] │     ...      │[9] ││                 │
+  ├─────┼────┬────┬────┬────┬┼────┼──────────────┴────┘│                 │
+  │ 'h' │'e' │'l' │'l' │'o' ││NUL │                    │after valid range│
+  ├─────┴────┴────┴────┴────┴┴────┴───────────────────┐│                 │
   │             'buf' (type: 'char[10]')              ││                 │
   └───────────────────────────────────────────────────┘└─────────────────┘
   ├─────────────────────────┬─────────────────────────┤├────────┬────────┤
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-18.c 
b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-18.c
index ea0b88019cd..f54cd80c338 100644
--- a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-18.c
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-18.c
@@ -11,28 +11,34 @@ void test (void)
 }
 
 /* { dg-begin-multiline-output "" }
-                             ┌─────┬─────────┐┌────┬────┬────┬────┬──────┐
-                             │ [0] │   [1]   ││[2] │[3] │[4] │[5] │ [6]  │
-                             ├─────┼─────────┤├────┼────┼────┼────┼──────┤
-                             │0xe3 │  0x83   ││0xa1│0xe3│0x82│0xa4│ 0x00 │
-                             ├─────┴─────────┴┴────┼────┴────┴────┼──────┤
-                             │       U+30e1        │    U+30a4    │U+0000│
-                             ├─────────────────────┼──────────────┼──────┤
-                             │         メ          │      イ      │ NUL  │
-                             ├─────────────────────┴──────────────┴──────┤
-                             │     string literal (type: 'char[7]')      │
-                             └───────────────────────────────────────────┘
-                                │       │       │    │    │    │     │
-                                │       │       │    │    │    │     │
-                                v       v       v    v    v    v     v
-  ┌────┬───────────────────────────┬─────────┐┌──────────────────────────┐
-  │[0] │            ...            │  [10]   ││                          │
-  ├────┴───────────────────────────┴─────────┤│    after valid range     │
-  │         'buf' (type: 'char[11]')         ││                          │
-  └──────────────────────────────────────────┘└──────────────────────────┘
-  ├────────────────────┬─────────────────────┤├────────────┬─────────────┤
-                       │                                   │
-             ╭─────────┴────────╮                ╭─────────┴─────────╮
-             │capacity: 11 bytes│                │overflow of 5 bytes│
-             ╰──────────────────╯                ╰───────────────────╯
+                                                
┌──────┬────┐┌────┬────┬────┬────┬──────┐
+                                                │ [0]  │[1] ││[2] │[3] │[4] 
│[5] │ [6]  │
+                                                
├──────┼────┤├────┼────┼────┼────┼──────┤
+                                                │ 0xe3 
│0x83││0xa1│0xe3│0x82│0xa4│ 0x00 │
+                                                
├──────┴────┴┴────┼────┴────┴────┼──────┤
+                                                │     U+30e1      │    U+30a4  
  │U+0000│
+                                                
├─────────────────┼──────────────┼──────┤
+                                                │       メ        │      イ      
│ NUL  │
+                                                
├─────────────────┴──────────────┴──────┤
+                                                │   string literal (type: 
'char[7]')    │
+                                                
└───────────────────────────────────────┘
+                                                   │     │     │    │    │    
│     │
+                                                   │     │     │    │    │    
│     │
+                                                   v     v     v    v    v    
v     v
+  
┌────┬────────────────────────────────────────┬──────┬────┐┌──────────────────────────┐
+  │[0] │                  ...                   │ [9]  │[10]││                 
         │
+  ├────┼────┬────┬────┬────┬────┬────┬────┬────┬┼──────┼────┘│                 
         │
+  │0xe3│0x82│0xb5│0xe3│0x83│0x84│0xe3│0x82│0xad││ 0x00 │     │                 
         │
+  ├────┴────┴────┼────┴────┴────┼────┴────┴────┤├──────┤     │                 
         │
+  │    U+30b5    │    U+30c4    │    U+30ad    ││U+0000│     │    after valid 
range     │
+  ├──────────────┼──────────────┼──────────────┤├──────┤     │                 
         │
+  │      サ      │      ツ      │      キ      ││ NUL  │     │                    
      │
+  ├──────────────┴──────────────┴──────────────┴┴──────┴────┐│                 
         │
+  │                'buf' (type: 'char[11]')                 ││                 
         │
+  
└─────────────────────────────────────────────────────────┘└──────────────────────────┘
+  
├────────────────────────────┬────────────────────────────┤├────────────┬─────────────┤
+                               │                                          │
+                     ╭─────────┴────────╮                       
╭─────────┴─────────╮
+                     │capacity: 11 bytes│                       │overflow of 5 
bytes│
+                     ╰──────────────────╯                       
╰───────────────────╯
    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c 
b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c
index 35ab72b6efc..6af5c0f301b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c
@@ -22,24 +22,26 @@ test_long_string ()
 }
 
 /* { dg-begin-multiline-output "" }
-        ┌───┬───┬───┬───┬───┬───┬───────┬─────┬─────┬─────┬─────┬─────┬─────┐
-        │[0]│[1]│[2]│[3]│[4]│[5]│       │[440]│[441]│[442]│[443]│[444]│[445]│
-        ├───┼───┼───┼───┼───┼───┤  ...  ├─────┼─────┼─────┼─────┼─────┼─────┤
-        │'L'│'o'│'r'│'e'│'m'│' '│       │ 'o' │ 'r' │ 'u' │ 'm' │ '.' │ NUL │
-        ├───┴───┴───┴───┴───┴───┴───────┴─────┴─────┴─────┴─────┴─────┴─────┤
-        │                string literal (type: 'char[446]')                 │
-        └───────────────────────────────────────────────────────────────────┘
-          │   │   │   │   │   │ │  │   │   │     │     │     │     │     │
-          │   │   │   │   │   │ │  │   │   │     │     │     │     │     │
-          v   v   v   v   v   v v  v   v   v     v     v     v     v     v
-  ┌───┬──────────────────────────┬────┐┌────────────────────────────────────┐
-  │[0]│           ...            │[99]││                                    │
-  ├───┴──────────────────────────┴────┤│         after valid range          │
-  │     'buf' (type: 'char[100]')     ││                                    │
-  └───────────────────────────────────┘└────────────────────────────────────┘
-  ├─────────────────┬─────────────────┤├─────────────────┬──────────────────┤
-                    │                                    │
-          ╭─────────┴─────────╮               ╭──────────┴──────────╮
-          │capacity: 100 bytes│               │overflow of 350 bytes│
-          ╰───────────────────╯               ╰─────────────────────╯
+                   
┌───┬───┬───┬───┬───┬───┬───────┬─────┬─────┬─────┬─────┬─────┬─────┐
+                   │[0]│[1]│[2]│[3]│[4]│[5]│       
│[440]│[441]│[442]│[443]│[444]│[445]│
+                   ├───┼───┼───┼───┼───┼───┤  ...  
├─────┼─────┼─────┼─────┼─────┼─────┤
+                   │'L'│'o'│'r'│'e'│'m'│' '│       │ 'o' │ 'r' │ 'u' │ 'm' │ 
'.' │ NUL │
+                   
├───┴───┴───┴───┴───┴───┴───────┴─────┴─────┴─────┴─────┴─────┴─────┤
+                   │                string literal (type: 'char[446]')         
        │
+                   
└───────────────────────────────────────────────────────────────────┘
+                     │   │   │   │   │   │ │  │   │   │     │     │     │     
│     │
+                     │   │   │   │   │   │ │  │   │   │     │     │     │     
│     │
+                     v   v   v   v   v   v v  v   v   v     v     v     v     
v     v
+  
┌───┬────────────┬───┬────────────────────┬────┐┌────────────────────────────────────┐
+  │[0]│    ...     │[4]│        ...         │[99]││                            
        │
+  ├───┼───┬───┬───┬┼───┼────────────────────┴────┘│                            
        │
+  │'a'│'b'│'c'│' '││NUL│                          │         after valid range  
        │
+  ├───┴───┴───┴───┴┴───┴─────────────────────────┐│                            
        │
+  │          'buf' (type: 'char[100]')           ││                            
        │
+  
└──────────────────────────────────────────────┘└────────────────────────────────────┘
+  
├──────────────────────┬───────────────────────┤├─────────────────┬──────────────────┤
+                         │                                          │
+               ╭─────────┴─────────╮                     
╭──────────┴──────────╮
+               │capacity: 100 bytes│                     │overflow of 350 
bytes│
+               ╰───────────────────╯                     
╰─────────────────────╯
    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c 
b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c
index 25bf9d53b2b..ad320964057 100644
--- a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c
@@ -33,22 +33,24 @@ test_bad_memcpy ()
 
 /* { dg-begin-multiline-output "" }
 
-  ┌─────────────────────────────────────────────────────────────────────────┐
-  │                           read of 4096 bytes                            │
-  └─────────────────────────────────────────────────────────────────────────┘
-           ^               ^          ^           ^                ^
-           │               │          │           │                │
-           │               │          │           │                │
-  ┌──────────────────┐┌──────────┬──────────┬────────────┐┌─────────────────┐
-  │                  ││   [0]    │   ...    │   [445]    ││                 │
-  │before valid range│├──────────┴──────────┴────────────┤│after valid range│
-  │                  ││string literal (type: 'char[446]')││                 │
-  └──────────────────┘└──────────────────────────────────┘└─────────────────┘
-  ├────────┬─────────┤├────────────────┬─────────────────┤├────────┬────────┤
-           │                           │                           │
-  ╭────────┴──────────────╮    ╭───────┴───────╮       
╭───────────┴───────────╮
-  │under-read of 100 bytes│    │size: 446 bytes│       │over-read of 3550 
bytes│
-  ╰───────────────────────╯    ╰───────────────╯       
╰───────────────────────╯
+  
┌────────────────────────────────────────────────────────────────────────────────────────────┐
+  │                                     read of 4096 bytes                     
                │
+  
└────────────────────────────────────────────────────────────────────────────────────────────┘
+           ^            ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   ^    ^     
       ^
+           │            │   │   │   │   │   │   │   │   │   │   │   │    │     
       │
+           │            │   │   │   │   │   │   │   │   │   │   │   │    │     
       │
+  
┌──────────────────┐┌───┬───────────────────────────────────────────┬─────┐┌─────────────────┐
+  │                  ││[0]│                    ...                    │[445]││ 
                │
+  │                  │├───┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┼─────┤│ 
                │
+  │before valid range││'L'│'o'│'r'│'e'│'m'│' '│...│'o'│'r'│'u'│'m'│'.'│ NUL 
││after valid range│
+  │                  │├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴─────┤│ 
                │
+  │                  ││         string literal (type: 'char[446]')          ││ 
                │
+  
└──────────────────┘└─────────────────────────────────────────────────────┘└─────────────────┘
+  
├────────┬─────────┤├──────────────────────────┬──────────────────────────┤├────────┬────────┤
+           │                                     │                             
       │
+  ╭────────┴──────────────╮              ╭───────┴───────╮                
╭───────────┴───────────╮
+  │under-read of 100 bytes│              │size: 446 bytes│                
│over-read of 3550 bytes│
+  ╰───────────────────────╯              ╰───────────────╯                
╰───────────────────────╯
 
    { dg-end-multiline-output "" } */
 
@@ -81,22 +83,24 @@ test_bad_memcpy ()
 
 /* { dg-begin-multiline-output "" }
 
-  ┌─────────────────────────────────────────────────────────────────────────┐
-  │                           read of 4096 bytes                            │
-  └─────────────────────────────────────────────────────────────────────────┘
-           ^               ^          ^           ^                ^
-           │               │          │           │                │
-           │               │          │           │                │
-  ┌──────────────────┐┌──────────┬──────────┬────────────┐┌─────────────────┐
-  │                  ││   [0]    │   ...    │   [445]    ││                 │
-  │before valid range│├──────────┴──────────┴────────────┤│after valid range│
-  │                  ││string literal (type: 'char[446]')││                 │
-  └──────────────────┘└──────────────────────────────────┘└─────────────────┘
-  ├────────┬─────────┤├────────────────┬─────────────────┤├────────┬────────┤
-           │                           │                           │
-  ╭────────┴──────────────╮    ╭───────┴───────╮       
╭───────────┴───────────╮
-  │under-read of 100 bytes│    │size: 446 bytes│       │over-read of 3550 
bytes│
-  ╰───────────────────────╯    ╰───────────────╯       
╰───────────────────────╯
+  
┌────────────────────────────────────────────────────────────────────────────────────────────┐
+  │                                     read of 4096 bytes                     
                │
+  
└────────────────────────────────────────────────────────────────────────────────────────────┘
+           ^            ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   ^    ^     
       ^
+           │            │   │   │   │   │   │   │   │   │   │   │   │    │     
       │
+           │            │   │   │   │   │   │   │   │   │   │   │   │    │     
       │
+  
┌──────────────────┐┌───┬───────────────────────────────────────────┬─────┐┌─────────────────┐
+  │                  ││[0]│                    ...                    │[445]││ 
                │
+  │                  │├───┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┼─────┤│ 
                │
+  │before valid range││'L'│'o'│'r'│'e'│'m'│' '│...│'o'│'r'│'u'│'m'│'.'│ NUL 
││after valid range│
+  │                  │├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴─────┤│ 
                │
+  │                  ││         string literal (type: 'char[446]')          ││ 
                │
+  
└──────────────────┘└─────────────────────────────────────────────────────┘└─────────────────┘
+  
├────────┬─────────┤├──────────────────────────┬──────────────────────────┤├────────┬────────┤
+           │                                     │                             
       │
+  ╭────────┴──────────────╮              ╭───────┴───────╮                
╭───────────┴───────────╮
+  │under-read of 100 bytes│              │size: 446 bytes│                
│over-read of 3550 bytes│
+  ╰───────────────────────╯              ╰───────────────╯                
╰───────────────────────╯
 
    { dg-end-multiline-output "" } */
 
diff --git a/gcc/text-art/table.cc b/gcc/text-art/table.cc
index 2f857a0e2a7..2ea0da3a3d8 100644
--- a/gcc/text-art/table.cc
+++ b/gcc/text-art/table.cc
@@ -150,6 +150,26 @@ table::set_cell_span (rect_t span,
       }
 }
 
+/* If SPAN is unoccuped, set it to CONTENT.
+   Otherwise, discard CONTENT.  */
+
+void
+table::maybe_set_cell_span (rect_t span,
+                           table_cell_content &&content,
+                           enum x_align x_align,
+                           enum y_align y_align)
+{
+  gcc_assert (span.m_size.w > 0);
+  gcc_assert (span.m_size.h > 0);
+  for (int y = span.get_min_y (); y < span.get_next_y (); y++)
+    for (int x = span.get_min_x (); x < span.get_next_x (); x++)
+      {
+       if (m_occupancy.get (coord_t (x, y)) != -1)
+         return;
+      }
+  set_cell_span (span, std::move (content), x_align, y_align);
+}
+
 canvas
 table::to_canvas (const theme &theme, const style_manager &sm) const
 {
@@ -189,6 +209,21 @@ table::debug () const
   canvas.debug (false);
 }
 
+/* Move OTHER's content this table, starting at OFFSET.  */
+
+void
+table::add_other_table (table &&other,
+                       table::coord_t offset)
+{
+  for (auto &&placement : other.m_placements)
+    {
+      set_cell_span (placement.m_rect + offset,
+                    std::move (placement.m_content),
+                    placement.m_x_align,
+                    placement.m_y_align);
+    }
+}
+
 const table::cell_placement *
 table::get_placement_at (coord_t coord) const
 {
diff --git a/gcc/text-art/table.h b/gcc/text-art/table.h
index 17eda912f1a..5d5d4bdde1f 100644
--- a/gcc/text-art/table.h
+++ b/gcc/text-art/table.h
@@ -115,6 +115,7 @@ class table
     const table_cell_content &get_content () const { return m_content; }
 
   private:
+    friend class table;
     friend class table_cell_sizes;
     rect_t m_rect;
     table_cell_content m_content;
@@ -130,11 +131,18 @@ class table
 
   const size_t &get_size () const { return m_size; }
 
+  int add_rows (unsigned num)
+  {
+    int topmost_new_row = m_size.h;
+    m_size.h += num;
+    for (unsigned i = 0; i < num; i++)
+      m_occupancy.add_row (-1);
+    return topmost_new_row;
+  }
+
   int add_row ()
   {
-    m_size.h++;
-    m_occupancy.add_row (-1);
-    return m_size.h - 1; // return the table_y of the newly-added row
+    return add_rows (1);
   }
 
   void set_cell (coord_t coord,
@@ -147,6 +155,11 @@ class table
                      enum x_align x_align = x_align::CENTER,
                      enum y_align y_align = y_align::CENTER);
 
+  void maybe_set_cell_span (rect_t span,
+                           table_cell_content &&content,
+                           enum x_align x_align = x_align::CENTER,
+                           enum y_align y_align = y_align::CENTER);
+
   canvas to_canvas (const theme &theme, const style_manager &sm) const;
 
   void paint_to_canvas(canvas &canvas,
@@ -156,6 +169,8 @@ class table
 
   void debug () const;
 
+  void add_other_table (table &&other, table::coord_t offset);
+
   /* Self-test support.  */
   const cell_placement *get_placement_at (coord_t coord) const;
 
diff --git a/gcc/text-art/types.h b/gcc/text-art/types.h
index ea4ff4be8cc..d5394a92d6f 100644
--- a/gcc/text-art/types.h
+++ b/gcc/text-art/types.h
@@ -129,6 +129,13 @@ struct rect
   size<CoordinateSystem> m_size;
 };
 
+template <typename CoordinateSystem>
+rect<CoordinateSystem> operator+ (rect<CoordinateSystem> r,
+                                 coord<CoordinateSystem> offset)
+{
+  return rect<CoordinateSystem> (r.m_top_left + offset, r.m_size);
+}
+
 template <typename ElementType, typename SizeType, typename CoordType>
 class array2
 {
-- 
2.26.3

Reply via email to