public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/3] Introduce gdb::array_view
  2017-08-21 19:27 [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector Pedro Alves
@ 2017-08-21 19:27 ` Pedro Alves
  2017-09-04 16:19   ` Pedro Alves
  2017-08-21 19:28 ` [PATCH 3/3] Kill init_sal Pedro Alves
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Pedro Alves @ 2017-08-21 19:27 UTC (permalink / raw)
  To: gdb-patches

An array_view is an abstraction that provides a non-owning view over a
sequence of contiguous objects.

A way to put it is that array_view is to std::vector (and std::array
and built-in arrays with rank==1) like std::string_view is to
std::string.

The main intent of array_view is to use it as function input parameter
type, making it possible to pass in any sequence of contiguous
objects, irrespective of whether the objects live on the stack or heap
and what actual container owns them.  Implicit construction from the
element type is supported too, making it easy to call functions that
expect an array of elements when you only have one element (usually on
the stack).  For example:

 struct A { .... };
 void function (gdb::array_view<A> as);

 std::vector<A> std_vec = ...;
 std::array<A, N> std_array = ...;
 A array[] = {...};
 A elem;

 function (std_vec);
 function (std_array);
 function (array);
 function (elem);

Views can be either mutable or const.  A const view is simply created
by specifying a const T as array_view template parameter, in which
case operator[] of non-const array_view objects ends up returning
const references.  (Making the array_view itself const is analogous to
making a pointer itself be const.  I.e., disables re-seating the
view/pointer.)  Normally functions will pass around array_views by
value.

Uses of gdb::array_view (other than the ones in the unit tests) will
be added in a follow up patch.

gdb/ChangeLog
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
	unittests/array-view-selftests.c.
	(SUBDIR_UNITTESTS_OBS): Add array-view-selftests.o.
	* common/array-view.h: New file.
	* unittests/array-view-selftests.c: New file.
---
 gdb/Makefile.in                      |   2 +
 gdb/common/array-view.h              | 179 +++++++++++++
 gdb/unittests/array-view-selftests.c | 489 +++++++++++++++++++++++++++++++++++
 3 files changed, 670 insertions(+)
 create mode 100644 gdb/common/array-view.h
 create mode 100644 gdb/unittests/array-view-selftests.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 85de646..d9094fd 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -526,6 +526,7 @@ SUBDIR_PYTHON_LDFLAGS =
 SUBDIR_PYTHON_CFLAGS =
 
 SUBDIR_UNITTESTS_SRCS = \
+	unittests/array-view-selftests.c \
 	unittests/environ-selftests.c \
 	unittests/function-view-selftests.c \
 	unittests/offset-type-selftests.c \
@@ -534,6 +535,7 @@ SUBDIR_UNITTESTS_SRCS = \
 	unittests/scoped_restore-selftests.c
 
 SUBDIR_UNITTESTS_OBS = \
+	array-view-selftests.o \
 	environ-selftests.o \
 	function-view-selftests.o \
 	offset-type-selftests.o \
diff --git a/gdb/common/array-view.h b/gdb/common/array-view.h
new file mode 100644
index 0000000..f12d1e83
--- /dev/null
+++ b/gdb/common/array-view.h
@@ -0,0 +1,179 @@
+/* Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef COMMON_ARRAY_VIEW_H
+#define COMMON_ARRAY_VIEW_H
+
+#include "traits.h"
+#include <type_traits>
+
+/* An array_view is an abstraction that provides a non-owning view
+   over a sequence of contiguous objects.
+
+   A way to put it is that array_view is to std::vector (and
+   std::array and built-in arrays with rank==1) like std::string_view
+   is to std::string.
+
+   The main intent of array_view is to use it as function input
+   parameter type, making it possible to pass in any sequence of
+   contiguous objects, irrespective of whether the objects live on the
+   stack or heap and what actual container owns them.  Implicit
+   construction from the element type is supported too, making it easy
+   to call functions that expect an array of elements when you only
+   have one element (usually on the stack).  For example:
+
+    struct A { .... };
+    void function (gdb::array_view<A> as);
+
+    std::vector<A> std_vec = ...;
+    std::array<A, N> std_array = ...;
+    A array[] = {...};
+    A elem;
+
+    function (std_vec);
+    function (std_array);
+    function (array);
+    function (elem);
+
+   Views can be either mutable or const.  A const view is simply
+   created by specifying a const T as array_view template parameter,
+   in which case operator[] of non-const array_view objects ends up
+   returning const references.  Making the array_view itself const is
+   analogous to making a pointer itself be const.  I.e., disables
+   re-seating the view/pointer.
+
+   Since array_view objects are small (pointer plus size), and
+   designed to be trivially copyable, they should generally be passed
+   around by value.
+
+   You can find unit tests covering the whole API in
+   unittests/array-view-selftests.c.  */
+
+namespace gdb {
+
+template <typename T>
+class array_view
+{
+  /* True iff decayed T is the same as decayed U.  E.g., we want to
+     say that 'T&' is the same as 'const T'.  */
+  template <typename U>
+  using IsDecayedT = typename std::is_same<typename std::decay<T>::type,
+					   typename std::decay<U>::type>;
+
+  /* True iff decayed T is the same as decayed U, and 'U *' is
+     implicitly convertible to 'T *'.  This is a requirement for
+     several methods.  */
+  template <typename U>
+  using DecayedConvertible = gdb::And<IsDecayedT<U>,
+				      std::is_convertible<U *, T *>>;
+
+public:
+  using value_type = T;
+  using reference = T &;
+  using const_reference = const T &;
+  using size_type = size_t;
+
+  /* Default construction creates an empty view.  */
+  constexpr array_view () noexcept
+    : m_array (nullptr), m_size (0)
+  {}
+
+  /* Create an array view over a single object of the type of a
+     array_view element.  The created view as size==1.  This is
+     templated on U to allow constructing a array_view<const T> over a
+     (non-const) T.  The "convertible" requirement makes sure that you
+     can't create an array_view<T> over a const T.  */
+  template<typename U,
+	   typename = Requires<DecayedConvertible<U>>>
+  constexpr array_view (U &elem) noexcept
+    : m_array (&elem), m_size (1)
+  {}
+
+  /* Same as above, for rvalue references.  */
+  template<typename U,
+	   typename = Requires<DecayedConvertible<U>>>
+  constexpr array_view (U &&elem) noexcept
+    : m_array (&elem), m_size (1)
+  {}
+
+  /* Create an array view from a pointer to an array and an element
+     count.  */
+  template<typename U,
+	   typename = Requires<DecayedConvertible<U>>>
+  constexpr array_view (U *array, size_t size) noexcept
+    : m_array (array), m_size (size)
+  {}
+
+  /* Create an array view from a range.  This is templated on both U
+     an V to allow passing in a mix of 'const T *' and 'T *'.  */
+  template<typename U, typename V,
+	   typename = Requires<DecayedConvertible<U>>,
+	   typename = Requires<DecayedConvertible<V>>>
+  constexpr array_view (U *begin, V *end) noexcept
+    : m_array (begin), m_size (end - begin)
+  {}
+
+  /* Create an array view from an array.  */
+  template<typename U, size_t Size,
+	   typename = Requires<DecayedConvertible<U>>>
+  constexpr array_view (U (&array)[Size]) noexcept
+    : m_array (array), m_size (Size)
+  {}
+
+  /* Create an array view from a contiguous container.  E.g.,
+     std::vector and std::array.  */
+  template<typename Container,
+	   typename = Requires<gdb::Not<IsDecayedT<Container>>>,
+	   typename
+	     = Requires<std::is_convertible
+			<decltype (std::declval<Container> ().data ()),
+			 T *>>,
+	   typename
+	     = Requires<std::is_convertible
+			<decltype (std::declval<Container> ().size ()),
+			 size_type>>>
+  constexpr array_view (Container &&c) noexcept
+    : m_array (c.data ()), m_size (c.size ())
+  {}
+
+  /* Observer methods.  Some of these can't be constexpr until we
+     require C++14.  */
+  /*constexpr14*/ T *data () noexcept { return m_array; }
+  constexpr const T *data () const noexcept { return m_array; }
+
+  /*constexpr14*/ T *begin () noexcept { return m_array; }
+  constexpr const T *begin () const noexcept { return m_array; }
+
+  /*constexpr14*/ T *end () noexcept { return m_array + m_size; }
+  constexpr const T *end () const noexcept { return m_array + m_size; }
+
+  /*constexpr14*/ reference operator[] (size_t index) noexcept
+  { return m_array[index]; }
+  constexpr const_reference operator[] (size_t index) const noexcept
+  { return m_array[index]; }
+
+  constexpr size_type size () const noexcept { return m_size; }
+  constexpr bool empty () const noexcept { return m_size == 0; }
+
+private:
+  T *m_array;
+  size_type m_size;
+};
+
+} /* namespace gdb */
+
+#endif
diff --git a/gdb/unittests/array-view-selftests.c b/gdb/unittests/array-view-selftests.c
new file mode 100644
index 0000000..91d845c
--- /dev/null
+++ b/gdb/unittests/array-view-selftests.c
@@ -0,0 +1,489 @@
+/* Self tests for array_view for GDB, the GNU debugger.
+
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "selftest.h"
+#include "common/array-view.h"
+
+namespace selftests {
+namespace array_view_tests {
+
+/* Triviality checks.  */
+#define CHECK_TRAIT(TRAIT)			\
+  static_assert (std::TRAIT<gdb_byte>::value, "")
+
+#if HAVE_IS_TRIVIALLY_COPYABLE
+
+CHECK_TRAIT (is_trivially_copyable);
+CHECK_TRAIT (is_trivially_move_assignable);
+CHECK_TRAIT (is_trivially_move_constructible);
+CHECK_TRAIT (is_nothrow_move_assignable);
+CHECK_TRAIT (is_nothrow_move_constructible);
+
+#endif
+
+#undef CHECK_TRAIT
+
+/* Check for implicit conversion to a mutable view.  The first
+   overload is taken iff the argument is convertible to a view,
+   otherwise, the second overload is taken.  */
+
+template<typename From, typename To>
+static constexpr bool
+is_convertible ()
+{
+  return std::is_convertible<From, To>::value;
+}
+
+template<typename T>
+using av = gdb::array_view<T>;
+
+/* Check for implicit conversion to immutable and mutable views.  */
+
+static constexpr bool
+check_convertible ()
+{
+  using T = gdb_byte;
+  using gdb::array_view;
+
+  return (true
+	  /* immutable array_view */
+	  &&  is_convertible<const T (&) [1],	array_view<const T>> ()
+	  &&  is_convertible<T (&) [1], 	array_view<const T>> ()
+	  &&  is_convertible<const T, 		array_view<const T>> ()
+	  &&  is_convertible<T, 		array_view<const T>> ()
+
+	  /* mutable array_view */
+	  &&  is_convertible<T (&) [1], 	array_view<T>> ()
+	  && !is_convertible<const T (&) [1],	array_view<T>> ()
+	  &&  is_convertible<T, 		array_view<T>> ()
+	  && !is_convertible<const T,		array_view<T>> ()
+
+	  /* While float is implicitly convertible to gdb_byte, we
+	     don't want implicit float->array_view<gdb_byte>
+	     conversion.  */
+	  && !is_convertible<float, 		array_view<const T>> ()
+	  && !is_convertible<float, 		array_view<T>> ());
+}
+
+static_assert (check_convertible (), "");
+
+struct A { int i; };
+struct B : A { int j; };
+struct C : A { int l; };
+
+/* Check that there's no array->view conversion for arrays of derived
+   types or subclasses.  */
+static constexpr bool
+check_no_slicing ()
+{
+  using gdb::array_view;
+
+  return (true
+
+	  /* array->view  */
+
+	  &&  is_convertible <A (&)[1], array_view<A>> ()
+	  && !is_convertible <B (&)[1], array_view<A>> ()
+	  && !is_convertible <C (&)[1], array_view<A>> ()
+
+	  && !is_convertible <A (&)[1], array_view<B>> ()
+	  &&  is_convertible <B (&)[1], array_view<B>> ()
+	  && !is_convertible <C (&)[1], array_view<B>> ()
+
+	  /* elem->view  */
+
+	  &&  is_convertible <A, array_view<A>> ()
+	  && !is_convertible <B, array_view<A>> ()
+	  && !is_convertible <C, array_view<A>> ()
+
+	  && !is_convertible <A, array_view<B>> ()
+	  &&  is_convertible <B, array_view<B>> ()
+	  && !is_convertible <C, array_view<B>> ());
+}
+
+static_assert (check_no_slicing (), "");
+
+/* Check that array_view implicitly converts from std::vector.  */
+
+static constexpr bool
+check_convertible_from_std_vector ()
+{
+  using gdb::array_view;
+  using T = gdb_byte;
+
+  /* Note there's no such thing as std::vector<const T>.  */
+
+  return (true
+	  &&  is_convertible <std::vector<T>, array_view<T>> ()
+	  &&  is_convertible <std::vector<T>, array_view<const T>> ());
+}
+
+static_assert (check_convertible_from_std_vector (), "");
+
+/* Check that array_view implicitly converts from std::array.  */
+
+static constexpr bool
+check_convertible_from_std_array ()
+{
+  using gdb::array_view;
+  using T = gdb_byte;
+
+  /* Note: a non-const T view can't refer to a const T array.  */
+
+  return (true
+	  &&  is_convertible <std::array<T, 1>,		array_view<T>> ()
+	  &&  is_convertible <std::array<T, 1>,		array_view<const T>> ()
+	  && !is_convertible <std::array<const T, 1>,	array_view<T>> ()
+	  &&  is_convertible <std::array<const T, 1>,	array_view<const T>> ());
+}
+
+static_assert (check_convertible_from_std_array (), "");
+
+/* Check that VIEW views C (a container like std::vector/std::array)
+   correctly.  */
+
+template<typename View, typename Container>
+static bool
+check_container_view (const View &view, const Container &c)
+{
+  if (view.empty ())
+    return false;
+  if (view.size () != c.size ())
+    return false;
+  if (view.data () != c.data ())
+    return false;
+  for (size_t i = 0; i < c.size (); i++)
+    {
+      if (&view[i] != &c[i])
+	return false;
+      if (view[i] != c[i])
+	return false;
+    }
+  return true;
+}
+
+/* Check that VIEW views E (an object of the type of a view element)
+   correctly.  */
+
+template<typename View, typename Elem>
+static bool
+check_elem_view (const View &view, const Elem &e)
+{
+  if (view.empty ())
+    return false;
+  if (view.size () != 1)
+    return false;
+  if (view.data () != &e)
+    return false;
+  if (&view[0] != &e)
+    return false;
+  if (view[0] != e)
+    return false;
+  return true;
+}
+
+/* Check for operator[].  The first overload is taken iff
+   'view<T>()[0] = T()' compiles.  */
+
+template<typename View,
+	 typename = decltype (std::declval<View> ()[0]
+			      = std::declval<typename View::value_type> ())>
+static bool
+check_op_subscript (const View &view)
+{
+  return true;
+}
+
+/* This overload is taken iff 'view<T>()[0] = T()' does not
+   compile.  */
+
+static bool
+check_op_subscript (...)
+{
+  return false;
+}
+
+/* Check construction with a pointer + size.  This is a template in
+   order to test both gdb_byte and const gdb_byte.  */
+
+template<typename T>
+static void
+check_ptr_size_ctor ()
+{
+  T data[] = {0x11, 0x22, 0x33, 0x44};
+
+  gdb::array_view<T> view (data + 1, 2);
+
+  SELF_CHECK (!view.empty ());
+  SELF_CHECK (view.size () == 2);
+  SELF_CHECK (view.data () == &data[1]);
+  SELF_CHECK (view[0] == data[1]);
+  SELF_CHECK (view[1] == data[2]);
+
+  gdb::array_view<const T> view2 (data + 1, 2);
+  SELF_CHECK (!view2.empty ());
+  SELF_CHECK (view2.size () == 2);
+  SELF_CHECK (view2.data () == &data[1]);
+  SELF_CHECK (view2[0] == data[1]);
+  SELF_CHECK (view2[1] == data[2]);
+}
+
+template<typename T, typename... Args>
+static constexpr bool
+require_not_constructible ()
+{
+  static_assert (!std::is_constructible<T, Args...>::value, "");
+  return true;
+};
+
+/* Check the array_view<T>(PTR, SIZE) ctor, when T is a pointer.  */
+
+void
+check_ptr_size_ctor2 ()
+{
+  struct A {};
+  A an_a;
+
+  A *array[] = { &an_a };
+  const A * const carray[] = { &an_a };
+
+  gdb::array_view<A *> v1 = {array, ARRAY_SIZE (array)};
+  gdb::array_view<A *> v2 = {array, (char) ARRAY_SIZE (array)};
+  gdb::array_view<A * const> v3 = {array, ARRAY_SIZE (array)};
+  gdb::array_view<const A * const> cv1 = {carray, ARRAY_SIZE (carray)};
+
+  require_not_constructible<gdb::array_view<A *>, decltype (carray), size_t> ();
+
+  SELF_CHECK (v1[0] == array[0]);
+  SELF_CHECK (v2[0] == array[0]);
+  SELF_CHECK (v3[0] == array[0]);
+
+  SELF_CHECK (!v1.empty ());
+  SELF_CHECK (v1.size () == 1);
+  SELF_CHECK (v1.data () == &array[0]);
+
+  SELF_CHECK (cv1[0] == carray[0]);
+
+  SELF_CHECK (!cv1.empty ());
+  SELF_CHECK (cv1.size () == 1);
+  SELF_CHECK (cv1.data () == &carray[0]);
+}
+
+/* Check construction with a pair of pointers.  This is a template in
+   order to test both gdb_byte and const gdb_byte.  */
+
+template<typename T>
+static void
+check_ptr_ptr_ctor ()
+{
+  T data[] = {0x11, 0x22, 0x33, 0x44};
+
+  gdb::array_view<T> view (data + 1, data + 3);
+
+  SELF_CHECK (!view.empty ());
+  SELF_CHECK (view.size () == 2);
+  SELF_CHECK (view.data () == &data[1]);
+  SELF_CHECK (view[0] == data[1]);
+  SELF_CHECK (view[1] == data[2]);
+
+  gdb_byte array[] = {0x11, 0x22, 0x33, 0x44};
+  const gdb_byte *p1 = array;
+  gdb_byte *p2 = array + ARRAY_SIZE (array);
+  gdb::array_view<const gdb_byte> view2 (p1, p2);
+}
+
+/* Check construction with a pair of pointers of mixed constness.  */
+
+static void
+check_ptr_ptr_mixed_cv ()
+{
+  gdb_byte array[] = {0x11, 0x22, 0x33, 0x44};
+  const gdb_byte *cp = array;
+  gdb_byte *p = array;
+  gdb::array_view<const gdb_byte> view1 (cp, p);
+  gdb::array_view<const gdb_byte> view2 (p, cp);
+  SELF_CHECK (view1.empty ());
+  SELF_CHECK (view2.empty ());
+}
+
+/* Check range-for support (i.e., begin()/end()).  This is a template
+   in order to test both gdb_byte and const gdb_byte.  */
+
+template<typename T>
+static void
+check_range_for ()
+{
+  T data[] = {1, 2, 3, 4};
+  gdb::array_view<T> view (data);
+
+  typename std::decay<T>::type sum = 0;
+  for (auto &elem : view)
+    sum += elem;
+  SELF_CHECK (sum == 1 + 2 + 3 + 4);
+}
+
+static void
+run_tests ()
+{
+  /* Empty views.  */
+  {
+    constexpr gdb::array_view<gdb_byte> view1;
+    constexpr gdb::array_view<const gdb_byte> view2;
+
+    static_assert (view1.empty (), "");
+    static_assert (view1.data () == nullptr, "");
+    static_assert (view1.size () == 0, "");
+    static_assert (view2.empty (), "");
+    static_assert (view2.size () == 0, "");
+    static_assert (view2.data () == nullptr, "");
+  }
+
+  std::vector<gdb_byte> vec = {0x11, 0x22, 0x33, 0x44 };
+  std::array<gdb_byte, 4> array = {{0x11, 0x22, 0x33, 0x44}};
+
+  /* Various tests of views over std::vector.  */
+  {
+    gdb::array_view<gdb_byte> view = vec;
+    SELF_CHECK (check_container_view (view, vec));
+    gdb::array_view<const gdb_byte> cview = vec;
+    SELF_CHECK (check_container_view (cview, vec));
+  }
+
+  /* Likewise, over std::array.  */
+  {
+    gdb::array_view<gdb_byte> view = array;
+    SELF_CHECK (check_container_view (view, array));
+    gdb::array_view<gdb_byte> cview = array;
+    SELF_CHECK (check_container_view (cview, array));
+  }
+
+  /* op=(std::vector/std::array/elem) */
+  {
+    gdb::array_view<gdb_byte> view;
+
+    view = vec;
+    SELF_CHECK (check_container_view (view, vec));
+    view = std::move (vec);
+    SELF_CHECK (check_container_view (view, vec));
+
+    view = array;
+    SELF_CHECK (check_container_view (view, array));
+    view = std::move (array);
+    SELF_CHECK (check_container_view (view, array));
+
+    gdb_byte elem = 0;
+    view = elem;
+    SELF_CHECK (check_elem_view (view, elem));
+    view = std::move (elem);
+    SELF_CHECK (check_elem_view (view, elem));
+  }
+
+  /* Test copy/move ctor and mutable->immutable conversion.  */
+  {
+    gdb_byte data[] = {0x11, 0x22, 0x33, 0x44};
+    gdb::array_view<gdb_byte> view1 = data;
+    gdb::array_view<gdb_byte> view2 = view1;
+    gdb::array_view<gdb_byte> view3 = std::move (view1);
+    gdb::array_view<const gdb_byte> cview1 = data;
+    gdb::array_view<const gdb_byte> cview2 = cview1;
+    gdb::array_view<const gdb_byte> cview3 = std::move (cview1);
+    SELF_CHECK (view1[0] == data[0]);
+    SELF_CHECK (view2[0] == data[0]);
+    SELF_CHECK (view3[0] == data[0]);
+    SELF_CHECK (cview1[0] == data[0]);
+    SELF_CHECK (cview2[0] == data[0]);
+    SELF_CHECK (cview3[0] == data[0]);
+  }
+
+  /* Same, but op=(view).  */
+  {
+    gdb_byte data[] = {0x55, 0x66, 0x77, 0x88};
+    gdb::array_view<gdb_byte> view1;
+    gdb::array_view<gdb_byte> view2;
+    gdb::array_view<gdb_byte> view3;
+    gdb::array_view<const gdb_byte> cview1;
+    gdb::array_view<const gdb_byte> cview2;
+    gdb::array_view<const gdb_byte> cview3;
+
+    view1 = data;
+    view2 = view1;
+    view3 = std::move (view1);
+    cview1 = data;
+    cview2 = cview1;
+    cview3 = std::move (cview1);
+    SELF_CHECK (view1[0] == data[0]);
+    SELF_CHECK (view2[0] == data[0]);
+    SELF_CHECK (view3[0] == data[0]);
+    SELF_CHECK (cview1[0] == data[0]);
+    SELF_CHECK (cview2[0] == data[0]);
+    SELF_CHECK (cview3[0] == data[0]);
+  }
+
+  /* op[] */
+  {
+    std::vector<gdb_byte> vec = {0x11, 0x22};
+    gdb::array_view<gdb_byte> view = vec;
+    gdb::array_view<const gdb_byte> cview = vec;
+
+    /* Check that op[] on a non-const view of non-const T returns a
+       mutable reference.  */
+    view[0] = 0x33;
+    SELF_CHECK (vec[0] == 0x33);
+
+    /* OTOH, check that assigning through op[] on a view of const T
+       wouldn't compile.  */
+    SELF_CHECK (!check_op_subscript (cview));
+    /* For completeness.  */
+    SELF_CHECK (check_op_subscript (view));
+  }
+
+  check_ptr_size_ctor<const gdb_byte> ();
+  check_ptr_size_ctor<gdb_byte> ();
+  check_ptr_size_ctor2 ();
+  check_ptr_ptr_ctor<const gdb_byte> ();
+  check_ptr_ptr_ctor<gdb_byte> ();
+  check_ptr_ptr_mixed_cv ();
+
+  check_range_for<gdb_byte> ();
+  check_range_for<const gdb_byte> ();
+
+  /* Check that the right ctor overloads are taken when the element is
+     a container.  */
+  {
+    using Vec = std::vector<gdb_byte>;
+    Vec vecs[3];
+
+    gdb::array_view<Vec> view_array = vecs;
+    SELF_CHECK (view_array.size () == 3);
+
+    Vec elem;
+    gdb::array_view<Vec> view_elem = elem;
+    SELF_CHECK (view_elem.size () == 1);
+  }
+}
+
+} /* namespace array_view_tests */
+} /* namespace selftests */
+
+void
+_initialize_array_view_selftests ()
+{
+  selftests::register_test (selftests::array_view_tests::run_tests);
+}
-- 
2.5.5

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector
@ 2017-08-21 19:27 Pedro Alves
  2017-08-21 19:27 ` [PATCH 1/3] Introduce gdb::array_view Pedro Alves
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Pedro Alves @ 2017-08-21 19:27 UTC (permalink / raw)
  To: gdb-patches

Looking at the "list and multiple locations" patch [1], I thought that
it's a bit annoying/frustrating to have do remember to free the sals
array that a symtabs_and_lines object wraps, with 'xfree (sals.sals)'
or the equivalent make_cleanup(xfree, ...), not to mention how
error-prone manual memory management is.  We can do better in C++.

So I wrote a patch to replace 'struct symtabs_and_lines' with 'typedef
std::vector<symtab_and_line> symtabs_and_lines'.  That works nicelly,
except that I wasn't entirely happy that that would introduce heap
allocations in a few cases where there is none nowadays.

I then addressed that by adding an array_view abstraction, which is
something that I've wanted at other times before.

After that, we'd end up with some places using a std::vector disguised
behind the symtabs_and_lines typedef, while in other places we'd be
spelling out gdb::array_view.  With that, I no longer see any value in
keeping the symtabs_and_lines typedef, kind of giving preference to
std::vector, so I removed the typedef, choosing to spell out
std::vector explicitly.

(I'm aware that there are several proposals for adding something like
array_view to the standard C++ library.  In this implementation, which
was written from scratch and developed in parallel with the unit
tests, I chose to keep it as simple and safe as possible.  For
example, there is no support for ranks != 1, simply because the main
use case this aims at addressing is abstracting out std::vector vs
stack arrays in APIs.)

[1] https://sourceware.org/ml/gdb-patches/2017-07/msg00280.html

Note that the net increase shown below is mostly caused by the new
unit tests.  If we only count patches 2 and 3, then we get instead:

  33 files changed, 537 insertions(+), 820 deletions(-)

Pedro Alves (3):
  Introduce gdb::array_view
  struct symtabs_and_lines -> std::vector<symtab_and_line>
  Kill init_sal

 gdb/Makefile.in                      |   2 +
 gdb/ada-lang.c                       |   3 +-
 gdb/ax-gdb.c                         |  12 +-
 gdb/break-catch-throw.c              |  12 +-
 gdb/breakpoint.c                     | 359 +++++++++++--------------
 gdb/breakpoint.h                     |  20 +-
 gdb/cli/cli-cmds.c                   | 158 +++++------
 gdb/common/array-view.h              | 179 +++++++++++++
 gdb/elfread.c                        |  14 +-
 gdb/frame.c                          |  25 +-
 gdb/frame.h                          |   3 +-
 gdb/guile/scm-frame.c                |   2 +-
 gdb/guile/scm-symtab.c               |   6 +-
 gdb/infcall.c                        |  11 +-
 gdb/infcmd.c                         |  35 +--
 gdb/infrun.c                         |  52 ++--
 gdb/linespec.c                       | 264 ++++++++-----------
 gdb/linespec.h                       |  32 +--
 gdb/macrocmd.c                       |   8 +-
 gdb/mi/mi-main.c                     |  17 +-
 gdb/probe.c                          |  33 +--
 gdb/probe.h                          |  14 +-
 gdb/python/py-frame.c                |   3 +-
 gdb/python/python.c                  |  47 ++--
 gdb/reverse.c                        |   8 +-
 gdb/source.c                         |  54 ++--
 gdb/source.h                         |   3 +-
 gdb/stack.c                          |  56 ++--
 gdb/stack.h                          |   2 +-
 gdb/symtab.c                         |  26 +-
 gdb/symtab.h                         |  27 +-
 gdb/tracepoint.c                     |  39 +--
 gdb/tui/tui-disasm.c                 |   2 +-
 gdb/tui/tui-stack.c                  |   3 +-
 gdb/tui/tui-winsource.c              |   7 +-
 gdb/unittests/array-view-selftests.c | 489 +++++++++++++++++++++++++++++++++++
 36 files changed, 1207 insertions(+), 820 deletions(-)
 create mode 100644 gdb/common/array-view.h
 create mode 100644 gdb/unittests/array-view-selftests.c

-- 
2.5.5

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/3] Kill init_sal
  2017-08-21 19:27 [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector Pedro Alves
  2017-08-21 19:27 ` [PATCH 1/3] Introduce gdb::array_view Pedro Alves
@ 2017-08-21 19:28 ` Pedro Alves
  2017-08-21 19:28 ` [PATCH 2/3] struct symtabs_and_lines -> std::vector<symtab_and_line> Pedro Alves
  2017-09-04 16:15 ` [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector Pedro Alves
  3 siblings, 0 replies; 6+ messages in thread
From: Pedro Alves @ 2017-08-21 19:28 UTC (permalink / raw)
  To: gdb-patches

Instead, make symtab_and_line initialize its members itself.  Many
symtab_and_line declarations are moved to where the object is
initialized at the same time both for clarity and to avoid double
initialization.  A few functions, like e.g., find_frame_sal are
adjusted to return the sal using normal function return instead of an
output parameter likewise to avoid having to default-construct a sal
and then immediately have the object overwritten.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (is_known_support_routine): Move sal declaration to
	where it is initialized.
	* breakpoint.c (create_internal_breakpoint, init_catchpoint)
	(parse_breakpoint_sals, decode_static_tracepoint_spec)
	(clear_command, update_static_tracepoint): Remove init_sal
	references.  Move declarations closer to initializations.
	* cli/cli-cmds.c (list_command): Move sal declarations closer to
	initializations.
	* elfread.c (elf_gnu_ifunc_resolver_stop): Remove init_sal
	references.  Move sal declarations closer to initializations.
	* frame.c (find_frame_sal): Return a symtab_and_line via function
	return instead of output parameter.  Remove init_sal references.
	* frame.h (find_frame_sal): Return a symtab_and_line via function
	return instead of output parameter.
	* guile/scm-frame.c (gdbscm_frame_sal): Adjust.
	* guile/scm-symtab.c (stscm_make_sal_smob): Use in-place new
	instead of memset.
	(gdbscm_find_pc_line): Remove init_sal reference.
	* infcall.c (call_function_by_hand_dummy): Remove init_sal
	references.  Move declarations closer to initializations.
	* infcmd.c (set_step_frame): Update.  Move declarations closer to
	initializations.
	(finish_backward): Remove init_sal references.  Move declarations
	closer to initializations.
	* infrun.c (process_event_stop_test, handle_step_into_function)
	(insert_hp_step_resume_breakpoint_at_frame)
	(insert_step_resume_breakpoint_at_caller): Likewise.
	* linespec.c (create_sals_line_offset, decode_digits_ordinary)
	(symbol_to_sal): Likewise.
	* probe.c (parse_probes_in_pspace): Remove init_sal reference.
	* python/py-frame.c (frapy_find_sal): Move sal declaration closer
	to its initialization.
	* reverse.c (save_bookmark_command): Use new/delete.  Remove
	init_sal references.  Move declarations closer to initializations.
	* source.c (get_current_source_symtab_and_line): Remove brace
	initialization.
	(set_current_source_symtab_and_line): Now takes the sal by const
	reference.  Remove brace initialization.
	(line_info): Remove init_sal reference.
	* source.h (set_current_source_symtab_and_line): Now takes a
	symtab_and_line via const reference.
	* stack.c (set_current_sal_from_frame): Adjust.
	(print_frame_info): Adjust.
	(get_last_displayed_sal): Return the sal via function return
	instead of via output parameter.  Simplify.
	(frame_info): Adjust.
	* stack.h (get_last_displayed_sal): Return the sal via function
	return instead of via output parameter.
	* symtab.c (init_sal): Delete.
	(find_pc_sect_line): Remove init_sal references.  Move
	declarations closer to initializations.
	(find_function_start_sal): Remove init_sal references.  Move
	declarations closer to initializations.
	* symtab.h (struct symtab_and_line): In-class initialize all
	fields.
	* tracepoint.c (set_traceframe_context)
	(print_one_static_tracepoint_marker): Remove init_sal references.
	Move declarations closer to initializations.
	* tui/tui-disasm.c (tui_show_disassem_and_update_source): Adjust.
	* tui/tui-stack.c (tui_show_frame_info): Adjust.  Move
	declarations closer to initializations.
	* tui/tui-winsource.c (tui_update_source_window_as_is): Remove
	init_sal references.  Adjust.
---
 gdb/ada-lang.c          |  3 +--
 gdb/breakpoint.c        | 38 +++++++++---------------------------
 gdb/cli/cli-cmds.c      |  7 +++----
 gdb/elfread.c           |  4 +---
 gdb/frame.c             | 25 +++++++++++-------------
 gdb/frame.h             |  3 +--
 gdb/guile/scm-frame.c   |  2 +-
 gdb/guile/scm-symtab.c  |  6 ++----
 gdb/infcall.c           | 11 +++++------
 gdb/infcmd.c            | 19 +++++++-----------
 gdb/infrun.c            | 52 ++++++++++++++-----------------------------------
 gdb/linespec.c          | 12 ++++--------
 gdb/probe.c             |  3 ---
 gdb/python/py-frame.c   |  3 +--
 gdb/reverse.c           |  8 +++-----
 gdb/source.c            | 13 ++++++-------
 gdb/source.h            |  3 ++-
 gdb/stack.c             | 35 +++++++++++++--------------------
 gdb/stack.h             |  2 +-
 gdb/symtab.c            | 26 +++++++------------------
 gdb/symtab.h            | 22 ++++++++++-----------
 gdb/tracepoint.c        |  8 ++------
 gdb/tui/tui-disasm.c    |  2 +-
 gdb/tui/tui-stack.c     |  3 +--
 gdb/tui/tui-winsource.c |  7 +++----
 25 files changed, 111 insertions(+), 206 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 280247b..7278a2b 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -11995,7 +11995,6 @@ ada_exception_support_info_sniffer (void)
 static int
 is_known_support_routine (struct frame_info *frame)
 {
-  struct symtab_and_line sal;
   char *func_name;
   enum language func_lang;
   int i;
@@ -12004,7 +12003,7 @@ is_known_support_routine (struct frame_info *frame)
   /* If this code does not have any debugging information (no symtab),
      This cannot be any user code.  */
 
-  find_frame_sal (frame, &sal);
+  symtab_and_line sal = find_frame_sal (frame);
   if (sal.symtab == NULL)
     return 1;
 
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index f7d404d..aa1e2f2 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -3360,16 +3360,12 @@ create_internal_breakpoint (struct gdbarch *gdbarch,
 			    CORE_ADDR address, enum bptype type,
 			    const struct breakpoint_ops *ops)
 {
-  struct symtab_and_line sal;
-  struct breakpoint *b;
-
-  init_sal (&sal);		/* Initialize to zeroes.  */
-
+  symtab_and_line sal;
   sal.pc = address;
   sal.section = find_pc_overlay (sal.pc);
   sal.pspace = current_program_space;
 
-  b = set_raw_breakpoint (gdbarch, sal, type, ops);
+  breakpoint *b = set_raw_breakpoint (gdbarch, sal, type, ops);
   b->number = internal_breakpoint_number--;
   b->disposition = disp_donttouch;
 
@@ -8532,9 +8528,7 @@ init_catchpoint (struct breakpoint *b,
 		 const char *cond_string,
 		 const struct breakpoint_ops *ops)
 {
-  struct symtab_and_line sal;
-
-  init_sal (&sal);
+  symtab_and_line sal;
   sal.pspace = current_program_space;
 
   init_raw_breakpoint (b, gdbarch, sal, bp_catchpoint, ops);
@@ -9388,18 +9382,14 @@ parse_breakpoint_sals (const struct event_location *location,
 	     breakpoint address.  */
 	  if (last_displayed_sal_is_valid ())
 	    {
-	      struct symtab_and_line sal;
-	      CORE_ADDR pc;
-
-	      init_sal (&sal);		/* Initialize to zeroes.  */
-
 	      /* Set sal's pspace, pc, symtab, and line to the values
 		 corresponding to the last call to print_frame_info.
 		 Be sure to reinitialize LINE with NOTCURRENT == 0
 		 as the breakpoint line number is inappropriate otherwise.
 		 find_pc_line would adjust PC, re-set it back.  */
-	      get_last_displayed_sal (&sal);
-	      pc = sal.pc;
+	      symtab_and_line sal = get_last_displayed_sal ();
+	      CORE_ADDR pc = sal.pc;
+
 	      sal = find_pc_line (pc, 0);
 
 	      /* "break" without arguments is equivalent to "break *PC"
@@ -9609,10 +9599,7 @@ decode_static_tracepoint_spec (const char **arg_p)
 
       marker = VEC_index (static_tracepoint_marker_p, markers, i);
 
-      symtab_and_line sal;
-      init_sal (&sal);
-
-      sal = find_pc_line (marker->address, 0);
+      symtab_and_line sal = find_pc_line (marker->address, 0);
       sal.pc = marker->address;
       sals.push_back (sal);
 
@@ -11819,12 +11806,10 @@ clear_command (char *arg, int from_tty)
     }
   else
     {
-      init_sal (&last_sal);		/* Initialize to zeroes.  */
-
       /* Set sal's line, symtab, pc, and pspace to the values
 	 corresponding to the last call to print_frame_info.  If the
 	 codepoint is not valid, this will set all the fields to 0.  */
-      get_last_displayed_sal (&last_sal);
+      last_sal = get_last_displayed_sal ();
       if (last_sal.symtab == 0)
 	error (_("No source file specified."));
 
@@ -13903,7 +13888,6 @@ update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal)
 
       if (!VEC_empty(static_tracepoint_marker_p, markers))
 	{
-	  struct symtab_and_line sal2;
 	  struct symbol *sym;
 	  struct static_tracepoint_marker *tpmarker;
 	  struct ui_out *uiout = current_uiout;
@@ -13918,11 +13902,7 @@ update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal)
 		     "found at previous line number"),
 		   b->number, tp->static_trace_marker_id);
 
-	  init_sal (&sal2);
-
-	  sal2.pc = tpmarker->address;
-
-	  sal2 = find_pc_line (tpmarker->address, 0);
+	  symtab_and_line sal2 = find_pc_line (tpmarker->address, 0);
 	  sym = find_pc_sect_function (tpmarker->address, NULL);
 	  uiout->text ("Now in ");
 	  if (sym)
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 3506254..b467045 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -885,9 +885,6 @@ edit_command (char *arg, int from_tty)
 static void
 list_command (char *arg, int from_tty)
 {
-  struct symtab_and_line sal = { 0 };
-  struct symtab_and_line sal_end = { 0 };
-  struct symtab_and_line cursal = { 0 };
   struct symbol *sym;
   char *arg1;
   int no_end = 1;
@@ -900,7 +897,7 @@ list_command (char *arg, int from_tty)
   if (arg == NULL || ((arg[0] == '+' || arg[0] == '-') && arg[1] == '\0'))
     {
       set_default_source_symtab_and_line ();
-      cursal = get_current_source_symtab_and_line ();
+      symtab_and_line cursal = get_current_source_symtab_and_line ();
 
       /* If this is the first "list" since we've set the current
 	 source line, center the listing around that line.  */
@@ -952,6 +949,8 @@ list_command (char *arg, int from_tty)
   if (!have_full_symbols () && !have_partial_symbols ())
     error (_("No symbol table is loaded.  Use the \"file\" command."));
 
+  symtab_and_line sal, sal_end;
+
   arg1 = arg;
   if (*arg1 == ',')
     dummy_beg = 1;
diff --git a/gdb/elfread.c b/gdb/elfread.c
index ddff16b..cad7aa8 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -917,12 +917,10 @@ elf_gnu_ifunc_resolver_stop (struct breakpoint *b)
 
   if (b_return == b)
     {
-      struct symtab_and_line sal;
-
       /* No need to call find_pc_line for symbols resolving as this is only
 	 a helper breakpointer never shown to the user.  */
 
-      init_sal (&sal);
+      symtab_and_line sal;
       sal.pspace = current_inferior ()->pspace;
       sal.pc = prev_pc;
       sal.section = find_pc_overlay (sal.pc);
diff --git a/gdb/frame.c b/gdb/frame.c
index 30e4aea..55d4ddb 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2506,8 +2506,8 @@ get_frame_address_in_block_if_available (struct frame_info *this_frame,
   return 1;
 }
 
-void
-find_frame_sal (struct frame_info *frame, struct symtab_and_line *sal)
+symtab_and_line
+find_frame_sal (frame_info *frame)
 {
   struct frame_info *next_frame;
   int notcurrent;
@@ -2528,21 +2528,21 @@ find_frame_sal (struct frame_info *frame, struct symtab_and_line *sal)
 
       /* If frame is inline, it certainly has symbols.  */
       gdb_assert (sym);
-      init_sal (sal);
+
+      symtab_and_line sal;
       if (SYMBOL_LINE (sym) != 0)
 	{
-	  sal->symtab = symbol_symtab (sym);
-	  sal->line = SYMBOL_LINE (sym);
+	  sal.symtab = symbol_symtab (sym);
+	  sal.line = SYMBOL_LINE (sym);
 	}
       else
 	/* If the symbol does not have a location, we don't know where
 	   the call site is.  Do not pretend to.  This is jarring, but
 	   we can't do much better.  */
-	sal->pc = get_frame_pc (frame);
+	sal.pc = get_frame_pc (frame);
 
-      sal->pspace = get_frame_program_space (frame);
-
-      return;
+      sal.pspace = get_frame_program_space (frame);
+      return sal;
     }
 
   /* If FRAME is not the innermost frame, that normally means that
@@ -2555,13 +2555,10 @@ find_frame_sal (struct frame_info *frame, struct symtab_and_line *sal)
      instruction/line, consequently, for such cases, want to get the
      line containing fi->pc.  */
   if (!get_frame_pc_if_available (frame, &pc))
-    {
-      init_sal (sal);
-      return;
-    }
+    return {};
 
   notcurrent = (pc != get_frame_address_in_block (frame));
-  (*sal) = find_pc_line (pc, notcurrent);
+  return find_pc_line (pc, notcurrent);
 }
 
 /* Per "frame.h", return the ``address'' of the frame.  Code should
diff --git a/gdb/frame.h b/gdb/frame.h
index 56cbd44..bf43a8b 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -408,8 +408,7 @@ extern int get_frame_func_if_available (struct frame_info *fi, CORE_ADDR *);
    find_frame_symtab(), find_frame_function().  Each will need to be
    carefully considered to determine if the real intent was for it to
    apply to the PC or the adjusted PC.  */
-extern void find_frame_sal (struct frame_info *frame,
-			    struct symtab_and_line *sal);
+extern symtab_and_line find_frame_sal (frame_info *frame);
 
 /* Set the current source and line to the location given by frame
    FRAME, if possible.  */
diff --git a/gdb/guile/scm-frame.c b/gdb/guile/scm-frame.c
index 994f92d..b2af743 100644
--- a/gdb/guile/scm-frame.c
+++ b/gdb/guile/scm-frame.c
@@ -761,7 +761,7 @@ gdbscm_frame_sal (SCM self)
     {
       frame = frscm_frame_smob_to_frame (f_smob);
       if (frame != NULL)
-	find_frame_sal (frame, &sal);
+	sal = find_frame_sal (frame);
     }
   CATCH (except, RETURN_MASK_ALL)
     {
diff --git a/gdb/guile/scm-symtab.c b/gdb/guile/scm-symtab.c
index 925ab39..755ea67 100644
--- a/gdb/guile/scm-symtab.c
+++ b/gdb/guile/scm-symtab.c
@@ -419,7 +419,7 @@ stscm_make_sal_smob (void)
   SCM s_scm;
 
   s_smob->symtab_scm = SCM_BOOL_F;
-  memset (&s_smob->sal, 0, sizeof (s_smob->sal));
+  new (&s_smob->sal) symtab_and_line ();
   s_scm = scm_new_smob (sal_smob_tag, (scm_t_bits) s_smob);
   gdbscm_init_gsmob (&s_smob->base);
 
@@ -589,9 +589,7 @@ static SCM
 gdbscm_find_pc_line (SCM pc_scm)
 {
   ULONGEST pc_ull;
-  struct symtab_and_line sal;
-
-  init_sal (&sal); /* -Wall */
+  symtab_and_line sal;
 
   gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, NULL, "U", pc_scm, &pc_ull);
 
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 38335a7..9e434a9 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -1051,17 +1051,16 @@ call_function_by_hand_dummy (struct value *function,
      inferior.  That way it breaks when it returns.  */
 
   {
-    struct breakpoint *bpt, *longjmp_b;
-    struct symtab_and_line sal;
-
-    init_sal (&sal);		/* initialize to zeroes */
+    symtab_and_line sal;
     sal.pspace = current_program_space;
     sal.pc = bp_addr;
     sal.section = find_pc_overlay (sal.pc);
+
     /* Sanity.  The exact same SP value is returned by
        PUSH_DUMMY_CALL, saved as the dummy-frame TOS, and used by
        dummy_id to form the frame ID's stack address.  */
-    bpt = set_momentary_breakpoint (gdbarch, sal, dummy_id, bp_call_dummy);
+    breakpoint *bpt = set_momentary_breakpoint (gdbarch, sal,
+						dummy_id, bp_call_dummy);
 
     /* set_momentary_breakpoint invalidates FRAME.  */
     frame = NULL;
@@ -1069,7 +1068,7 @@ call_function_by_hand_dummy (struct value *function,
     bpt->disposition = disp_del;
     gdb_assert (bpt->related_breakpoint == bpt);
 
-    longjmp_b = set_longjmp_breakpoint_for_call_dummy ();
+    breakpoint *longjmp_b = set_longjmp_breakpoint_for_call_dummy ();
     if (longjmp_b)
       {
 	/* Link BPT into the chain of LONGJMP_B.  */
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index c70a1d5..9846a55 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -865,14 +865,13 @@ continue_command (char *args, int from_tty)
 static void
 set_step_frame (void)
 {
-  struct symtab_and_line sal;
-  CORE_ADDR pc;
-  struct frame_info *frame = get_current_frame ();
-  struct thread_info *tp = inferior_thread ();
+  frame_info *frame = get_current_frame ();
 
-  find_frame_sal (frame, &sal);
+  symtab_and_line sal = find_frame_sal (frame);
   set_step_info (frame, sal);
-  pc = get_frame_pc (frame);
+
+  CORE_ADDR pc = get_frame_pc (frame);
+  thread_info *tp = inferior_thread ();
   tp->control.step_start_function = find_pc_function (pc);
 }
 
@@ -1873,11 +1872,10 @@ finish_backward (struct finish_command_fsm *sm)
     {
       struct frame_info *frame = get_selected_frame (NULL);
       struct gdbarch *gdbarch = get_frame_arch (frame);
-      struct symtab_and_line sr_sal;
 
       /* Set a step-resume at the function's entry point.  Once that's
 	 hit, we'll do one more step backwards.  */
-      init_sal (&sr_sal);
+      symtab_and_line sr_sal;
       sr_sal.pc = sal.pc;
       sr_sal.pspace = get_frame_program_space (frame);
       insert_step_resume_breakpoint_at_sal (gdbarch,
@@ -1998,10 +1996,7 @@ finish_command (char *arg, int from_tty)
 	 called by that frame.  We don't use the magic "1" value for
 	 step_range_end, because then infrun will think this is nexti,
 	 and not step over the rest of this inlined function call.  */
-      struct symtab_and_line empty_sal;
-
-      init_sal (&empty_sal);
-      set_step_info (frame, empty_sal);
+      set_step_info (frame, {});
       tp->control.step_range_start = get_frame_pc (frame);
       tp->control.step_range_end = tp->control.step_range_start;
       tp->control.step_over_calls = STEP_OVER_ALL;
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 541fc09..951dc4d 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -6567,9 +6567,7 @@ process_event_stop_test (struct execution_control_state *ecs)
 	{
 	  /* Set up a step-resume breakpoint at the address
 	     indicated by SKIP_SOLIB_RESOLVER.  */
-	  struct symtab_and_line sr_sal;
-
-	  init_sal (&sr_sal);
+	  symtab_and_line sr_sal;
 	  sr_sal.pc = pc_after_resolver;
 	  sr_sal.pspace = get_frame_program_space (frame);
 
@@ -6620,9 +6618,7 @@ process_event_stop_test (struct execution_control_state *ecs)
       if (real_stop_pc)
 	{
 	  /* And put the step-breakpoint there and go until there.  */
-	  struct symtab_and_line sr_sal;
-
-	  init_sal (&sr_sal);	/* initialize to zeroes */
+	  symtab_and_line sr_sal;
 	  sr_sal.pc = real_stop_pc;
 	  sr_sal.section = find_pc_overlay (sr_sal.pc);
 	  sr_sal.pspace = get_frame_program_space (frame);
@@ -6720,10 +6716,8 @@ process_event_stop_test (struct execution_control_state *ecs)
 		 to the caller.  */
 	      if (ecs->stop_func_start != stop_pc && ecs->stop_func_start != 0)
 		{
-		  struct symtab_and_line sr_sal;
-
 		  /* Normal function call return (static or dynamic).  */
-		  init_sal (&sr_sal);
+		  symtab_and_line sr_sal;
 		  sr_sal.pc = ecs->stop_func_start;
 		  sr_sal.pspace = get_frame_program_space (frame);
 		  insert_step_resume_breakpoint_at_sal (gdbarch,
@@ -6750,9 +6744,7 @@ process_event_stop_test (struct execution_control_state *ecs)
 
       if (real_stop_pc != 0 && in_solib_dynsym_resolve_code (real_stop_pc))
 	{
-	  struct symtab_and_line sr_sal;
-
-	  init_sal (&sr_sal);
+	  symtab_and_line sr_sal;
 	  sr_sal.pc = ecs->stop_func_start;
 	  sr_sal.pspace = get_frame_program_space (frame);
 
@@ -6806,9 +6798,7 @@ process_event_stop_test (struct execution_control_state *ecs)
 	    {
 	      /* Set a breakpoint at callee's start address.
 		 From there we can step once and be back in the caller.  */
-	      struct symtab_and_line sr_sal;
-
-	      init_sal (&sr_sal);
+	      symtab_and_line sr_sal;
 	      sr_sal.pc = ecs->stop_func_start;
 	      sr_sal.pspace = get_frame_program_space (frame);
 	      insert_step_resume_breakpoint_at_sal (gdbarch,
@@ -6846,9 +6836,7 @@ process_event_stop_test (struct execution_control_state *ecs)
 	  /* Stepped backward into the solib dynsym resolver.
 	     Set a breakpoint at its start and continue, then
 	     one more step will take us out.  */
-	  struct symtab_and_line sr_sal;
-
-	  init_sal (&sr_sal);
+	  symtab_and_line sr_sal;
 	  sr_sal.pc = ecs->stop_func_start;
 	  sr_sal.pspace = get_frame_program_space (frame);
 	  insert_step_resume_breakpoint_at_sal (gdbarch, 
@@ -6928,13 +6916,11 @@ process_event_stop_test (struct execution_control_state *ecs)
 		   ecs->event_thread->control.step_frame_id)
       && inline_skipped_frames (ecs->ptid))
     {
-      struct symtab_and_line call_sal;
-
       if (debug_infrun)
 	fprintf_unfiltered (gdb_stdlog,
 			    "infrun: stepped into inlined function\n");
 
-      find_frame_sal (get_current_frame (), &call_sal);
+      symtab_and_line call_sal = find_frame_sal (get_current_frame ());
 
       if (ecs->event_thread->control.step_over_calls != STEP_OVER_ALL)
 	{
@@ -7298,17 +7284,14 @@ static void
 handle_step_into_function (struct gdbarch *gdbarch,
 			   struct execution_control_state *ecs)
 {
-  struct compunit_symtab *cust;
-  struct symtab_and_line stop_func_sal, sr_sal;
-
   fill_in_stop_func (gdbarch, ecs);
 
-  cust = find_pc_compunit_symtab (stop_pc);
+  compunit_symtab *cust = find_pc_compunit_symtab (stop_pc);
   if (cust != NULL && compunit_language (cust) != language_asm)
     ecs->stop_func_start
       = gdbarch_skip_prologue_noexcept (gdbarch, ecs->stop_func_start);
 
-  stop_func_sal = find_pc_line (ecs->stop_func_start, 0);
+  symtab_and_line stop_func_sal = find_pc_line (ecs->stop_func_start, 0);
   /* Use the step_resume_break to step until the end of the prologue,
      even if that involves jumps (as it seems to on the vax under
      4.2).  */
@@ -7352,7 +7335,7 @@ handle_step_into_function (struct gdbarch *gdbarch,
   else
     {
       /* Put the step-breakpoint there and go until there.  */
-      init_sal (&sr_sal);	/* initialize to zeroes */
+      symtab_and_line sr_sal;
       sr_sal.pc = ecs->stop_func_start;
       sr_sal.section = find_pc_overlay (ecs->stop_func_start);
       sr_sal.pspace = get_frame_program_space (get_current_frame ());
@@ -7451,13 +7434,11 @@ insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
 static void
 insert_hp_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
 {
-  struct symtab_and_line sr_sal;
-  struct gdbarch *gdbarch;
-
   gdb_assert (return_frame != NULL);
-  init_sal (&sr_sal);		/* initialize to zeros */
 
-  gdbarch = get_frame_arch (return_frame);
+  struct gdbarch *gdbarch = get_frame_arch (return_frame);
+
+  symtab_and_line sr_sal;
   sr_sal.pc = gdbarch_addr_bits_remove (gdbarch, get_frame_pc (return_frame));
   sr_sal.section = find_pc_overlay (sr_sal.pc);
   sr_sal.pspace = get_frame_program_space (return_frame);
@@ -7484,16 +7465,13 @@ insert_hp_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
 static void
 insert_step_resume_breakpoint_at_caller (struct frame_info *next_frame)
 {
-  struct symtab_and_line sr_sal;
-  struct gdbarch *gdbarch;
-
   /* We shouldn't have gotten here if we don't know where the call site
      is.  */
   gdb_assert (frame_id_p (frame_unwind_caller_id (next_frame)));
 
-  init_sal (&sr_sal);		/* initialize to zeros */
+  struct gdbarch *gdbarch = frame_unwind_caller_arch (next_frame);
 
-  gdbarch = frame_unwind_caller_arch (next_frame);
+  symtab_and_line sr_sal;
   sr_sal.pc = gdbarch_addr_bits_remove (gdbarch,
 					frame_unwind_caller_pc (next_frame));
   sr_sal.section = find_pc_overlay (sr_sal.pc);
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 5396eba..4801808 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -2126,11 +2126,8 @@ static std::vector<symtab_and_line>
 create_sals_line_offset (struct linespec_state *self,
 			 linespec_p ls)
 {
-  struct symtab_and_line val;
   int use_default = 0;
 
-  init_sal (&val);
-
   /* This is where we need to make sure we have good defaults.
      We must guarantee that this section of code is never executed
      when we are called with just a function name, since
@@ -2155,6 +2152,7 @@ create_sals_line_offset (struct linespec_state *self,
       use_default = 1;
     }
 
+  symtab_and_line val;
   val.line = ls->explicit_loc.line_offset.offset;
   switch (ls->explicit_loc.line_offset.sign)
     {
@@ -4235,9 +4233,7 @@ decode_digits_ordinary (struct linespec_state *self,
       pcs = find_pcs_for_symtab_line (elt, line, best_entry);
       for (CORE_ADDR pc : pcs)
 	{
-	  struct symtab_and_line sal;
-
-	  init_sal (&sal);
+	  symtab_and_line sal;
 	  sal.pspace = SYMTAB_PSPACE (elt);
 	  sal.symtab = elt;
 	  sal.line = line;
@@ -4619,7 +4615,7 @@ symbol_to_sal (struct symtab_and_line *result,
     {
       if (SYMBOL_CLASS (sym) == LOC_LABEL && SYMBOL_VALUE_ADDRESS (sym) != 0)
 	{
-	  init_sal (result);
+	  *result = {};
 	  result->symtab = symbol_symtab (sym);
 	  result->line = SYMBOL_LINE (sym);
 	  result->pc = SYMBOL_VALUE_ADDRESS (sym);
@@ -4634,7 +4630,7 @@ symbol_to_sal (struct symtab_and_line *result,
       else if (SYMBOL_LINE (sym) != 0)
 	{
 	  /* We know its line number.  */
-	  init_sal (result);
+	  *result = {};
 	  result->symtab = symbol_symtab (sym);
 	  result->line = SYMBOL_LINE (sym);
 	  result->pspace = SYMTAB_PSPACE (result->symtab);
diff --git a/gdb/probe.c b/gdb/probe.c
index 5b5e6f0..ce28361 100644
--- a/gdb/probe.c
+++ b/gdb/probe.c
@@ -85,9 +85,6 @@ parse_probes_in_pspace (const struct probe_ops *probe_ops,
 	    continue;
 
 	  symtab_and_line sal;
-
-	  init_sal (&sal);
-
 	  sal.pc = get_probe_address (probe, objfile);
 	  sal.explicit_pc = 1;
 	  sal.section = find_pc_overlay (sal.pc);
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 891f44e..c5ae391 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -468,14 +468,13 @@ static PyObject *
 frapy_find_sal (PyObject *self, PyObject *args)
 {
   struct frame_info *frame;
-  struct symtab_and_line sal;
   PyObject *sal_obj = NULL;   /* Initialize to appease gcc warning.  */
 
   TRY
     {
       FRAPY_REQUIRE_VALID (self, frame);
 
-      find_frame_sal (frame, &sal);
+      symtab_and_line sal = find_frame_sal (frame);
       sal_obj = symtab_and_line_to_sal_object (sal);
     }
   CATCH (except, RETURN_MASK_ALL)
diff --git a/gdb/reverse.c b/gdb/reverse.c
index c8f3811..4c3bbe1 100644
--- a/gdb/reverse.c
+++ b/gdb/reverse.c
@@ -121,7 +121,6 @@ save_bookmark_command (char *args, int from_tty)
 {
   /* Get target's idea of a bookmark.  */
   gdb_byte *bookmark_id = target_get_bookmark (args, from_tty);
-  struct bookmark *b, *b1;
   struct gdbarch *gdbarch = get_regcache_arch (get_current_regcache ());
 
   /* CR should not cause another identical bookmark.  */
@@ -131,9 +130,8 @@ save_bookmark_command (char *args, int from_tty)
     error (_("target_get_bookmark failed."));
 
   /* Set up a bookmark struct.  */
-  b = XCNEW (struct bookmark);
+  bookmark *b = new bookmark ();
   b->number = ++bookmark_count;
-  init_sal (&b->sal);
   b->pc = regcache_read_pc (get_current_regcache ());
   b->sal = find_pc_line (b->pc, 0);
   b->sal.pspace = get_frame_program_space (get_current_frame ());
@@ -143,7 +141,7 @@ save_bookmark_command (char *args, int from_tty)
   /* Add this bookmark to the end of the chain, so that a list
      of bookmarks will come out in order of increasing numbers.  */
 
-  b1 = bookmark_chain;
+  bookmark *b1 = bookmark_chain;
   if (b1 == 0)
     bookmark_chain = b;
   else
@@ -183,7 +181,7 @@ delete_one_bookmark (int num)
 	    break;
 	  }
       xfree (b->opaque_data);
-      xfree (b);
+      delete b;
       return 1;		/* success */
     }
   return 0;		/* failure */
diff --git a/gdb/source.c b/gdb/source.c
index 0c44879..11ba8be 100644
--- a/gdb/source.c
+++ b/gdb/source.c
@@ -182,7 +182,7 @@ get_lines_to_list (void)
 struct symtab_and_line
 get_current_source_symtab_and_line (void)
 {
-  struct symtab_and_line cursal = { 0 };
+  symtab_and_line cursal;
 
   cursal.pspace = current_source_pspace;
   cursal.symtab = current_source_symtab;
@@ -218,9 +218,9 @@ set_default_source_symtab_and_line (void)
    NOTE: The returned sal pc and end fields are not valid.  */
    
 struct symtab_and_line
-set_current_source_symtab_and_line (const struct symtab_and_line *sal)
+set_current_source_symtab_and_line (const symtab_and_line &sal)
 {
-  struct symtab_and_line cursal = { 0 };
+  symtab_and_line cursal;
 
   cursal.pspace = current_source_pspace;
   cursal.symtab = current_source_symtab;
@@ -228,9 +228,9 @@ set_current_source_symtab_and_line (const struct symtab_and_line *sal)
   cursal.pc = 0;
   cursal.end = 0;
 
-  current_source_pspace = sal->pspace;
-  current_source_symtab = sal->symtab;
-  current_source_line = sal->line;
+  current_source_pspace = sal.pspace;
+  current_source_symtab = sal.symtab;
+  current_source_line = sal.line;
 
   /* Force the next "list" to center around the current line.  */
   clear_lines_listed_range ();
@@ -1510,7 +1510,6 @@ line_info (char *arg, int from_tty)
 
   if (arg == 0)
     {
-      init_sal (&curr_sal);		/* initialize to zeroes */
       curr_sal.symtab = current_source_symtab;
       curr_sal.pspace = current_program_space;
       if (last_line_listed != 0)
diff --git a/gdb/source.h b/gdb/source.h
index ee9f3fa..72fa0e2 100644
--- a/gdb/source.h
+++ b/gdb/source.h
@@ -91,7 +91,8 @@ extern void set_default_source_symtab_and_line (void);
    (the returned sal pc and end fields are not valid.)
    and set the current default to whatever is in SAL.
    NOTE: The returned sal pc and end fields are not valid.  */
-extern struct symtab_and_line set_current_source_symtab_and_line (const struct symtab_and_line *);
+extern symtab_and_line set_current_source_symtab_and_line
+  (const symtab_and_line &sal);
 
 /* Reset any information stored about a default file and line to print.  */
 extern void clear_current_source_symtab_and_line (void);
diff --git a/gdb/stack.c b/gdb/stack.c
index 82600ec..76d5364 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -723,11 +723,9 @@ print_frame_args (struct symbol *func, struct frame_info *frame,
 void
 set_current_sal_from_frame (struct frame_info *frame)
 {
-  struct symtab_and_line sal;
-
-  find_frame_sal (frame, &sal);
+  symtab_and_line sal = find_frame_sal (frame);
   if (sal.symtab != NULL)
-    set_current_source_symtab_and_line (&sal);
+    set_current_source_symtab_and_line (sal);
 }
 
 /* If ON, GDB will display disassembly of the next source line when
@@ -789,7 +787,6 @@ print_frame_info (struct frame_info *frame, int print_level,
 		  int set_current_sal)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct symtab_and_line sal;
   int source_print;
   int location_print;
   struct ui_out *uiout = current_uiout;
@@ -852,7 +849,7 @@ print_frame_info (struct frame_info *frame, int print_level,
      the next frame is a SIGTRAMP_FRAME or a DUMMY_FRAME, then the
      next frame was not entered as the result of a call, and we want
      to get the line containing FRAME->pc.  */
-  find_frame_sal (frame, &sal);
+  symtab_and_line sal = find_frame_sal (frame);
 
   location_print = (print_what == LOCATION 
 		    || print_what == LOC_AND_ADDRESS
@@ -1017,23 +1014,20 @@ get_last_displayed_line (void)
 
 /* Get the last sal we displayed, if it's valid.  */
 
-void
-get_last_displayed_sal (struct symtab_and_line *sal)
+symtab_and_line
+get_last_displayed_sal ()
 {
+  symtab_and_line sal;
+
   if (last_displayed_sal_valid)
     {
-      sal->pspace = last_displayed_pspace;
-      sal->pc = last_displayed_addr;
-      sal->symtab = last_displayed_symtab;
-      sal->line = last_displayed_line;
-    }
-  else
-    {
-      sal->pspace = 0;
-      sal->pc = 0;
-      sal->symtab = 0;
-      sal->line = 0;
+      sal.pspace = last_displayed_pspace;
+      sal.pc = last_displayed_addr;
+      sal.symtab = last_displayed_symtab;
+      sal.line = last_displayed_line;
     }
+
+  return sal;
 }
 
 
@@ -1399,7 +1393,6 @@ static void
 frame_info (char *addr_exp, int from_tty)
 {
   struct frame_info *fi;
-  struct symtab_and_line sal;
   struct symbol *func;
   struct symtab *s;
   struct frame_info *calling_frame_info;
@@ -1434,8 +1427,8 @@ frame_info (char *addr_exp, int from_tty)
     pc_regname = "pc";
 
   frame_pc_p = get_frame_pc_if_available (fi, &frame_pc);
-  find_frame_sal (fi, &sal);
   func = get_frame_function (fi);
+  symtab_and_line sal = find_frame_sal (fi);
   s = sal.symtab;
   if (func)
     {
diff --git a/gdb/stack.h b/gdb/stack.h
index 1583200..f41d21e 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -45,6 +45,6 @@ struct program_space* get_last_displayed_pspace (void);
 CORE_ADDR get_last_displayed_addr (void);
 struct symtab* get_last_displayed_symtab (void);
 int get_last_displayed_line (void);
-void get_last_displayed_sal (struct symtab_and_line *sal);
+symtab_and_line get_last_displayed_sal ();
 
 #endif /* #ifndef STACK_H */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index ccf31cc..ce59e31 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -947,14 +947,6 @@ symbol_search_name (const struct general_symbol_info *gsymbol)
   else
     return symbol_natural_name (gsymbol);
 }
-
-/* Initialize the structure fields to zero values.  */
-
-void
-init_sal (struct symtab_and_line *sal)
-{
-  memset (sal, 0, sizeof (*sal));
-}
 \f
 
 /* Return 1 if the two sections are the same, or if they could
@@ -2938,7 +2930,6 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
   int len;
   int i;
   struct linetable_entry *item;
-  struct symtab_and_line val;
   const struct blockvector *bv;
   struct bound_minimal_symbol msymbol;
 
@@ -2965,10 +2956,6 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
      But what we want is the statement containing the instruction.
      Fudge the pc to make sure we get that.  */
 
-  init_sal (&val);		/* initialize to zeroes */
-
-  val.pspace = current_program_space;
-
   /* It's tempting to assume that, if we can't find debugging info for
      any function enclosing PC, that we shouldn't search for line
      number info, either.  However, GAS can emit line number info for
@@ -3057,6 +3044,8 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
 	  return find_pc_line (BMSYMBOL_VALUE_ADDRESS (mfunsym), 0);
       }
 
+  symtab_and_line val;
+  val.pspace = current_program_space;
 
   cust = find_pc_sect_compunit_symtab (pc, section);
   if (cust == NULL)
@@ -3469,12 +3458,11 @@ find_pc_line_pc_range (CORE_ADDR pc, CORE_ADDR *startptr, CORE_ADDR *endptr)
 struct symtab_and_line
 find_function_start_sal (struct symbol *sym, int funfirstline)
 {
-  struct symtab_and_line sal;
-  struct obj_section *section;
-
   fixup_symbol_section (sym, NULL);
-  section = SYMBOL_OBJ_SECTION (symbol_objfile (sym), sym);
-  sal = find_pc_sect_line (BLOCK_START (SYMBOL_BLOCK_VALUE (sym)), section, 0);
+
+  obj_section *section = SYMBOL_OBJ_SECTION (symbol_objfile (sym), sym);
+  symtab_and_line sal
+    = find_pc_sect_line (BLOCK_START (SYMBOL_BLOCK_VALUE (sym)), section, 0);
 
   if (funfirstline && sal.symtab != NULL
       && (COMPUNIT_LOCATIONS_VALID (SYMTAB_COMPUNIT (sal.symtab))
@@ -3494,7 +3482,7 @@ find_function_start_sal (struct symbol *sym, int funfirstline)
      can find a line number for after the prologue.  */
   if (sal.pc < BLOCK_START (SYMBOL_BLOCK_VALUE (sym)))
     {
-      init_sal (&sal);
+      sal = {};
       sal.pspace = current_program_space;
       sal.pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
       sal.section = section;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 37cfe05..36b5606 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1418,29 +1418,27 @@ extern CORE_ADDR find_solib_trampoline_target (struct frame_info *, CORE_ADDR);
 struct symtab_and_line
 {
   /* The program space of this sal.  */
-  struct program_space *pspace;
+  struct program_space *pspace = NULL;
 
-  struct symtab *symtab;
-  struct obj_section *section;
+  struct symtab *symtab = NULL;
+  struct obj_section *section = NULL;
   /* Line number.  Line numbers start at 1 and proceed through symtab->nlines.
      0 is never a valid line number; it is used to indicate that line number
      information is not available.  */
-  int line;
+  int line = 0;
 
-  CORE_ADDR pc;
-  CORE_ADDR end;
-  int explicit_pc;
-  int explicit_line;
+  CORE_ADDR pc = 0;
+  CORE_ADDR end = 0;
+  bool explicit_pc = false;
+  bool explicit_line = false;
 
   /* The probe associated with this symtab_and_line.  */
-  struct probe *probe;
+  struct probe *probe = NULL;
   /* If PROBE is not NULL, then this is the objfile in which the probe
      originated.  */
-  struct objfile *objfile;
+  struct objfile *objfile = NULL;
 };
 
-extern void init_sal (struct symtab_and_line *sal);
-
 \f
 
 /* Given a pc value, return line number it is in.  Second arg nonzero means
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 8f4de36..bcc0cd2 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -253,7 +253,7 @@ set_traceframe_context (struct frame_info *trace_frame)
 {
   CORE_ADDR trace_pc;
   struct symbol *traceframe_fun;
-  struct symtab_and_line traceframe_sal;
+  symtab_and_line traceframe_sal;
 
   /* Save as globals for internal use.  */
   if (trace_frame != NULL
@@ -269,7 +269,6 @@ set_traceframe_context (struct frame_info *trace_frame)
     }
   else
     {
-      init_sal (&traceframe_sal);
       traceframe_fun = NULL;
       set_internalvar_integer (lookup_internalvar ("trace_line"), -1);
     }
@@ -3818,10 +3817,7 @@ print_one_static_tracepoint_marker (int count,
   struct ui_out *uiout = current_uiout;
   VEC(breakpoint_p) *tracepoints;
 
-  struct symtab_and_line sal;
-
-  init_sal (&sal);
-
+  symtab_and_line sal;
   sal.pc = marker->address;
 
   tracepoints = static_tracepoints_here (marker->address);
diff --git a/gdb/tui/tui-disasm.c b/gdb/tui/tui-disasm.c
index 123a906..a763832 100644
--- a/gdb/tui/tui-disasm.c
+++ b/gdb/tui/tui-disasm.c
@@ -305,7 +305,7 @@ tui_show_disassem_and_update_source (struct gdbarch *gdbarch,
       tui_update_source_window (TUI_SRC_WIN, gdbarch, sal.symtab, val, TRUE);
       if (sal.symtab)
 	{
-	  set_current_source_symtab_and_line (&sal);
+	  set_current_source_symtab_and_line (sal);
 	  tui_update_locator_fullname (symtab_to_fullname (sal.symtab));
 	}
       else
diff --git a/gdb/tui/tui-stack.c b/gdb/tui/tui-stack.c
index 21a8bac..c075bef 100644
--- a/gdb/tui/tui-stack.c
+++ b/gdb/tui/tui-stack.c
@@ -366,10 +366,9 @@ tui_show_frame_info (struct frame_info *fi)
       CORE_ADDR low;
       struct tui_gen_win_info *locator = tui_locator_win_info_ptr ();
       int source_already_displayed;
-      struct symtab_and_line sal;
       CORE_ADDR pc;
 
-      find_frame_sal (fi, &sal);
+      symtab_and_line sal = find_frame_sal (fi);
 
       source_already_displayed = sal.symtab != 0
         && tui_source_is_displayed (symtab_to_fullname (sal.symtab));
diff --git a/gdb/tui/tui-winsource.c b/gdb/tui/tui-winsource.c
index 3ae9c71..103b1e6 100644
--- a/gdb/tui/tui-winsource.c
+++ b/gdb/tui/tui-winsource.c
@@ -109,14 +109,13 @@ tui_update_source_window_as_is (struct tui_win_info *win_info,
       tui_update_exec_info (win_info);
       if (win_info->generic.type == SRC_WIN)
 	{
-	  struct symtab_and_line sal;
-	  
-	  init_sal (&sal);
+	  symtab_and_line sal;
+
 	  sal.line = line_or_addr.u.line_no +
 	    (win_info->generic.content_size - 2);
 	  sal.symtab = s;
 	  sal.pspace = SYMTAB_PSPACE (s);
-	  set_current_source_symtab_and_line (&sal);
+	  set_current_source_symtab_and_line (sal);
 	  /* If the focus was in the asm win, put it in the src win if
 	     we don't have a split layout.  */
 	  if (tui_win_with_focus () == TUI_DISASM_WIN
-- 
2.5.5

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/3] struct symtabs_and_lines -> std::vector<symtab_and_line>
  2017-08-21 19:27 [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector Pedro Alves
  2017-08-21 19:27 ` [PATCH 1/3] Introduce gdb::array_view Pedro Alves
  2017-08-21 19:28 ` [PATCH 3/3] Kill init_sal Pedro Alves
@ 2017-08-21 19:28 ` Pedro Alves
  2017-09-04 16:15 ` [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector Pedro Alves
  3 siblings, 0 replies; 6+ messages in thread
From: Pedro Alves @ 2017-08-21 19:28 UTC (permalink / raw)
  To: gdb-patches

This replaces "struct symtabs_and_lines" with
std::vector<symtab_and_line> in most cases.  This removes a number of
cleanups.

In some cases, the sals objects do not own the sals they point at.
Instead they point at some sal that lives on the stack.  Typically
something like this:

  struct symtab_and_line sal;
  struct symtabs_and_lines sals;

  // fill in sal

  sals.nelts = 1;
  sals.sals = &sal;

  // use sals

Instead of switching those cases to std::vector too, such usages are
replaced by gdb::array_view<symtab_and_line> instead.  This avoids
introducing heap allocations.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ax-gdb.c (agent_command_1): Use range-for.
	* break-catch-throw.c (re_set_exception_catchpoint): Update.
	* breakpoint.c: Include "common/array-view.h".
	(init_breakpoint_sal, create_breakpoint_sal): Change sals
	parameter from struct symtabs_and_lines to
	array_view<symtab_and_line>.  Adjust.  Use range-for.  Update.
	(breakpoint_sals_to_pc): Change sals parameter from struct
	symtabs_and_lines to std::vector reference.
	(check_fast_tracepoint_sals): Change sals parameter from struct
	symtabs_and_lines to std::array_view.  Use range-for.
	(decode_static_tracepoint_spec): Return a std::vector instead of
	symtabs_and_lines.  Update.
	(create_breakpoint): Update.
	(break_range_command, until_break_command, clear_command): Update.
	(base_breakpoint_decode_location, bkpt_decode_location)
	(bkpt_probe_create_sals_from_location)
	(bkpt_probe_decode_location, tracepoint_decode_location)
	(tracepoint_probe_decode_location)
	(strace_marker_create_sals_from_location): Return a std::vector
	instead of symtabs_and_lines.
	(strace_marker_create_breakpoints_sal): Update.
	(strace_marker_decode_location): Return a std::vector instead of
	symtabs_and_lines.  Update.
	(update_breakpoint_locations): Change struct symtabs_and_lines
	parameters to gdb::array_view.  Adjust.
	(location_to_sals): Return a std::vector instead of
	symtabs_and_lines.  Update.
	(breakpoint_re_set_default): Use std::vector instead of struct
	symtabs_and_lines.
	(decode_location_default): Return a std::vector instead of
	symtabs_and_lines.  Update.
	* breakpoint.h: Include "common/array-view.h".
	(struct breakpoint_ops) <decode_location>: Now returns a
	std::vector instead of returning a symtabs_and_lines via output
	parameter.
	(update_breakpoint_locations): Change sals parameters to use
	gdb::array_view.
	* cli/cli-cmds.c (edit_command, list_command): Update to use
	std::vector and gdb::array_view.
	(ambiguous_line_spec): Adjust to use gdb::array_view and
	range-for.
	(compare_symtabs): Rename to ...
	(cmp_symtabs): ... this.  Change parameters to symtab_and_line
	const reference and adjust.
	(filter_sals): Rewrite using std::vector and standard algorithms.
	* elfread.c (elf_gnu_ifunc_resolver_return_stop): Simplify.
	(jump_command): Update to use std::vector.
	* linespec.c (struct linespec_state) <canonical_names>: Update
	comment.
	(add_sal_to_sals_basic): Delete.
	(add_sal_to_sals, filter_results, convert_results_to_lsals)
	(decode_line_2, create_sals_line_offset)
	(convert_address_location_to_sals, convert_linespec_to_sals)
	(convert_explicit_location_to_sals, parse_linespec)
	(event_location_to_sals, decode_line_full, decode_line_1)
	(decode_line_with_current_source)
	(decode_line_with_last_displayed, decode_objc)
	(decode_digits_list_mode, decode_digits_ordinary, minsym_found)
	(linespec_result::~linespec_result): Adjust to use std::vector
	instead of symtabs_and_lines.
	* linespec.h (linespec_sals::sals): Now a std::vector.
	(struct linespec_result): Use std::vector, bool, and in-class
	initialization.
	(decode_line_1, decode_line_with_current_source)
	(decode_line_with_last_displayed): Return std::vector.
	* macrocmd.c (info_macros_command): Use std::vector.
	* mi/mi-main.c (mi_cmd_trace_find): Use std::vector.
	* probe.c (parse_probes_in_pspace, parse_probes): Adjust to use
	std::vector.
	* probe.h (parse_probes): Return a std::vector.
	* python/python.c (gdbpy_decode_line): Use std::vector and
	gdb::array_view.
	* source.c (select_source_symtab, line_info): Use std::vector.
	* stack.c (func_command): Use std::vector.
	* symtab.h (struct symtabs_and_lines): Delete.
	* tracepoint.c (tfind_line_command, scope_info): Use std::vector.
---
 gdb/ax-gdb.c            |  12 +-
 gdb/break-catch-throw.c |  12 +-
 gdb/breakpoint.c        | 331 +++++++++++++++++++++---------------------------
 gdb/breakpoint.h        |  20 +--
 gdb/cli/cli-cmds.c      | 151 +++++++++-------------
 gdb/elfread.c           |  10 +-
 gdb/infcmd.c            |  16 +--
 gdb/linespec.c          | 252 ++++++++++++++++--------------------
 gdb/linespec.h          |  32 ++---
 gdb/macrocmd.c          |   8 +-
 gdb/mi/mi-main.c        |  17 +--
 gdb/probe.c             |  34 ++---
 gdb/probe.h             |  14 +-
 gdb/python/python.c     |  47 +++----
 gdb/source.c            |  43 +++----
 gdb/stack.c             |  21 ++-
 gdb/symtab.h            |   5 -
 gdb/tracepoint.c        |  31 ++---
 18 files changed, 434 insertions(+), 622 deletions(-)

diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c
index 91bc4b8..a72a458 100644
--- a/gdb/ax-gdb.c
+++ b/gdb/ax-gdb.c
@@ -2577,8 +2577,6 @@ agent_command_1 (char *exp, int eval)
   if (check_for_argument (&exp, "-at", sizeof ("-at") - 1))
     {
       struct linespec_result canonical;
-      int ix;
-      struct linespec_sals *iter;
 
       exp = skip_spaces (exp);
 
@@ -2592,13 +2590,9 @@ agent_command_1 (char *exp, int eval)
 	  exp++;
 	  exp = skip_spaces (exp);
 	}
-      for (ix = 0; VEC_iterate (linespec_sals, canonical.sals, ix, iter); ++ix)
-        {
-	  int i;
-
-	  for (i = 0; i < iter->sals.nelts; i++)
-	    agent_eval_command_one (exp, eval, iter->sals.sals[i].pc);
-        }
+      for (const auto &lsal : canonical.lsals)
+	for (const auto &sal : lsal.sals)
+	  agent_eval_command_one (exp, eval, sal.pc);
     }
   else
     agent_eval_command_one (exp, eval, get_frame_pc (get_current_frame ()));
diff --git a/gdb/break-catch-throw.c b/gdb/break-catch-throw.c
index 5318e5f..e05834a 100644
--- a/gdb/break-catch-throw.c
+++ b/gdb/break-catch-throw.c
@@ -184,9 +184,7 @@ check_status_exception_catchpoint (struct bpstats *bs)
 static void
 re_set_exception_catchpoint (struct breakpoint *self)
 {
-  struct symtabs_and_lines sals = {0};
-  struct symtabs_and_lines sals_end = {0};
-  struct cleanup *cleanup;
+  std::vector<symtab_and_line> sals;
   enum exception_event_kind kind = classify_exception_breakpoint (self);
   struct program_space *filter_pspace = current_program_space;
 
@@ -209,8 +207,8 @@ re_set_exception_catchpoint (struct breakpoint *self)
 	  explicit_loc.function_name
 	    = ASTRDUP (exception_functions[kind].function);
 	  event_location_up location = new_explicit_location (&explicit_loc);
-	  self->ops->decode_location (self, location.get (), filter_pspace,
-				      &sals);
+	  sals = self->ops->decode_location (self, location.get (),
+					     filter_pspace);
 	}
       CATCH (ex, RETURN_MASK_ERROR)
 	{
@@ -223,9 +221,7 @@ re_set_exception_catchpoint (struct breakpoint *self)
     }
   END_CATCH
 
-  cleanup = make_cleanup (xfree, sals.sals);
-  update_breakpoint_locations (self, filter_pspace, sals, sals_end);
-  do_cleanups (cleanup);
+  update_breakpoint_locations (self, filter_pspace, sals, {});
 }
 
 static enum print_stop_action
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index bc681cf..f7d404d 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -81,6 +81,7 @@
 #include "extension.h"
 #include <algorithm>
 #include "progspace-and-thread.h"
+#include "common/array-view.h"
 
 /* Enums for exception-handling support.  */
 enum exception_event_kind
@@ -128,10 +129,9 @@ static void create_breakpoints_sal_default (struct gdbarch *,
 					    const struct breakpoint_ops *,
 					    int, int, int, unsigned);
 
-static void decode_location_default (struct breakpoint *b,
-				     const struct event_location *location,
-				     struct program_space *search_pspace,
-				     struct symtabs_and_lines *sals);
+static std::vector<symtab_and_line> decode_location_default
+  (struct breakpoint *b, const struct event_location *location,
+   struct program_space *search_pspace);
 
 static void clear_command (char *, int);
 
@@ -9148,7 +9148,7 @@ update_dprintf_commands (char *args, int from_tty,
 
 static void
 init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
-		     struct symtabs_and_lines sals,
+		     gdb::array_view<const symtab_and_line> sals,
 		     event_location_up &&location,
 		     gdb::unique_xmalloc_ptr<char> filter,
 		     gdb::unique_xmalloc_ptr<char> cond_string,
@@ -9175,11 +9175,10 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
 	error (_("Hardware breakpoints used exceeds limit."));
     }
 
-  gdb_assert (sals.nelts > 0);
+  gdb_assert (!sals.empty ());
 
-  for (i = 0; i < sals.nelts; ++i)
+  for (const auto &sal : sals)
     {
-      struct symtab_and_line sal = sals.sals[i];
       struct bp_location *loc;
 
       if (from_tty)
@@ -9192,7 +9191,7 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
 				      sal.pspace, sal.pc, sal.section, thread);
 	}
 
-      if (i == 0)
+      if (&sal == &sals[0])
 	{
 	  init_raw_breakpoint (b, gdbarch, sal, type, ops);
 	  b->thread = thread;
@@ -9288,7 +9287,7 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
 
 static void
 create_breakpoint_sal (struct gdbarch *gdbarch,
-		       struct symtabs_and_lines sals,
+		       gdb::array_view<const symtab_and_line> sals,
 		       event_location_up &&location,
 		       gdb::unique_xmalloc_ptr<char> filter,
 		       gdb::unique_xmalloc_ptr<char> cond_string,
@@ -9340,13 +9339,10 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 			const struct breakpoint_ops *ops, int from_tty,
 			int enabled, int internal, unsigned flags)
 {
-  int i;
-  struct linespec_sals *lsal;
-
   if (canonical->pre_expanded)
-    gdb_assert (VEC_length (linespec_sals, canonical->sals) == 1);
+    gdb_assert (canonical->lsals.size () == 1);
 
-  for (i = 0; VEC_iterate (linespec_sals, canonical->sals, i, lsal); ++i)
+  for (const auto &lsal : canonical->lsals)
     {
       /* Note that 'location' can be NULL in the case of a plain
 	 'break', without arguments.  */
@@ -9354,9 +9350,9 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 	= (canonical->location != NULL
 	   ? copy_event_location (canonical->location.get ()) : NULL);
       gdb::unique_xmalloc_ptr<char> filter_string
-	(lsal->canonical != NULL ? xstrdup (lsal->canonical) : NULL);
+	(lsal.canonical != NULL ? xstrdup (lsal.canonical) : NULL);
 
-      create_breakpoint_sal (gdbarch, lsal->sals,
+      create_breakpoint_sal (gdbarch, lsal.sals,
 			     std::move (location),
 			     std::move (filter_string),
 			     std::move (cond_string),
@@ -9392,12 +9388,10 @@ parse_breakpoint_sals (const struct event_location *location,
 	     breakpoint address.  */
 	  if (last_displayed_sal_is_valid ())
 	    {
-	      struct linespec_sals lsal;
 	      struct symtab_and_line sal;
 	      CORE_ADDR pc;
 
 	      init_sal (&sal);		/* Initialize to zeroes.  */
-	      lsal.sals.sals = XNEW (struct symtab_and_line);
 
 	      /* Set sal's pspace, pc, symtab, and line to the values
 		 corresponding to the last call to print_frame_info.
@@ -9416,11 +9410,11 @@ parse_breakpoint_sals (const struct event_location *location,
 	      sal.pc = pc;
 	      sal.explicit_pc = 1;
 
-	      lsal.sals.sals[0] = sal;
-	      lsal.sals.nelts = 1;
+	      struct linespec_sals lsal;
+	      lsal.sals = {sal};
 	      lsal.canonical = NULL;
 
-	      VEC_safe_push (linespec_sals, canonical->sals, &lsal);
+	      canonical->lsals.push_back (std::move (lsal));
 	      return;
 	    }
 	  else
@@ -9465,12 +9459,10 @@ parse_breakpoint_sals (const struct event_location *location,
    inserted as a breakpoint.  If it can't throw an error.  */
 
 static void
-breakpoint_sals_to_pc (struct symtabs_and_lines *sals)
+breakpoint_sals_to_pc (std::vector<symtab_and_line> &sals)
 {    
-  int i;
-
-  for (i = 0; i < sals->nelts; i++)
-    resolve_sal_pc (&sals->sals[i]);
+  for (auto &sal : sals)
+    resolve_sal_pc (&sal);
 }
 
 /* Fast tracepoints may have restrictions on valid locations.  For
@@ -9482,30 +9474,27 @@ breakpoint_sals_to_pc (struct symtabs_and_lines *sals)
 
 static void
 check_fast_tracepoint_sals (struct gdbarch *gdbarch,
-			    struct symtabs_and_lines *sals)
+			    gdb::array_view<const symtab_and_line> sals)
 {
-  int i, rslt;
-  struct symtab_and_line *sal;
+  int rslt;
   char *msg;
   struct cleanup *old_chain;
 
-  for (i = 0; i < sals->nelts; i++)
+  for (const auto &sal : sals)
     {
       struct gdbarch *sarch;
 
-      sal = &sals->sals[i];
-
-      sarch = get_sal_arch (*sal);
+      sarch = get_sal_arch (sal);
       /* We fall back to GDBARCH if there is no architecture
 	 associated with SAL.  */
       if (sarch == NULL)
 	sarch = gdbarch;
-      rslt = gdbarch_fast_tracepoint_valid_at (sarch, sal->pc, &msg);
+      rslt = gdbarch_fast_tracepoint_valid_at (sarch, sal.pc, &msg);
       old_chain = make_cleanup (xfree, msg);
 
       if (!rslt)
 	error (_("May not have a fast tracepoint at %s%s"),
-	       paddress (sarch, sal->pc), (msg ? msg : ""));
+	       paddress (sarch, sal.pc), (msg ? msg : ""));
 
       do_cleanups (old_chain);
     }
@@ -9590,11 +9579,10 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc,
 
 /* Decode a static tracepoint marker spec.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 decode_static_tracepoint_spec (const char **arg_p)
 {
   VEC(static_tracepoint_marker_p) *markers = NULL;
-  struct symtabs_and_lines sals;
   struct cleanup *old_chain;
   const char *p = &(*arg_p)[3];
   const char *endp;
@@ -9612,19 +9600,21 @@ decode_static_tracepoint_spec (const char **arg_p)
   if (VEC_empty(static_tracepoint_marker_p, markers))
     error (_("No known static tracepoint marker named %s"), marker_str);
 
-  sals.nelts = VEC_length(static_tracepoint_marker_p, markers);
-  sals.sals = XNEWVEC (struct symtab_and_line, sals.nelts);
+  std::vector<symtab_and_line> sals;
+  sals.reserve (VEC_length(static_tracepoint_marker_p, markers));
 
-  for (i = 0; i < sals.nelts; i++)
+  for (i = 0; i < VEC_length(static_tracepoint_marker_p, markers); i++)
     {
       struct static_tracepoint_marker *marker;
 
       marker = VEC_index (static_tracepoint_marker_p, markers, i);
 
-      init_sal (&sals.sals[i]);
+      symtab_and_line sal;
+      init_sal (&sal);
 
-      sals.sals[i] = find_pc_line (marker->address, 0);
-      sals.sals[i].pc = marker->address;
+      sal = find_pc_line (marker->address, 0);
+      sal.pc = marker->address;
+      sals.push_back (sal);
 
       release_static_tracepoint_marker (marker);
     }
@@ -9698,7 +9688,7 @@ create_breakpoint (struct gdbarch *gdbarch,
     }
   END_CATCH
 
-  if (!pending && VEC_empty (linespec_sals, canonical.sals))
+  if (!pending && canonical.lsals.empty ())
     return 0;
 
   /* ----------------------------- SNIP -----------------------------
@@ -9711,21 +9701,15 @@ create_breakpoint (struct gdbarch *gdbarch,
      are ok for the target.  */
   if (!pending)
     {
-      int ix;
-      struct linespec_sals *iter;
-
-      for (ix = 0; VEC_iterate (linespec_sals, canonical.sals, ix, iter); ++ix)
-	breakpoint_sals_to_pc (&iter->sals);
+      for (auto &lsal : canonical.lsals)
+	breakpoint_sals_to_pc (lsal.sals);
     }
 
   /* Fast tracepoints may have additional restrictions on location.  */
   if (!pending && type_wanted == bp_fast_tracepoint)
     {
-      int ix;
-      struct linespec_sals *iter;
-
-      for (ix = 0; VEC_iterate (linespec_sals, canonical.sals, ix, iter); ++ix)
-	check_fast_tracepoint_sals (gdbarch, &iter->sals);
+      for (const auto &lsal : canonical.lsals)
+	check_fast_tracepoint_sals (gdbarch, lsal.sals);
     }
 
   /* Verify that condition can be parsed, before setting any
@@ -9740,16 +9724,15 @@ create_breakpoint (struct gdbarch *gdbarch,
         {
 	  char *rest;
 	  char *cond;
-	  struct linespec_sals *lsal;
 
-	  lsal = VEC_index (linespec_sals, canonical.sals, 0);
+	  const linespec_sals &lsal = canonical.lsals[0];
 
 	  /* Here we only parse 'arg' to separate condition
 	     from thread number, so parsing in context of first
 	     sal is OK.  When setting the breakpoint we'll
 	     re-parse it in context of each sal.  */
 
-	  find_condition_and_thread (extra_string, lsal->sals.sals[0].pc,
+	  find_condition_and_thread (extra_string, lsal.sals[0].pc,
 				     &cond, &thread, &task, &rest);
 	  cond_string_copy.reset (cond);
 	  extra_string_copy.reset (rest);
@@ -9805,7 +9788,7 @@ create_breakpoint (struct gdbarch *gdbarch,
       install_breakpoint (internal, b.release (), 0);
     }
   
-  if (VEC_length (linespec_sals, canonical.sals) > 1)
+  if (canonical.lsals.size () > 1)
     {
       warning (_("Multiple breakpoints were set.\nUse the "
 		 "\"delete\" command to delete unwanted breakpoints."));
@@ -10240,9 +10223,7 @@ break_range_command (char *arg, int from_tty)
   int bp_count, can_use_bp, length;
   CORE_ADDR end;
   struct breakpoint *b;
-  struct symtab_and_line sal_start, sal_end;
   struct cleanup *cleanup_bkpt;
-  struct linespec_sals *lsal_start, *lsal_end;
 
   /* We don't support software ranged breakpoints.  */
   if (target_ranged_break_num_registers () < 0)
@@ -10266,16 +10247,16 @@ break_range_command (char *arg, int from_tty)
 
   if (arg[0] != ',')
     error (_("Too few arguments."));
-  else if (VEC_empty (linespec_sals, canonical_start.sals))
+  else if (canonical_start.lsals.empty ())
     error (_("Could not find location of the beginning of the range."));
 
-  lsal_start = VEC_index (linespec_sals, canonical_start.sals, 0);
+  const linespec_sals &lsal_start = canonical_start.lsals[0];
 
-  if (VEC_length (linespec_sals, canonical_start.sals) > 1
-      || lsal_start->sals.nelts != 1)
+  if (canonical_start.lsals.size () > 1
+      || lsal_start.sals.size () != 1)
     error (_("Cannot create a ranged breakpoint with multiple locations."));
 
-  sal_start = lsal_start->sals.sals[0];
+  const symtab_and_line &sal_start = lsal_start.sals[0];
   addr_string_start = savestring (arg_start, arg - arg_start);
   cleanup_bkpt = make_cleanup (xfree, addr_string_start);
 
@@ -10297,15 +10278,15 @@ break_range_command (char *arg, int from_tty)
 		    sal_start.symtab, sal_start.line,
 		    &canonical_end, NULL, NULL);
 
-  if (VEC_empty (linespec_sals, canonical_end.sals))
+  if (canonical_end.lsals.empty ())
     error (_("Could not find location of the end of the range."));
 
-  lsal_end = VEC_index (linespec_sals, canonical_end.sals, 0);
-  if (VEC_length (linespec_sals, canonical_end.sals) > 1
-      || lsal_end->sals.nelts != 1)
+  const linespec_sals &lsal_end = canonical_end.lsals[0];
+  if (canonical_end.lsals.size () > 1
+      || lsal_end.sals.size () != 1)
     error (_("Cannot create a ranged breakpoint with multiple locations."));
 
-  sal_end = lsal_end->sals.sals[0];
+  const symtab_and_line &sal_end = lsal_end.sals[0];
 
   end = find_breakpoint_range_end (sal_end);
   if (sal_start.pc > end)
@@ -11521,8 +11502,6 @@ until_break_fsm_async_reply_reason (struct thread_fsm *self)
 void
 until_break_command (char *arg, int from_tty, int anywhere)
 {
-  struct symtabs_and_lines sals;
-  struct symtab_and_line sal;
   struct frame_info *frame;
   struct gdbarch *frame_gdbarch;
   struct frame_id stack_frame_id;
@@ -11541,19 +11520,18 @@ until_break_command (char *arg, int from_tty, int anywhere)
 
   event_location_up location = string_to_event_location (&arg, current_language);
 
-  if (last_displayed_sal_is_valid ())
-    sals = decode_line_1 (location.get (), DECODE_LINE_FUNFIRSTLINE, NULL,
-			  get_last_displayed_symtab (),
-			  get_last_displayed_line ());
-  else
-    sals = decode_line_1 (location.get (), DECODE_LINE_FUNFIRSTLINE,
-			  NULL, (struct symtab *) NULL, 0);
+  std::vector<symtab_and_line> sals
+    = (last_displayed_sal_is_valid ()
+       ? decode_line_1 (location.get (), DECODE_LINE_FUNFIRSTLINE, NULL,
+			get_last_displayed_symtab (),
+			get_last_displayed_line ())
+       : decode_line_1 (location.get (), DECODE_LINE_FUNFIRSTLINE,
+			NULL, (struct symtab *) NULL, 0));
 
-  if (sals.nelts != 1)
+  if (sals.size () != 1)
     error (_("Couldn't get information on specified line."));
 
-  sal = sals.sals[0];
-  xfree (sals.sals);	/* malloc'd, so freed.  */
+  symtab_and_line &sal = sals[0];
 
   if (*arg)
     error (_("Junk at end of arguments."));
@@ -11824,36 +11802,34 @@ clear_command (char *arg, int from_tty)
   VEC(breakpoint_p) *found = 0;
   int ix;
   int default_match;
-  struct symtabs_and_lines sals;
-  struct symtab_and_line sal;
   int i;
   struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
 
+  std::vector<symtab_and_line> decoded_sals;
+  symtab_and_line last_sal;
+  gdb::array_view<symtab_and_line> sals;
   if (arg)
     {
-      sals = decode_line_with_current_source (arg,
-					      (DECODE_LINE_FUNFIRSTLINE
-					       | DECODE_LINE_LIST_MODE));
-      make_cleanup (xfree, sals.sals);
+      decoded_sals
+	= decode_line_with_current_source (arg,
+					   (DECODE_LINE_FUNFIRSTLINE
+					    | DECODE_LINE_LIST_MODE));
       default_match = 0;
+      sals = decoded_sals;
     }
   else
     {
-      sals.sals = XNEW (struct symtab_and_line);
-      make_cleanup (xfree, sals.sals);
-      init_sal (&sal);		/* Initialize to zeroes.  */
+      init_sal (&last_sal);		/* Initialize to zeroes.  */
 
       /* Set sal's line, symtab, pc, and pspace to the values
 	 corresponding to the last call to print_frame_info.  If the
 	 codepoint is not valid, this will set all the fields to 0.  */
-      get_last_displayed_sal (&sal);
-      if (sal.symtab == 0)
+      get_last_displayed_sal (&last_sal);
+      if (last_sal.symtab == 0)
 	error (_("No source file specified."));
 
-      sals.sals[0] = sal;
-      sals.nelts = 1;
-
       default_match = 1;
+      sals = last_sal;
     }
 
   /* We don't call resolve_sal_pc here.  That's not as bad as it
@@ -11880,7 +11856,7 @@ clear_command (char *arg, int from_tty)
 
   found = NULL;
   make_cleanup (VEC_cleanup (breakpoint_p), &found);
-  for (i = 0; i < sals.nelts; i++)
+  for (const auto &sal : sals)
     {
       const char *sal_fullname;
 
@@ -11896,7 +11872,6 @@ clear_command (char *arg, int from_tty)
          0              0             line
          1              0             <can't happen> */
 
-      sal = sals.sals[i];
       sal_fullname = (sal.symtab == NULL
 		      ? NULL : symtab_to_fullname (sal.symtab));
 
@@ -12826,11 +12801,10 @@ base_breakpoint_create_breakpoints_sal (struct gdbarch *gdbarch,
   internal_error_pure_virtual_called ();
 }
 
-static void
+static std::vector<symtab_and_line>
 base_breakpoint_decode_location (struct breakpoint *b,
 				 const struct event_location *location,
-				 struct program_space *search_pspace,
-				 struct symtabs_and_lines *sals)
+				 struct program_space *search_pspace)
 {
   internal_error_pure_virtual_called ();
 }
@@ -13081,13 +13055,12 @@ bkpt_create_breakpoints_sal (struct gdbarch *gdbarch,
 				  enabled, internal, flags);
 }
 
-static void
+static std::vector<symtab_and_line>
 bkpt_decode_location (struct breakpoint *b,
 		      const struct event_location *location,
-		      struct program_space *search_pspace,
-		      struct symtabs_and_lines *sals)
+		      struct program_space *search_pspace)
 {
-  decode_location_default (b, location, search_pspace, sals);
+  return decode_location_default (b, location, search_pspace);
 }
 
 /* Virtual table for internal breakpoints.  */
@@ -13272,18 +13245,18 @@ bkpt_probe_create_sals_from_location (const struct event_location *location,
   lsal.sals = parse_probes (location, NULL, canonical);
   lsal.canonical
     = xstrdup (event_location_to_string (canonical->location.get ()));
-  VEC_safe_push (linespec_sals, canonical->sals, &lsal);
+  canonical->lsals.push_back (std::move (lsal));
 }
 
-static void
+static std::vector<symtab_and_line>
 bkpt_probe_decode_location (struct breakpoint *b,
 			    const struct event_location *location,
-			    struct program_space *search_pspace,
-			    struct symtabs_and_lines *sals)
+			    struct program_space *search_pspace)
 {
-  *sals = parse_probes (location, search_pspace, NULL);
-  if (!sals->sals)
+  std::vector<symtab_and_line> sals = parse_probes (location, search_pspace, NULL);
+  if (sals.empty ())
     error (_("probe not found"));
+  return sals;
 }
 
 /* The breakpoint_ops structure to be used in tracepoints.  */
@@ -13401,13 +13374,12 @@ tracepoint_create_breakpoints_sal (struct gdbarch *gdbarch,
 				  enabled, internal, flags);
 }
 
-static void
+static std::vector<symtab_and_line>
 tracepoint_decode_location (struct breakpoint *b,
 			    const struct event_location *location,
-			    struct program_space *search_pspace,
-			    struct symtabs_and_lines *sals)
+			    struct program_space *search_pspace)
 {
-  decode_location_default (b, location, search_pspace, sals);
+  return decode_location_default (b, location, search_pspace);
 }
 
 struct breakpoint_ops tracepoint_breakpoint_ops;
@@ -13425,14 +13397,13 @@ tracepoint_probe_create_sals_from_location
   bkpt_probe_create_sals_from_location (location, canonical, type_wanted);
 }
 
-static void
+static std::vector<symtab_and_line>
 tracepoint_probe_decode_location (struct breakpoint *b,
 				  const struct event_location *location,
-				  struct program_space *search_pspace,
-				  struct symtabs_and_lines *sals)
+				  struct program_space *search_pspace)
 {
   /* We use the same method for breakpoint on probes.  */
-  bkpt_probe_decode_location (b, location, search_pspace, sals);
+  return bkpt_probe_decode_location (b, location, search_pspace);
 }
 
 static struct breakpoint_ops tracepoint_probe_breakpoint_ops;
@@ -13533,7 +13504,7 @@ strace_marker_create_sals_from_location (const struct event_location *location,
 
   lsal.canonical
     = xstrdup (event_location_to_string (canonical->location.get ()));
-  VEC_safe_push (linespec_sals, canonical->sals, &lsal);
+  canonical->lsals.push_back (std::move (lsal));
 }
 
 static void
@@ -13549,9 +13520,7 @@ strace_marker_create_breakpoints_sal (struct gdbarch *gdbarch,
 				      int from_tty, int enabled,
 				      int internal, unsigned flags)
 {
-  int i;
-  struct linespec_sals *lsal = VEC_index (linespec_sals,
-					  canonical->sals, 0);
+  const linespec_sals &lsal = canonical->lsals[0];
 
   /* If the user is creating a static tracepoint by marker id
      (strace -m MARKER_ID), then store the sals index, so that
@@ -13560,19 +13529,13 @@ strace_marker_create_breakpoints_sal (struct gdbarch *gdbarch,
      expand multiple locations for each sal, given than SALS
      already should contain all sals for MARKER_ID.  */
 
-  for (i = 0; i < lsal->sals.nelts; ++i)
+  for (size_t i = 0; i < lsal.sals.size (); i++)
     {
-      struct symtabs_and_lines expanded;
-      struct tracepoint *tp;
-      event_location_up location;
-
-      expanded.nelts = 1;
-      expanded.sals = &lsal->sals.sals[i];
-
-      location = copy_event_location (canonical->location.get ());
+      event_location_up location
+	= copy_event_location (canonical->location.get ());
 
-      tp = new tracepoint ();
-      init_breakpoint_sal (tp, gdbarch, expanded,
+      tracepoint *tp = new tracepoint ();
+      init_breakpoint_sal (tp, gdbarch, lsal.sals[i],
 			   std::move (location), NULL,
 			   std::move (cond_string),
 			   std::move (extra_string),
@@ -13592,20 +13555,20 @@ strace_marker_create_breakpoints_sal (struct gdbarch *gdbarch,
     }
 }
 
-static void
+static std::vector<symtab_and_line>
 strace_marker_decode_location (struct breakpoint *b,
 			       const struct event_location *location,
-			       struct program_space *search_pspace,
-			       struct symtabs_and_lines *sals)
+			       struct program_space *search_pspace)
 {
   struct tracepoint *tp = (struct tracepoint *) b;
   const char *s = get_linespec_location (location);
 
-  *sals = decode_static_tracepoint_spec (&s);
-  if (sals->nelts > tp->static_trace_marker_id_idx)
+  std::vector<symtab_and_line> sals = decode_static_tracepoint_spec (&s);
+  if (sals.size () > tp->static_trace_marker_id_idx)
     {
-      sals->sals[0] = sals->sals[tp->static_trace_marker_id_idx];
-      sals->nelts = 1;
+      sals[0] = sals[tp->static_trace_marker_id_idx];
+      sals.resize (1);
+      return sals;
     }
   else
     error (_("marker %s not found"), tp->static_trace_marker_id);
@@ -14075,13 +14038,13 @@ hoist_existing_locations (struct breakpoint *b, struct program_space *pspace)
 void
 update_breakpoint_locations (struct breakpoint *b,
 			     struct program_space *filter_pspace,
-			     struct symtabs_and_lines sals,
-			     struct symtabs_and_lines sals_end)
+			     gdb::array_view<const symtab_and_line> sals,
+			     gdb::array_view<const symtab_and_line> sals_end)
 {
   int i;
   struct bp_location *existing_locations;
 
-  if (sals_end.nelts != 0 && (sals.nelts != 1 || sals_end.nelts != 1))
+  if (!sals_end.empty () && (sals.size () != 1 || sals_end.size () != 1))
     {
       /* Ranged breakpoints have only one start location and one end
 	 location.  */
@@ -14098,18 +14061,18 @@ update_breakpoint_locations (struct breakpoint *b,
      We'd like to retain the location, so that when the library is
      loaded again, we don't loose the enabled/disabled status of the
      individual locations.  */
-  if (all_locations_are_pending (b, filter_pspace) && sals.nelts == 0)
+  if (all_locations_are_pending (b, filter_pspace) && sals.empty ())
     return;
 
   existing_locations = hoist_existing_locations (b, filter_pspace);
 
-  for (i = 0; i < sals.nelts; ++i)
+  for (const auto &sal : sals)
     {
       struct bp_location *new_loc;
 
-      switch_to_program_space_and_thread (sals.sals[i].pspace);
+      switch_to_program_space_and_thread (sal.pspace);
 
-      new_loc = add_location_to_breakpoint (b, &(sals.sals[i]));
+      new_loc = add_location_to_breakpoint (b, &sal);
 
       /* Reparse conditions, they might contain references to the
 	 old symtab.  */
@@ -14120,8 +14083,8 @@ update_breakpoint_locations (struct breakpoint *b,
 	  s = b->cond_string;
 	  TRY
 	    {
-	      new_loc->cond = parse_exp_1 (&s, sals.sals[i].pc,
-					   block_for_pc (sals.sals[i].pc), 
+	      new_loc->cond = parse_exp_1 (&s, sal.pc,
+					   block_for_pc (sal.pc),
 					   0);
 	    }
 	  CATCH (e, RETURN_MASK_ERROR)
@@ -14134,11 +14097,11 @@ update_breakpoint_locations (struct breakpoint *b,
 	  END_CATCH
 	}
 
-      if (sals_end.nelts)
+      if (!sals_end.empty ())
 	{
-	  CORE_ADDR end = find_breakpoint_range_end (sals_end.sals[0]);
+	  CORE_ADDR end = find_breakpoint_range_end (sals_end[0]);
 
-	  new_loc->length = end - sals.sals[0].pc + 1;
+	  new_loc->length = end - sals[0].pc + 1;
 	}
     }
 
@@ -14188,18 +14151,19 @@ update_breakpoint_locations (struct breakpoint *b,
 /* Find the SaL locations corresponding to the given LOCATION.
    On return, FOUND will be 1 if any SaL was found, zero otherwise.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 location_to_sals (struct breakpoint *b, struct event_location *location,
 		  struct program_space *search_pspace, int *found)
 {
-  struct symtabs_and_lines sals = {0};
   struct gdb_exception exception = exception_none;
 
   gdb_assert (b->ops != NULL);
 
+  std::vector<symtab_and_line> sals;
+
   TRY
     {
-      b->ops->decode_location (b, location, search_pspace, &sals);
+      sals = b->ops->decode_location (b, location, search_pspace);
     }
   CATCH (e, RETURN_MASK_ERROR)
     {
@@ -14240,16 +14204,14 @@ location_to_sals (struct breakpoint *b, struct event_location *location,
 
   if (exception.reason == 0 || exception.error != NOT_FOUND_ERROR)
     {
-      int i;
-
-      for (i = 0; i < sals.nelts; ++i)
-	resolve_sal_pc (&sals.sals[i]);
+      for (auto &sal : sals)
+	resolve_sal_pc (&sal);
       if (b->condition_not_parsed && b->extra_string != NULL)
 	{
 	  char *cond_string, *extra_string;
 	  int thread, task;
 
-	  find_condition_and_thread (b->extra_string, sals.sals[0].pc,
+	  find_condition_and_thread (b->extra_string, sals[0].pc,
 				     &cond_string, &thread, &task,
 				     &extra_string);
 	  gdb_assert (b->cond_string == NULL);
@@ -14266,7 +14228,7 @@ location_to_sals (struct breakpoint *b, struct event_location *location,
 	}
 
       if (b->type == bp_static_tracepoint && !strace_marker_p (b))
-	sals.sals[0] = update_static_tracepoint (b, sals.sals[0]);
+	sals[0] = update_static_tracepoint (b, sals[0]);
 
       *found = 1;
     }
@@ -14283,28 +14245,22 @@ location_to_sals (struct breakpoint *b, struct event_location *location,
 static void
 breakpoint_re_set_default (struct breakpoint *b)
 {
-  int found;
-  struct symtabs_and_lines sals, sals_end;
-  struct symtabs_and_lines expanded = {0};
-  struct symtabs_and_lines expanded_end = {0};
   struct program_space *filter_pspace = current_program_space;
+  std::vector<symtab_and_line> expanded, expanded_end;
 
-  sals = location_to_sals (b, b->location.get (), filter_pspace, &found);
+  int found;
+  std::vector<symtab_and_line> sals = location_to_sals (b, b->location.get (),
+							filter_pspace, &found);
   if (found)
-    {
-      make_cleanup (xfree, sals.sals);
-      expanded = sals;
-    }
+    expanded = std::move (sals);
 
   if (b->location_range_end != NULL)
     {
-      sals_end = location_to_sals (b, b->location_range_end.get (),
-				   filter_pspace, &found);
+      std::vector<symtab_and_line> sals_end
+	= location_to_sals (b, b->location_range_end.get (),
+			    filter_pspace, &found);
       if (found)
-	{
-	  make_cleanup (xfree, sals_end.sals);
-	  expanded_end = sals_end;
-	}
+	expanded_end = std::move (sals_end);
     }
 
   update_breakpoint_locations (b, filter_pspace, expanded, expanded_end);
@@ -14349,11 +14305,10 @@ create_breakpoints_sal_default (struct gdbarch *gdbarch,
 /* Decode the line represented by S by calling decode_line_full.  This is the
    default function for the `decode_location' method of breakpoint_ops.  */
 
-static void
+static std::vector<symtab_and_line>
 decode_location_default (struct breakpoint *b,
 			 const struct event_location *location,
-			 struct program_space *search_pspace,
-			 struct symtabs_and_lines *sals)
+			 struct program_space *search_pspace)
 {
   struct linespec_result canonical;
 
@@ -14363,18 +14318,14 @@ decode_location_default (struct breakpoint *b,
 		    b->filter);
 
   /* We should get 0 or 1 resulting SALs.  */
-  gdb_assert (VEC_length (linespec_sals, canonical.sals) < 2);
+  gdb_assert (canonical.lsals.size () < 2);
 
-  if (VEC_length (linespec_sals, canonical.sals) > 0)
+  if (!canonical.lsals.empty ())
     {
-      struct linespec_sals *lsal;
-
-      lsal = VEC_index (linespec_sals, canonical.sals, 0);
-      *sals = lsal->sals;
-      /* Arrange it so the destructor does not free the
-	 contents.  */
-      lsal->sals.sals = NULL;
+      const linespec_sals &lsal = canonical.lsals[0];
+      return std::move (lsal.sals);
     }
+  return {};
 }
 
 /* Prepare the global context for a re-set of breakpoint B.  */
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index d955184..79b7dcb 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -28,6 +28,7 @@
 #include "probe.h"
 #include "location.h"
 #include <vector>
+#include "common/array-view.h"
 
 struct value;
 struct block;
@@ -611,15 +612,15 @@ struct breakpoint_ops
 				  int, int, int, unsigned);
 
   /* Given the location (second parameter), this method decodes it and
-     provides the SAL locations related to it.  For ordinary
+     returns the SAL locations related to it.  For ordinary
      breakpoints, it calls `decode_line_full'.  If SEARCH_PSPACE is
      not NULL, symbol search is restricted to just that program space.
 
      This function is called inside `location_to_sals'.  */
-  void (*decode_location) (struct breakpoint *b,
-			   const struct event_location *location,
-			   struct program_space *search_pspace,
-			   struct symtabs_and_lines *sals);
+  std::vector<symtab_and_line> (*decode_location)
+    (struct breakpoint *b,
+     const struct event_location *location,
+     struct program_space *search_pspace);
 
   /* Return true if this breakpoint explains a signal.  See
      bpstat_explains_signal.  */
@@ -1202,10 +1203,11 @@ extern void until_break_command (char *, int, int);
 
 /* Initialize a struct bp_location.  */
 
-extern void update_breakpoint_locations (struct breakpoint *b,
-					 struct program_space *filter_pspace,
-					 struct symtabs_and_lines sals,
-					 struct symtabs_and_lines sals_end);
+extern void update_breakpoint_locations
+  (struct breakpoint *b,
+   struct program_space *filter_pspace,
+   gdb::array_view<const symtab_and_line> sals,
+   gdb::array_view<const symtab_and_line> sals_end);
 
 extern void breakpoint_re_set (void);
 
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 3fa2499..3506254 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -90,9 +90,9 @@ static void list_command (char *, int);
 
 /* Prototypes for local utility functions */
 
-static void ambiguous_line_spec (struct symtabs_and_lines *);
+static void ambiguous_line_spec (gdb::array_view<const symtab_and_line> sals);
 
-static void filter_sals (struct symtabs_and_lines *);
+static void filter_sals (std::vector<symtab_and_line> &);
 
 \f
 /* Limit the call depth of user-defined commands */
@@ -786,7 +786,6 @@ shell_command (char *arg, int from_tty)
 static void
 edit_command (char *arg, int from_tty)
 {
-  struct symtabs_and_lines sals;
   struct symtab_and_line sal;
   struct symbol *sym;
   const char *editor;
@@ -816,24 +815,23 @@ edit_command (char *arg, int from_tty)
       arg1 = arg;
       event_location_up location = string_to_event_location (&arg1,
 							     current_language);
-      sals = decode_line_1 (location.get (), DECODE_LINE_LIST_MODE,
-			    NULL, NULL, 0);
+      std::vector<symtab_and_line> sals = decode_line_1 (location.get (),
+							 DECODE_LINE_LIST_MODE,
+							 NULL, NULL, 0);
 
-      filter_sals (&sals);
-      if (! sals.nelts)
+      filter_sals (sals);
+      if (sals.empty ())
 	{
 	  /*  C++  */
 	  return;
 	}
-      if (sals.nelts > 1)
+      if (sals.size () > 1)
 	{
-	  ambiguous_line_spec (&sals);
-	  xfree (sals.sals);
+	  ambiguous_line_spec (sals);
 	  return;
 	}
 
-      sal = sals.sals[0];
-      xfree (sals.sals);
+      sal = sals[0];
 
       if (*arg1)
         error (_("Junk at end of line specification."));
@@ -887,7 +885,6 @@ edit_command (char *arg, int from_tty)
 static void
 list_command (char *arg, int from_tty)
 {
-  struct symtabs_and_lines sals, sals_end;
   struct symtab_and_line sal = { 0 };
   struct symtab_and_line sal_end = { 0 };
   struct symtab_and_line cursal = { 0 };
@@ -962,24 +959,23 @@ list_command (char *arg, int from_tty)
     {
       event_location_up location = string_to_event_location (&arg1,
 							     current_language);
-      sals = decode_line_1 (location.get (), DECODE_LINE_LIST_MODE,
-			    NULL, NULL, 0);
+      std::vector<symtab_and_line> sals = decode_line_1 (location.get (),
+							 DECODE_LINE_LIST_MODE,
+							 NULL, NULL, 0);
 
-      filter_sals (&sals);
-      if (!sals.nelts)
+      filter_sals (sals);
+      if (sals.empty ())
 	{
 	  /*  C++  */
 	  return;
 	}
-      if (sals.nelts > 1)
+      if (sals.size () > 1)
 	{
-	  ambiguous_line_spec (&sals);
-	  xfree (sals.sals);
+	  ambiguous_line_spec (sals);
 	  return;
 	}
 
-      sal = sals.sals[0];
-      xfree (sals.sals);
+      sal = sals[0];
     }
 
   /* Record whether the BEG arg is all digits.  */
@@ -1001,24 +997,23 @@ list_command (char *arg, int from_tty)
 	{
 	  event_location_up location
 	    = string_to_event_location (&arg1, current_language);
-	  if (dummy_beg)
-	    sals_end = decode_line_1 (location.get (),
-				      DECODE_LINE_LIST_MODE, NULL, NULL, 0);
-	  else
-	    sals_end = decode_line_1 (location.get (), DECODE_LINE_LIST_MODE,
-				      NULL, sal.symtab, sal.line);
 
-	  filter_sals (&sals_end);
-	  if (sals_end.nelts == 0)
+	  std::vector<symtab_and_line> sals_end
+	    = (dummy_beg
+	       ? decode_line_1 (location.get (), DECODE_LINE_LIST_MODE,
+				NULL, NULL, 0)
+	       : decode_line_1 (location.get (), DECODE_LINE_LIST_MODE,
+				NULL, sal.symtab, sal.line));
+
+	  filter_sals (sals_end);
+	  if (sals_end.empty ())
 	    return;
-	  if (sals_end.nelts > 1)
+	  if (sals_end.size () > 1)
 	    {
-	      ambiguous_line_spec (&sals_end);
-	      xfree (sals_end.sals);
+	      ambiguous_line_spec (sals_end);
 	      return;
 	    }
-	  sal_end = sals_end.sals[0];
-	  xfree (sals_end.sals);
+	  sal_end = sals_end[0];
 	}
     }
 
@@ -1501,29 +1496,26 @@ alias_command (char *args, int from_tty)
 \f
 /* Print a list of files and line numbers which a user may choose from
    in order to list a function which was specified ambiguously (as
-   with `list classname::overloadedfuncname', for example).  The
-   vector in SALS provides the filenames and line numbers.  */
+   with `list classname::overloadedfuncname', for example).  The SALS
+   vector provides the filenames and line numbers.  */
 
 static void
-ambiguous_line_spec (struct symtabs_and_lines *sals)
+ambiguous_line_spec (gdb::array_view<const symtab_and_line> sals)
 {
-  int i;
-
-  for (i = 0; i < sals->nelts; ++i)
+  for (const auto &sal : sals)
     printf_filtered (_("file: \"%s\", line number: %d\n"),
-		     symtab_to_filename_for_display (sals->sals[i].symtab),
-		     sals->sals[i].line);
+		     symtab_to_filename_for_display (sal.symtab),
+		     sal.line);
 }
 
-/* Sort function for filter_sals.  */
+/* Comparison function for filter_sals.  Returns a qsort-style
+   result.  */
 
 static int
-compare_symtabs (const void *a, const void *b)
+cmp_symtabs (const symtab_and_line &sala, const symtab_and_line &salb)
 {
-  const struct symtab_and_line *sala = (const struct symtab_and_line *) a;
-  const struct symtab_and_line *salb = (const struct symtab_and_line *) b;
-  const char *dira = SYMTAB_DIRNAME (sala->symtab);
-  const char *dirb = SYMTAB_DIRNAME (salb->symtab);
+  const char *dira = SYMTAB_DIRNAME (sala.symtab);
+  const char *dirb = SYMTAB_DIRNAME (salb.symtab);
   int r;
 
   if (dira == NULL)
@@ -1543,58 +1535,37 @@ compare_symtabs (const void *a, const void *b)
 	return r;
     }
 
-  r = filename_cmp (sala->symtab->filename, salb->symtab->filename);
+  r = filename_cmp (sala.symtab->filename, salb.symtab->filename);
   if (r)
     return r;
 
-  if (sala->line < salb->line)
+  if (sala.line < salb.line)
     return -1;
-  return sala->line == salb->line ? 0 : 1;
+  return sala.line == salb.line ? 0 : 1;
 }
 
 /* Remove any SALs that do not match the current program space, or
    which appear to be "file:line" duplicates.  */
 
 static void
-filter_sals (struct symtabs_and_lines *sals)
+filter_sals (std::vector<symtab_and_line> &sals)
 {
-  int i, out, prev;
-
-  out = 0;
-  for (i = 0; i < sals->nelts; ++i)
-    {
-      if (sals->sals[i].pspace == current_program_space
-	  && sals->sals[i].symtab != NULL)
-	{
-	  sals->sals[out] = sals->sals[i];
-	  ++out;
-	}
-    }
-  sals->nelts = out;
-
-  qsort (sals->sals, sals->nelts, sizeof (struct symtab_and_line),
-	 compare_symtabs);
-
-  out = 1;
-  prev = 0;
-  for (i = 1; i < sals->nelts; ++i)
-    {
-      if (compare_symtabs (&sals->sals[prev], &sals->sals[i]))
-	{
-	  /* Symtabs differ.  */
-	  sals->sals[out] = sals->sals[i];
-	  prev = out;
-	  ++out;
-	}
-    }
-
-  if (sals->nelts == 0)
-    {
-      xfree (sals->sals);
-      sals->sals = NULL;
-    }
-  else
-    sals->nelts = out;
+  /* Remove SALs that do not match.  */
+  auto from = std::remove_if (sals.begin (), sals.end (),
+			      [&] (const symtab_and_line &sal)
+    { return (sal.pspace != current_program_space || sal.symtab == NULL); });
+
+  /* Remove dups.  */
+  std::sort (sals.begin (), from,
+	     [] (const symtab_and_line &sala, const symtab_and_line &salb)
+   { return cmp_symtabs (sala, salb) < 0; });
+
+  from = std::unique (sals.begin (), from,
+		      [&] (const symtab_and_line &sala,
+			   const symtab_and_line &salb)
+    { return cmp_symtabs (sala, salb) == 0; });
+
+  sals.erase (from, sals.end ());
 }
 
 static void
diff --git a/gdb/elfread.c b/gdb/elfread.c
index a654661..ddff16b 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -953,8 +953,6 @@ elf_gnu_ifunc_resolver_return_stop (struct breakpoint *b)
   struct value *func_func;
   struct value *value;
   CORE_ADDR resolved_address, resolved_pc;
-  struct symtab_and_line sal;
-  struct symtabs_and_lines sals, sals_end;
 
   gdb_assert (b->type == bp_gnu_ifunc_resolver_return);
 
@@ -997,13 +995,9 @@ elf_gnu_ifunc_resolver_return_stop (struct breakpoint *b)
   elf_gnu_ifunc_record_cache (event_location_to_string (b->location.get ()),
 			      resolved_pc);
 
-  sal = find_pc_line (resolved_pc, 0);
-  sals.nelts = 1;
-  sals.sals = &sal;
-  sals_end.nelts = 0;
-
   b->type = bp_breakpoint;
-  update_breakpoint_locations (b, current_program_space, sals, sals_end);
+  update_breakpoint_locations (b, current_program_space,
+			       find_pc_line (resolved_pc, 0), {});
 }
 
 /* A helper function for elf_symfile_read that reads the minimal
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index c8a82db..c70a1d5 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1179,8 +1179,6 @@ jump_command (char *arg, int from_tty)
 {
   struct gdbarch *gdbarch = get_current_arch ();
   CORE_ADDR addr;
-  struct symtabs_and_lines sals;
-  struct symtab_and_line sal;
   struct symbol *fn;
   struct symbol *sfn;
   int async_exec;
@@ -1200,18 +1198,16 @@ jump_command (char *arg, int from_tty)
   if (!arg)
     error_no_arg (_("starting address"));
 
-  sals = decode_line_with_last_displayed (arg, DECODE_LINE_FUNFIRSTLINE);
-  if (sals.nelts != 1)
-    {
-      error (_("Unreasonable jump request"));
-    }
-
-  sal = sals.sals[0];
-  xfree (sals.sals);
+  std::vector<symtab_and_line> sals
+    = decode_line_with_last_displayed (arg, DECODE_LINE_FUNFIRSTLINE);
+  if (sals.size () != 1)
+    error (_("Unreasonable jump request"));
 
   /* Done with ARGS.  */
   do_cleanups (args_chain);
 
+  symtab_and_line &sal = sals[0];
+
   if (sal.symtab == 0 && sal.pc == 0)
     error (_("No source file has been specified."));
 
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 136cb65..5396eba 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -173,7 +173,7 @@ struct linespec_state
   /* The 'canonical' value passed to decode_line_full, or NULL.  */
   struct linespec_result *canonical;
 
-  /* Canonical strings that mirror the symtabs_and_lines result.  */
+  /* Canonical strings that mirror the std::vector<symtab_and_line> result.  */
   struct linespec_canonical_name *canonical_names;
 
   /* This is a set of address_entry objects which is used to prevent
@@ -341,9 +341,9 @@ static void initialize_defaults (struct symtab **default_symtab,
 
 CORE_ADDR linespec_expression_to_pc (const char **exp_ptr);
 
-static struct symtabs_and_lines decode_objc (struct linespec_state *self,
-					     linespec_p ls,
-					     const char *arg);
+static std::vector<symtab_and_line> decode_objc (struct linespec_state *self,
+						 linespec_p ls,
+						 const char *arg);
 
 static VEC (symtab_ptr) *symtabs_from_filename (const char *,
 						struct program_space *pspace);
@@ -379,20 +379,20 @@ static VEC (symtab_ptr) *
   collect_symtabs_from_filename (const char *file,
 				 struct program_space *pspace);
 
-static void decode_digits_ordinary (struct linespec_state *self,
-				    linespec_p ls,
-				    int line,
-				    struct symtabs_and_lines *sals,
-				    struct linetable_entry **best_entry);
+static std::vector<symtab_and_line> decode_digits_ordinary
+  (struct linespec_state *self,
+   linespec_p ls,
+   int line,
+   linetable_entry **best_entry);
 
-static void decode_digits_list_mode (struct linespec_state *self,
-				     linespec_p ls,
-				     struct symtabs_and_lines *values,
-				     struct symtab_and_line val);
+static std::vector<symtab_and_line> decode_digits_list_mode
+  (struct linespec_state *self,
+   linespec_p ls,
+   struct symtab_and_line val);
 
 static void minsym_found (struct linespec_state *self, struct objfile *objfile,
 			  struct minimal_symbol *msymbol,
-			  struct symtabs_and_lines *result);
+			  std::vector<symtab_and_line> *result);
 
 static int compare_symbols (const void *a, const void *b);
 
@@ -996,17 +996,6 @@ linespec_lexer_peek_token (linespec_parser *parser)
 
 /* Helper functions.  */
 
-/* Add SAL to SALS.  */
-
-static void
-add_sal_to_sals_basic (struct symtabs_and_lines *sals,
-		       struct symtab_and_line *sal)
-{
-  ++sals->nelts;
-  sals->sals = XRESIZEVEC (struct symtab_and_line, sals->sals, sals->nelts);
-  sals->sals[sals->nelts - 1] = *sal;
-}
-
 /* Add SAL to SALS, and also update SELF->CANONICAL_NAMES to reflect
    the new sal, if needed.  If not NULL, SYMNAME is the name of the
    symbol to use when constructing the new canonical name.
@@ -1016,19 +1005,20 @@ add_sal_to_sals_basic (struct symtabs_and_lines *sals,
 
 static void
 add_sal_to_sals (struct linespec_state *self,
-		 struct symtabs_and_lines *sals,
+		 std::vector<symtab_and_line> *sals,
 		 struct symtab_and_line *sal,
 		 const char *symname, int literal_canonical)
 {
-  add_sal_to_sals_basic (sals, sal);
+  sals->push_back (*sal);
 
   if (self->canonical)
     {
       struct linespec_canonical_name *canonical;
 
       self->canonical_names = XRESIZEVEC (struct linespec_canonical_name,
-					  self->canonical_names, sals->nelts);
-      canonical = &self->canonical_names[sals->nelts - 1];
+					  self->canonical_names,
+					  sals->size ());
+      canonical = &self->canonical_names[sals->size () - 1];
       if (!literal_canonical && sal->symtab)
 	{
 	  symtab_to_fullname (sal->symtab);
@@ -1405,7 +1395,7 @@ canonical_to_fullform (const struct linespec_canonical_name *canonical)
 
 static void
 filter_results (struct linespec_state *self,
-		struct symtabs_and_lines *result,
+		std::vector<symtab_and_line> *result,
 		VEC (const_char_ptr) *filters)
 {
   int i;
@@ -1413,12 +1403,9 @@ filter_results (struct linespec_state *self,
 
   for (i = 0; VEC_iterate (const_char_ptr, filters, i, name); ++i)
     {
-      struct linespec_sals lsal;
-      int j;
-
-      memset (&lsal, 0, sizeof (lsal));
+      linespec_sals lsal;
 
-      for (j = 0; j < result->nelts; ++j)
+      for (size_t j = 0; j < result->size (); ++j)
 	{
 	  const struct linespec_canonical_name *canonical;
 	  char *fullform;
@@ -1429,15 +1416,15 @@ filter_results (struct linespec_state *self,
 	  cleanup = make_cleanup (xfree, fullform);
 
 	  if (strcmp (name, fullform) == 0)
-	    add_sal_to_sals_basic (&lsal.sals, &result->sals[j]);
+	    lsal.sals.push_back ((*result)[j]);
 
 	  do_cleanups (cleanup);
 	}
 
-      if (lsal.sals.nelts > 0)
+      if (!lsal.sals.empty ())
 	{
 	  lsal.canonical = xstrdup (name);
-	  VEC_safe_push (linespec_sals, self->canonical->sals, &lsal);
+	  self->canonical->lsals.push_back (std::move (lsal));
 	}
     }
 
@@ -1448,13 +1435,13 @@ filter_results (struct linespec_state *self,
 
 static void
 convert_results_to_lsals (struct linespec_state *self,
-			  struct symtabs_and_lines *result)
+			  std::vector<symtab_and_line> *result)
 {
   struct linespec_sals lsal;
 
   lsal.canonical = NULL;
-  lsal.sals = *result;
-  VEC_safe_push (linespec_sals, self->canonical->sals, &lsal);
+  lsal.sals = std::move (*result);
+  self->canonical->lsals.push_back (std::move (lsal));
 }
 
 /* A structure that contains two string representations of a struct
@@ -1502,7 +1489,7 @@ decode_line_2_compare_items (const void *ap, const void *bp)
 
 static void
 decode_line_2 (struct linespec_state *self,
-	       struct symtabs_and_lines *result,
+	       std::vector<symtab_and_line> *result,
 	       const char *select_mode)
 {
   char *args;
@@ -1515,12 +1502,12 @@ decode_line_2 (struct linespec_state *self,
 
   gdb_assert (select_mode != multiple_symbols_all);
   gdb_assert (self->canonical != NULL);
-  gdb_assert (result->nelts >= 1);
+  gdb_assert (!result->empty ());
 
   old_chain = make_cleanup (VEC_cleanup (const_char_ptr), &filters);
 
   /* Prepare ITEMS array.  */
-  items_count = result->nelts;
+  items_count = result->size ();
   items = XNEWVEC (struct decode_line_2_item, items_count);
   make_cleanup (xfree, items);
   for (i = 0; i < items_count; ++i)
@@ -2135,17 +2122,14 @@ canonicalize_linespec (struct linespec_state *state, const linespec_p ls)
 
 /* Given a line offset in LS, construct the relevant SALs.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 create_sals_line_offset (struct linespec_state *self,
 			 linespec_p ls)
 {
-  struct symtabs_and_lines values;
   struct symtab_and_line val;
   int use_default = 0;
 
   init_sal (&val);
-  values.sals = NULL;
-  values.nelts = 0;
 
   /* This is where we need to make sure we have good defaults.
      We must guarantee that this section of code is never executed
@@ -2194,27 +2178,22 @@ create_sals_line_offset (struct linespec_state *self,
       break;			/* No need to adjust val.line.  */
     }
 
+  std::vector<symtab_and_line> values;
   if (self->list_mode)
-    decode_digits_list_mode (self, ls, &values, val);
+    values = decode_digits_list_mode (self, ls, val);
   else
     {
       struct linetable_entry *best_entry = NULL;
       int *filter;
       const struct block **blocks;
-      struct cleanup *cleanup;
-      struct symtabs_and_lines intermediate_results;
       int i, j;
 
-      intermediate_results.sals = NULL;
-      intermediate_results.nelts = 0;
-
-      decode_digits_ordinary (self, ls, val.line, &intermediate_results,
-			      &best_entry);
-      if (intermediate_results.nelts == 0 && best_entry != NULL)
-	decode_digits_ordinary (self, ls, best_entry->line,
-				&intermediate_results, &best_entry);
-
-      cleanup = make_cleanup (xfree, intermediate_results.sals);
+      std::vector<symtab_and_line> intermediate_results
+	= decode_digits_ordinary (self, ls, val.line, &best_entry);
+      if (intermediate_results.empty () && best_entry != NULL)
+	intermediate_results = decode_digits_ordinary (self, ls,
+						       best_entry->line,
+						       &best_entry);
 
       /* For optimized code, the compiler can scatter one source line
 	 across disjoint ranges of PC values, even when no duplicate
@@ -2226,24 +2205,24 @@ create_sals_line_offset (struct linespec_state *self,
 	 above, we see if there are other PCs that are in the same
 	 block.  If yes, the other PCs are filtered out.  */
 
-      filter = XNEWVEC (int, intermediate_results.nelts);
-      make_cleanup (xfree, filter);
-      blocks = XNEWVEC (const struct block *, intermediate_results.nelts);
+      filter = XNEWVEC (int, intermediate_results.size ());
+      struct cleanup *cleanup = make_cleanup (xfree, filter);
+      blocks = XNEWVEC (const struct block *, intermediate_results.size ());
       make_cleanup (xfree, blocks);
 
-      for (i = 0; i < intermediate_results.nelts; ++i)
+      for (i = 0; i < intermediate_results.size (); ++i)
 	{
-	  set_current_program_space (intermediate_results.sals[i].pspace);
+	  set_current_program_space (intermediate_results[i].pspace);
 
 	  filter[i] = 1;
-	  blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc,
-					 intermediate_results.sals[i].section);
+	  blocks[i] = block_for_pc_sect (intermediate_results[i].pc,
+					 intermediate_results[i].section);
 	}
 
-      for (i = 0; i < intermediate_results.nelts; ++i)
+      for (i = 0; i < intermediate_results.size (); ++i)
 	{
 	  if (blocks[i] != NULL)
-	    for (j = i + 1; j < intermediate_results.nelts; ++j)
+	    for (j = i + 1; j < intermediate_results.size (); ++j)
 	      {
 		if (blocks[j] == blocks[i])
 		  {
@@ -2253,7 +2232,7 @@ create_sals_line_offset (struct linespec_state *self,
 	      }
 	}
 
-      for (i = 0; i < intermediate_results.nelts; ++i)
+      for (i = 0; i < intermediate_results.size (); ++i)
 	if (filter[i])
 	  {
 	    struct symbol *sym = (blocks[i]
@@ -2261,18 +2240,18 @@ create_sals_line_offset (struct linespec_state *self,
 				  : NULL);
 
 	    if (self->funfirstline)
-	      skip_prologue_sal (&intermediate_results.sals[i]);
+	      skip_prologue_sal (&intermediate_results[i]);
 	    /* Make sure the line matches the request, not what was
 	       found.  */
-	    intermediate_results.sals[i].line = val.line;
-	    add_sal_to_sals (self, &values, &intermediate_results.sals[i],
+	    intermediate_results[i].line = val.line;
+	    add_sal_to_sals (self, &values, &intermediate_results[i],
 			     sym ? SYMBOL_NATURAL_NAME (sym) : NULL, 0);
 	  }
 
       do_cleanups (cleanup);
     }
 
-  if (values.nelts == 0)
+  if (values.empty ())
     {
       if (ls->explicit_loc.source_filename)
 	throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."),
@@ -2287,17 +2266,16 @@ create_sals_line_offset (struct linespec_state *self,
 
 /* Convert the given ADDRESS into SaLs.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 convert_address_location_to_sals (struct linespec_state *self,
 				  CORE_ADDR address)
 {
-  struct symtab_and_line sal;
-  struct symtabs_and_lines sals = {NULL, 0};
-
-  sal = find_pc_line (address, 0);
+  symtab_and_line sal = find_pc_line (address, 0);
   sal.pc = address;
   sal.section = find_pc_overlay (address);
   sal.explicit_pc = 1;
+
+  std::vector<symtab_and_line> sals;
   add_sal_to_sals (self, &sals, &sal, core_addr_to_string (address), 1);
 
   return sals;
@@ -2305,10 +2283,10 @@ convert_address_location_to_sals (struct linespec_state *self,
 
 /* Create and return SALs from the linespec LS.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
 {
-  struct symtabs_and_lines sals = {NULL, 0};
+  std::vector<symtab_and_line> sals;
 
   if (ls->labels.label_symbols != NULL)
     {
@@ -2398,7 +2376,7 @@ convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
 
   canonicalize_linespec (state, ls);
 
-  if (sals.nelts > 0 && state->canonical != NULL)
+  if (!sals.empty () && state->canonical != NULL)
     state->canonical->pre_expanded = 1;
 
   return sals;
@@ -2474,7 +2452,7 @@ convert_explicit_location_to_linespec (struct linespec_state *self,
 
 /* Convert the explicit location EXPLICIT_LOC into SaLs.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 convert_explicit_location_to_sals (struct linespec_state *self,
 				   linespec_p result,
 				   const struct explicit_location *explicit_loc)
@@ -2537,17 +2515,13 @@ convert_explicit_location_to_sals (struct linespec_state *self,
 
 /* Parse the linespec in ARG.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 parse_linespec (linespec_parser *parser, const char *arg)
 {
   linespec_token token;
-  struct symtabs_and_lines values;
   struct gdb_exception file_exception = exception_none;
   struct cleanup *cleanup;
 
-  values.nelts = 0;
-  values.sals = NULL;
-
   /* A special case to start.  It has become quite popular for
      IDEs to work around bugs in the previous parser by quoting
      the entire linespec, so we attempt to deal with this nicely.  */
@@ -2580,8 +2554,9 @@ parse_linespec (linespec_parser *parser, const char *arg)
   /* Objective-C shortcut.  */
   if (parser->completion_tracker == NULL)
     {
-      values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
-      if (values.sals != NULL)
+      std::vector<symtab_and_line> values
+	= decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
+      if (!values.empty ())
 	return values;
     }
   else
@@ -2735,10 +2710,10 @@ parse_linespec (linespec_parser *parser, const char *arg)
 
   /* Convert the data in PARSER_RESULT to SALs.  */
   if (parser->completion_tracker == NULL)
-    values = convert_linespec_to_sals (PARSER_STATE (parser),
-				       PARSER_RESULT (parser));
+    return convert_linespec_to_sals (PARSER_STATE (parser),
+				     PARSER_RESULT (parser));
 
-  return values;
+  return {};
 }
 
 
@@ -3194,13 +3169,13 @@ linespec_complete (completion_tracker &tracker, const char *text)
 }
 
 /* A helper function for decode_line_full and decode_line_1 to
-   turn LOCATION into symtabs_and_lines.  */
+   turn LOCATION into std::vector<symtab_and_line>.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 event_location_to_sals (linespec_parser *parser,
 			const struct event_location *location)
 {
-  struct symtabs_and_lines result = {NULL, 0};
+  std::vector<symtab_and_line> result;
 
   switch (event_location_type (location))
     {
@@ -3276,7 +3251,6 @@ decode_line_full (const struct event_location *location, int flags,
 		  const char *select_mode,
 		  const char *filter)
 {
-  struct symtabs_and_lines result;
   struct cleanup *cleanups;
   VEC (const_char_ptr) *filters = NULL;
   linespec_parser parser;
@@ -3298,19 +3272,20 @@ decode_line_full (const struct event_location *location, int flags,
 
   scoped_restore_current_program_space restore_pspace;
 
-  result = event_location_to_sals (&parser, location);
+  std::vector<symtab_and_line> result = event_location_to_sals (&parser,
+								location);
   state = PARSER_STATE (&parser);
 
-  gdb_assert (result.nelts == 1 || canonical->pre_expanded);
+  gdb_assert (result.size () == 1 || canonical->pre_expanded);
   canonical->pre_expanded = 1;
 
   /* Arrange for allocated canonical names to be freed.  */
-  if (result.nelts > 0)
+  if (!result.empty ())
     {
       int i;
 
       make_cleanup (xfree, state->canonical_names);
-      for (i = 0; i < result.nelts; ++i)
+      for (i = 0; i < result.size (); ++i)
 	{
 	  gdb_assert (state->canonical_names[i].suffix != NULL);
 	  make_cleanup (xfree, state->canonical_names[i].suffix);
@@ -3344,13 +3319,12 @@ decode_line_full (const struct event_location *location, int flags,
 
 /* See linespec.h.  */
 
-struct symtabs_and_lines
+std::vector<symtab_and_line>
 decode_line_1 (const struct event_location *location, int flags,
 	       struct program_space *search_pspace,
 	       struct symtab *default_symtab,
 	       int default_line)
 {
-  struct symtabs_and_lines result;
   linespec_parser parser;
   struct cleanup *cleanups;
 
@@ -3361,7 +3335,8 @@ decode_line_1 (const struct event_location *location, int flags,
 
   scoped_restore_current_program_space restore_pspace;
 
-  result = event_location_to_sals (&parser, location);
+  std::vector<symtab_and_line> result = event_location_to_sals (&parser,
+								location);
 
   do_cleanups (cleanups);
   return result;
@@ -3369,23 +3344,20 @@ decode_line_1 (const struct event_location *location, int flags,
 
 /* See linespec.h.  */
 
-struct symtabs_and_lines
+std::vector<symtab_and_line>
 decode_line_with_current_source (char *string, int flags)
 {
-  struct symtabs_and_lines sals;
-  struct symtab_and_line cursal;
-
   if (string == 0)
     error (_("Empty line specification."));
 
   /* We use whatever is set as the current source line.  We do not try
      and get a default source symtab+line or it will recursively call us!  */
-  cursal = get_current_source_symtab_and_line ();
+  symtab_and_line cursal = get_current_source_symtab_and_line ();
 
   event_location_up location = string_to_event_location (&string,
 							 current_language);
-  sals = decode_line_1 (location.get (), flags, NULL,
-			cursal.symtab, cursal.line);
+  std::vector<symtab_and_line> sals
+    = decode_line_1 (location.get (), flags, NULL, cursal.symtab, cursal.line);
 
   if (*string)
     error (_("Junk at end of line specification: %s"), string);
@@ -3395,23 +3367,21 @@ decode_line_with_current_source (char *string, int flags)
 
 /* See linespec.h.  */
 
-struct symtabs_and_lines
+std::vector<symtab_and_line>
 decode_line_with_last_displayed (char *string, int flags)
 {
-  struct symtabs_and_lines sals;
-
   if (string == 0)
     error (_("Empty line specification."));
 
   event_location_up location = string_to_event_location (&string,
 							 current_language);
-  if (last_displayed_sal_is_valid ())
-    sals = decode_line_1 (location.get (), flags, NULL,
-			  get_last_displayed_symtab (),
-			  get_last_displayed_line ());
-  else
-    sals = decode_line_1 (location.get (), flags, NULL,
-			  (struct symtab *) NULL, 0);
+  std::vector<symtab_and_line> sals
+    = (last_displayed_sal_is_valid ()
+       ? decode_line_1 (location.get (), flags, NULL,
+			get_last_displayed_symtab (),
+			get_last_displayed_line ())
+       : decode_line_1 (location.get (), flags, NULL,
+			(struct symtab *) NULL, 0));
 
   if (*string)
     error (_("Junk at end of line specification: %s"), string);
@@ -3467,12 +3437,11 @@ linespec_expression_to_pc (const char **exp_ptr)
    than one method that could represent the selector, then use some of
    the existing C++ code to let the user choose one.  */
 
-static struct symtabs_and_lines
+static std::vector<symtab_and_line>
 decode_objc (struct linespec_state *self, linespec_p ls, const char *arg)
 {
   struct collect_info info;
   VEC (const_char_ptr) *symbol_names = NULL;
-  struct symtabs_and_lines values;
   const char *new_argptr;
   struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr),
 					  &symbol_names);
@@ -3483,18 +3452,17 @@ decode_objc (struct linespec_state *self, linespec_p ls, const char *arg)
   make_cleanup (VEC_cleanup (symtab_ptr), &info.file_symtabs);
   info.result.symbols = NULL;
   info.result.minimal_symbols = NULL;
-  values.nelts = 0;
-  values.sals = NULL;
 
   new_argptr = find_imps (arg, &symbol_names);
   if (VEC_empty (const_char_ptr, symbol_names))
     {
       do_cleanups (cleanup);
-      return values;
+      return {};
     }
 
   add_all_symbol_names_from_pspace (&info, NULL, symbol_names);
 
+  std::vector<symtab_and_line> values;
   if (!VEC_empty (symbolp, info.result.symbols)
       || !VEC_empty (bound_minimal_symbol_d, info.result.minimal_symbols))
     {
@@ -4208,10 +4176,9 @@ find_label_symbols (struct linespec_state *self,
 
 /* A helper for create_sals_line_offset that handles the 'list_mode' case.  */
 
-static void
+static std::vector<symtab_and_line>
 decode_digits_list_mode (struct linespec_state *self,
 			 linespec_p ls,
-			 struct symtabs_and_lines *values,
 			 struct symtab_and_line val)
 {
   int ix;
@@ -4219,6 +4186,8 @@ decode_digits_list_mode (struct linespec_state *self,
 
   gdb_assert (self->list_mode);
 
+  std::vector<symtab_and_line> values;
+
   for (ix = 0; VEC_iterate (symtab_ptr, ls->file_symtabs, ix, elt);
        ++ix)
     {
@@ -4235,23 +4204,25 @@ decode_digits_list_mode (struct linespec_state *self,
       val.pc = 0;
       val.explicit_line = 1;
 
-      add_sal_to_sals (self, values, &val, NULL, 0);
+      add_sal_to_sals (self, &values, &val, NULL, 0);
     }
+
+  return values;
 }
 
 /* A helper for create_sals_line_offset that iterates over the symtabs,
    adding lines to the VEC.  */
 
-static void
+static std::vector<symtab_and_line>
 decode_digits_ordinary (struct linespec_state *self,
 			linespec_p ls,
 			int line,
-			struct symtabs_and_lines *sals,
 			struct linetable_entry **best_entry)
 {
   int ix;
   struct symtab *elt;
 
+  std::vector<symtab_and_line> sals;
   for (ix = 0; VEC_iterate (symtab_ptr, ls->file_symtabs, ix, elt); ++ix)
     {
       std::vector<CORE_ADDR> pcs;
@@ -4271,9 +4242,11 @@ decode_digits_ordinary (struct linespec_state *self,
 	  sal.symtab = elt;
 	  sal.line = line;
 	  sal.pc = pc;
-	  add_sal_to_sals_basic (sals, &sal);
+	  sals.push_back (std::move (sal));
 	}
     }
+
+  return sals;
 }
 
 \f
@@ -4343,7 +4316,7 @@ linespec_parse_variable (struct linespec_state *self, const char *variable)
 static void
 minsym_found (struct linespec_state *self, struct objfile *objfile,
 	      struct minimal_symbol *msymbol,
-	      struct symtabs_and_lines *result)
+	      std::vector<symtab_and_line> *result)
 {
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
   CORE_ADDR pc;
@@ -4674,15 +4647,8 @@ symbol_to_sal (struct symtab_and_line *result,
 
 linespec_result::~linespec_result ()
 {
-  int i;
-  struct linespec_sals *lsal;
-
-  for (i = 0; VEC_iterate (linespec_sals, sals, i, lsal); ++i)
-    {
-      xfree (lsal->canonical);
-      xfree (lsal->sals.sals);
-    }
-  VEC_free (linespec_sals, sals);
+  for (linespec_sals &lsal : lsals)
+    xfree (lsal.canonical);
 }
 
 /* Return the quote characters permitted by the linespec parser.  */
diff --git a/gdb/linespec.h b/gdb/linespec.h
index 27d237a..ce74fe6 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -46,53 +46,43 @@ struct linespec_sals
      destructor.  */
   char *canonical;
 
-  /* Sals.  The 'sals' field is destroyed by the linespec_result
-     destructor.  */
-  struct symtabs_and_lines sals;
+  /* Sals.  */
+  std::vector<symtab_and_line> sals;
 };
 
-typedef struct linespec_sals linespec_sals;
-DEF_VEC_O (linespec_sals);
-
 /* An instance of this may be filled in by decode_line_1.  The caller
    must make copies of any data that it needs to keep.  */
 
 struct linespec_result
 {
-  linespec_result ()
-    : special_display (0),
-      pre_expanded (0),
-      sals (NULL)
-  {
-  }
-
+  linespec_result () = default;
   ~linespec_result ();
 
   linespec_result (const linespec_result &) = delete;
   linespec_result &operator= (const linespec_result &) = delete;
 
-  /* If non-zero, the linespec should be displayed to the user.  This
+  /* If true, the linespec should be displayed to the user.  This
      is used by "unusual" linespecs where the ordinary `info break'
      display mechanism would do the wrong thing.  */
-  int special_display;
+  bool special_display = false;
 
-  /* If non-zero, the linespec result should be considered to be a
+  /* If true, the linespec result should be considered to be a
      "pre-expanded" multi-location linespec.  A pre-expanded linespec
      holds all matching locations in a single linespec_sals
      object.  */
-  int pre_expanded;
+  bool pre_expanded = false;
 
   /* If PRE_EXPANDED is non-zero, this is set to the location entered
      by the user.  */
   event_location_up location;
 
   /* The sals.  The vector will be freed by the destructor.  */
-  VEC (linespec_sals) *sals;
+  std::vector<linespec_sals> lsals;
 };
 
 /* Decode a linespec using the provided default symtab and line.  */
 
-extern struct symtabs_and_lines
+extern std::vector<symtab_and_line>
 	decode_line_1 (const struct event_location *location, int flags,
 		       struct program_space *search_pspace,
 		       struct symtab *default_symtab, int default_line);
@@ -147,12 +137,12 @@ extern void decode_line_full (const struct event_location *location, int flags,
    source symtab and line as defaults.
    This is for commands like "list" and "breakpoint".  */
 
-extern struct symtabs_and_lines decode_line_with_current_source (char *, int);
+extern std::vector<symtab_and_line> decode_line_with_current_source (char *, int);
 
 /* Given a string, return the line specified by it, using the last displayed
    codepoint's values as defaults, or nothing if they aren't valid.  */
 
-extern struct symtabs_and_lines decode_line_with_last_displayed (char *, int);
+extern std::vector<symtab_and_line> decode_line_with_last_displayed (char *, int);
 
 /* Does P represent one of the keywords?  If so, return
    the keyword.  If not, return NULL.  */
diff --git a/gdb/macrocmd.c b/gdb/macrocmd.c
index c390d63..cb6c38c 100644
--- a/gdb/macrocmd.c
+++ b/gdb/macrocmd.c
@@ -279,11 +279,11 @@ info_macros_command (char *args, int from_tty)
     ms = default_macro_scope ();
   else
     {
-      struct symtabs_and_lines sals =
-	decode_line_with_current_source (args, 0);
+      std::vector<symtab_and_line> sals
+	= decode_line_with_current_source (args, 0);
 
-      if (sals.nelts)
-        ms = sal_macro_scope (sals.sals[0]);
+      if (!sals.empty ())
+	ms = sal_macro_scope (sals[0]);
     }
 
   if (! ms || ! ms->file || ! ms->file->table)
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 0bf587f..c485544 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2526,29 +2526,22 @@ mi_cmd_trace_find (const char *command, char **argv, int argc)
     }
   else if (strcmp (mode, "line") == 0)
     {
-      struct symtabs_and_lines sals;
-      struct symtab_and_line sal;
-      static CORE_ADDR start_pc, end_pc;
-      struct cleanup *back_to;
-
       if (argc != 2)
 	error (_("Line is required"));
 
-      sals = decode_line_with_current_source (argv[1],
-					      DECODE_LINE_FUNFIRSTLINE);
-      back_to = make_cleanup (xfree, sals.sals);
-
-      sal = sals.sals[0];
+      std::vector<symtab_and_line> sals
+	= decode_line_with_current_source (argv[1],
+					   DECODE_LINE_FUNFIRSTLINE);
+      const symtab_and_line &sal = sals[0];
 
       if (sal.symtab == 0)
 	error (_("Could not find the specified line"));
 
+      CORE_ADDR start_pc, end_pc;
       if (sal.line > 0 && find_line_pc_range (sal, &start_pc, &end_pc))
 	tfind_1 (tfind_range, 0, start_pc, end_pc - 1, 0);
       else
 	error (_("Could not find the specified line"));
-
-      do_cleanups (back_to);
     }
   else
     error (_("Invalid mode '%s'"), mode);
diff --git a/gdb/probe.c b/gdb/probe.c
index cb2dcdb..5b5e6f0 100644
--- a/gdb/probe.c
+++ b/gdb/probe.c
@@ -52,7 +52,7 @@ parse_probes_in_pspace (const struct probe_ops *probe_ops,
 			const char *objfile_namestr,
 			const char *provider,
 			const char *name,
-			struct symtabs_and_lines *result)
+			std::vector<symtab_and_line> *result)
 {
   struct objfile *objfile;
 
@@ -75,8 +75,6 @@ parse_probes_in_pspace (const struct probe_ops *probe_ops,
 
       for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
 	{
-	  struct symtab_and_line *sal;
-
 	  if (probe_ops != &probe_ops_any && probe->pops != probe_ops)
 	    continue;
 
@@ -86,26 +84,25 @@ parse_probes_in_pspace (const struct probe_ops *probe_ops,
 	  if (strcmp (probe->name, name) != 0)
 	    continue;
 
-	  ++result->nelts;
-	  result->sals = XRESIZEVEC (struct symtab_and_line, result->sals,
-				     result->nelts);
-	  sal = &result->sals[result->nelts - 1];
+	  symtab_and_line sal;
+
+	  init_sal (&sal);
 
-	  init_sal (sal);
+	  sal.pc = get_probe_address (probe, objfile);
+	  sal.explicit_pc = 1;
+	  sal.section = find_pc_overlay (sal.pc);
+	  sal.pspace = search_pspace;
+	  sal.probe = probe;
+	  sal.objfile = objfile;
 
-	  sal->pc = get_probe_address (probe, objfile);
-	  sal->explicit_pc = 1;
-	  sal->section = find_pc_overlay (sal->pc);
-	  sal->pspace = search_pspace;
-	  sal->probe = probe;
-	  sal->objfile = objfile;
+	  result->push_back (std::move (sal));
 	}
     }
 }
 
 /* See definition in probe.h.  */
 
-struct symtabs_and_lines
+std::vector<symtab_and_line>
 parse_probes (const struct event_location *location,
 	      struct program_space *search_pspace,
 	      struct linespec_result *canonical)
@@ -113,13 +110,9 @@ parse_probes (const struct event_location *location,
   char *arg_end, *arg;
   char *objfile_namestr = NULL, *provider = NULL, *name, *p;
   struct cleanup *cleanup;
-  struct symtabs_and_lines result;
   const struct probe_ops *probe_ops;
   const char *arg_start, *cs;
 
-  result.sals = NULL;
-  result.nelts = 0;
-
   gdb_assert (event_location_type (location) == PROBE_LOCATION);
   arg_start = get_probe_location (location);
 
@@ -175,6 +168,7 @@ parse_probes (const struct event_location *location,
   if (objfile_namestr && *objfile_namestr == '\0')
     error (_("invalid objfile name"));
 
+  std::vector<symtab_and_line> result;
   if (search_pspace != NULL)
     {
       parse_probes_in_pspace (probe_ops, search_pspace, objfile_namestr,
@@ -189,7 +183,7 @@ parse_probes (const struct event_location *location,
 				provider, name, &result);
     }
 
-  if (result.nelts == 0)
+  if (result.empty ())
     {
       throw_error (NOT_FOUND_ERROR,
 		   _("No probe matching objfile=`%s', provider=`%s', name=`%s'"),
diff --git a/gdb/probe.h b/gdb/probe.h
index a6c10e2..db7f1d1 100644
--- a/gdb/probe.h
+++ b/gdb/probe.h
@@ -224,12 +224,14 @@ struct bound_probe
     struct objfile *objfile;
   };
 
-/* A helper for linespec that decodes a probe specification.  It returns a
-   symtabs_and_lines object and updates LOC or throws an error.  */
-
-extern struct symtabs_and_lines parse_probes (const struct event_location *loc,
-					      struct program_space *pspace,
-					      struct linespec_result *canon);
+/* A helper for linespec that decodes a probe specification.  It
+   returns a std::vector<symtab_and_line> object and updates LOC or
+   throws an error.  */
+
+extern std::vector<symtab_and_line> parse_probes
+  (const struct event_location *loc,
+   struct program_space *pspace,
+   struct linespec_result *canon);
 
 /* Helper function to register the proper probe_ops to a newly created probe.
    This function is mainly called from `sym_get_probes'.  */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c53e10f..f40fe89 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -647,10 +647,6 @@ gdbpy_solib_name (PyObject *self, PyObject *args)
 static PyObject *
 gdbpy_decode_line (PyObject *self, PyObject *args)
 {
-  struct gdb_exception except = exception_none;
-  struct symtabs_and_lines sals = { NULL, 0 }; /* Initialize to
-						  appease gcc.  */
-  struct symtab_and_line sal;
   char *arg = NULL;
   gdbpy_ref<> result;
   gdbpy_ref<> unparsed;
@@ -659,54 +655,43 @@ gdbpy_decode_line (PyObject *self, PyObject *args)
   if (! PyArg_ParseTuple (args, "|s", &arg))
     return NULL;
 
-  sals.sals = NULL;
-
   if (arg != NULL)
     location = string_to_event_location_basic (&arg, python_language);
 
+  std::vector<symtab_and_line> decoded_sals;
+  symtab_and_line def_sal;
+  gdb::array_view<symtab_and_line> sals;
   TRY
     {
       if (location != NULL)
-	sals = decode_line_1 (location.get (), 0, NULL, NULL, 0);
+	{
+	  decoded_sals = decode_line_1 (location.get (), 0, NULL, NULL, 0);
+	  sals = decoded_sals;
+	}
       else
 	{
 	  set_default_source_symtab_and_line ();
-	  sal = get_current_source_symtab_and_line ();
-	  sals.sals = &sal;
-	  sals.nelts = 1;
+	  def_sal = get_current_source_symtab_and_line ();
+	  sals = def_sal;
 	}
     }
   CATCH (ex, RETURN_MASK_ALL)
     {
-      except = ex;
-    }
-  END_CATCH
-
-  /* Ensure that the sals data is freed, when needed.  */
-  gdb::unique_xmalloc_ptr<struct symtab_and_line> free_sals;
-  if (sals.sals != NULL && sals.sals != &sal)
-    free_sals.reset (sals.sals);
-
-  if (except.reason < 0)
-    {
       /* We know this will always throw.  */
-      gdbpy_convert_exception (except);
+      gdbpy_convert_exception (ex);
       return NULL;
     }
+  END_CATCH
 
-  if (sals.nelts)
+  if (!sals.empty ())
     {
-      int i;
-
-      result.reset (PyTuple_New (sals.nelts));
+      result.reset (PyTuple_New (sals.size ()));
       if (result == NULL)
 	return NULL;
-      for (i = 0; i < sals.nelts; ++i)
+      for (size_t i = 0; i < sals.size (); ++i)
 	{
-	  PyObject *obj;
-
-	  obj = symtab_and_line_to_sal_object (sals.sals[i]);
-	  if (! obj)
+	  PyObject *obj = symtab_and_line_to_sal_object (sals[i]);
+	  if (obj == NULL)
 	    return NULL;
 
 	  PyTuple_SetItem (result.get (), i, obj);
diff --git a/gdb/source.c b/gdb/source.c
index 769d9ef..0c44879 100644
--- a/gdb/source.c
+++ b/gdb/source.c
@@ -258,8 +258,6 @@ clear_current_source_symtab_and_line (void)
 void
 select_source_symtab (struct symtab *s)
 {
-  struct symtabs_and_lines sals;
-  struct symtab_and_line sal;
   struct objfile *ofp;
   struct compunit_symtab *cu;
 
@@ -278,10 +276,10 @@ select_source_symtab (struct symtab *s)
      if one exists.  */
   if (lookup_symbol (main_name (), 0, VAR_DOMAIN, 0).symbol)
     {
-      sals = decode_line_with_current_source (main_name (),
-					      DECODE_LINE_FUNFIRSTLINE);
-      sal = sals.sals[0];
-      xfree (sals.sals);
+      std::vector<symtab_and_line> sals
+	= decode_line_with_current_source (main_name (),
+					   DECODE_LINE_FUNFIRSTLINE);
+      const symtab_and_line &sal = sals[0];
       current_source_pspace = sal.pspace;
       current_source_symtab = sal.symtab;
       current_source_line = std::max (sal.line - (lines_to_list - 1), 1);
@@ -1504,41 +1502,37 @@ print_source_lines (struct symtab *s, int line, int stopline,
 static void
 line_info (char *arg, int from_tty)
 {
-  struct symtabs_and_lines sals;
-  struct symtab_and_line sal;
   CORE_ADDR start_pc, end_pc;
-  int i;
-  struct cleanup *cleanups;
 
-  init_sal (&sal);		/* initialize to zeroes */
+  std::vector<symtab_and_line> decoded_sals;
+  symtab_and_line curr_sal;
+  gdb::array_view<symtab_and_line> sals;
 
   if (arg == 0)
     {
-      sal.symtab = current_source_symtab;
-      sal.pspace = current_program_space;
+      init_sal (&curr_sal);		/* initialize to zeroes */
+      curr_sal.symtab = current_source_symtab;
+      curr_sal.pspace = current_program_space;
       if (last_line_listed != 0)
-	sal.line = last_line_listed;
+	curr_sal.line = last_line_listed;
       else
-	sal.line = current_source_line;
+	curr_sal.line = current_source_line;
 
-      sals.nelts = 1;
-      sals.sals = XNEW (struct symtab_and_line);
-      sals.sals[0] = sal;
+      sals = curr_sal;
     }
   else
     {
-      sals = decode_line_with_last_displayed (arg, DECODE_LINE_LIST_MODE);
+      decoded_sals = decode_line_with_last_displayed (arg,
+						      DECODE_LINE_LIST_MODE);
+      sals = decoded_sals;
 
       dont_repeat ();
     }
 
-  cleanups = make_cleanup (xfree, sals.sals);
-
   /* C++  More than one line may have been specified, as when the user
      specifies an overloaded function name.  Print info on them all.  */
-  for (i = 0; i < sals.nelts; i++)
+  for (const auto &sal : sals)
     {
-      sal = sals.sals[i];
       if (sal.pspace != current_program_space)
 	continue;
 
@@ -1599,7 +1593,7 @@ line_info (char *arg, int from_tty)
 
 	  /* If this is the only line, show the source code.  If it could
 	     not find the file, don't do anything special.  */
-	  if (annotation_level && sals.nelts == 1)
+	  if (annotation_level && sals.size () == 1)
 	    identify_source_line (sal.symtab, sal.line, 0, start_pc);
 	}
       else
@@ -1609,7 +1603,6 @@ line_info (char *arg, int from_tty)
 	printf_filtered (_("Line number %d is out of range for \"%s\".\n"),
 			 sal.line, symtab_to_filename_for_display (sal.symtab));
     }
-  do_cleanups (cleanups);
 }
 \f
 /* Commands to search the source file for a regexp.  */
diff --git a/gdb/stack.c b/gdb/stack.c
index 3e9dca2..82600ec 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2527,26 +2527,23 @@ func_command (char *arg, int from_tty)
 {
   struct frame_info *frame;
   int found = 0;
-  struct symtabs_and_lines sals;
-  int i;
   int level = 1;
   struct function_bounds *func_bounds = NULL;
-  struct cleanup *cleanups;
 
   if (arg == NULL)
     return;
 
   frame = get_current_frame ();
-  sals = decode_line_with_current_source (arg, DECODE_LINE_FUNFIRSTLINE);
-  cleanups = make_cleanup (xfree, sals.sals);
-  func_bounds = XNEWVEC (struct function_bounds, sals.nelts);
-  make_cleanup (xfree, func_bounds);
-  for (i = 0; (i < sals.nelts && !found); i++)
+  std::vector<symtab_and_line> sals
+    = decode_line_with_current_source (arg, DECODE_LINE_FUNFIRSTLINE);
+  func_bounds = XNEWVEC (struct function_bounds, sals.size ());
+  struct cleanup *cleanups = make_cleanup (xfree, func_bounds);
+  for (size_t i = 0; (i < sals.size () && !found); i++)
     {
-      if (sals.sals[i].pspace != current_program_space)
+      if (sals[i].pspace != current_program_space)
 	func_bounds[i].low = func_bounds[i].high = 0;
-      else if (sals.sals[i].pc == 0
-	       || find_pc_partial_function (sals.sals[i].pc, NULL,
+      else if (sals[i].pc == 0
+	       || find_pc_partial_function (sals[i].pc, NULL,
 					    &func_bounds[i].low,
 					    &func_bounds[i].high) == 0)
 	{
@@ -2556,7 +2553,7 @@ func_command (char *arg, int from_tty)
 
   do
     {
-      for (i = 0; (i < sals.nelts && !found); i++)
+      for (size_t i = 0; (i < sals.size () && !found); i++)
 	found = (get_frame_pc (frame) >= func_bounds[i].low
 		 && get_frame_pc (frame) < func_bounds[i].high);
       if (!found)
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 35949f0..37cfe05 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1441,11 +1441,6 @@ struct symtab_and_line
 
 extern void init_sal (struct symtab_and_line *sal);
 
-struct symtabs_and_lines
-{
-  struct symtab_and_line *sals;
-  int nelts;
-};
 \f
 
 /* Given a pc value, return line number it is in.  Second arg nonzero means
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 86acdbe..8f4de36 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -2433,30 +2433,24 @@ tfind_tracepoint_command (char *args, int from_tty)
 static void
 tfind_line_command (char *args, int from_tty)
 {
-  static CORE_ADDR start_pc, end_pc;
-  struct symtabs_and_lines sals;
-  struct symtab_and_line sal;
-  struct cleanup *old_chain;
-
   check_trace_running (current_trace_status ());
 
+  symtab_and_line sal;
   if (args == 0 || *args == 0)
     {
       sal = find_pc_line (get_frame_pc (get_current_frame ()), 0);
-      sals.nelts = 1;
-      sals.sals = XNEW (struct symtab_and_line);
-      sals.sals[0] = sal;
     }
   else
     {
-      sals = decode_line_with_current_source (args, DECODE_LINE_FUNFIRSTLINE);
-      sal = sals.sals[0];
+      std::vector<symtab_and_line> sals
+	= decode_line_with_current_source (args, DECODE_LINE_FUNFIRSTLINE);
+      sal = sals[0];
     }
-  
-  old_chain = make_cleanup (xfree, sals.sals);
+
   if (sal.symtab == 0)
     error (_("No line number information available."));
 
+  CORE_ADDR start_pc, end_pc;
   if (sal.line > 0 && find_line_pc_range (sal, &start_pc, &end_pc))
     {
       if (start_pc == end_pc)
@@ -2491,7 +2485,6 @@ tfind_line_command (char *args, int from_tty)
     tfind_1 (tfind_range, 0, start_pc, end_pc - 1, from_tty);
   else
     tfind_1 (tfind_outside, 0, start_pc, end_pc - 1, from_tty);
-  do_cleanups (old_chain);
 }
 
 /* tfind range command */
@@ -2562,7 +2555,6 @@ tfind_outside_command (char *args, int from_tty)
 static void
 scope_info (char *args, int from_tty)
 {
-  struct symtabs_and_lines sals;
   struct symbol *sym;
   struct bound_minimal_symbol msym;
   const struct block *block;
@@ -2579,17 +2571,18 @@ scope_info (char *args, int from_tty)
 
   event_location_up location = string_to_event_location (&args,
 							 current_language);
-  sals = decode_line_1 (location.get (), DECODE_LINE_FUNFIRSTLINE,
-			NULL, NULL, 0);
-  if (sals.nelts == 0)
+  std::vector<symtab_and_line> sals
+    = decode_line_1 (location.get (), DECODE_LINE_FUNFIRSTLINE,
+		     NULL, NULL, 0);
+  if (sals.empty ())
     {
       /* Presumably decode_line_1 has already warned.  */
       return;
     }
 
   /* Resolve line numbers to PC.  */
-  resolve_sal_pc (&sals.sals[0]);
-  block = block_for_pc (sals.sals[0].pc);
+  resolve_sal_pc (&sals[0]);
+  block = block_for_pc (sals[0].pc);
 
   while (block != 0)
     {
-- 
2.5.5

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector
  2017-08-21 19:27 [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector Pedro Alves
                   ` (2 preceding siblings ...)
  2017-08-21 19:28 ` [PATCH 2/3] struct symtabs_and_lines -> std::vector<symtab_and_line> Pedro Alves
@ 2017-09-04 16:15 ` Pedro Alves
  3 siblings, 0 replies; 6+ messages in thread
From: Pedro Alves @ 2017-09-04 16:15 UTC (permalink / raw)
  To: GDB Patches

On 08/21/2017 08:27 PM, Pedro Alves wrote:
> Looking at the "list and multiple locations" patch [1], I thought that
> it's a bit annoying/frustrating to have do remember to free the sals
> array that a symtabs_and_lines object wraps, with 'xfree (sals.sals)'
> or the equivalent make_cleanup(xfree, ...), not to mention how
> error-prone manual memory management is.  We can do better in C++.
> 
> So I wrote a patch to replace 'struct symtabs_and_lines' with 'typedef
> std::vector<symtab_and_line> symtabs_and_lines'.  That works nicelly,
> except that I wasn't entirely happy that that would introduce heap
> allocations in a few cases where there is none nowadays.
> 
> I then addressed that by adding an array_view abstraction, which is
> something that I've wanted at other times before.
> 
> After that, we'd end up with some places using a std::vector disguised
> behind the symtabs_and_lines typedef, while in other places we'd be
> spelling out gdb::array_view.  With that, I no longer see any value in
> keeping the symtabs_and_lines typedef, kind of giving preference to
> std::vector, so I removed the typedef, choosing to spell out
> std::vector explicitly.
> 
> (I'm aware that there are several proposals for adding something like
> array_view to the standard C++ library.  In this implementation, which
> was written from scratch and developed in parallel with the unit
> tests, I chose to keep it as simple and safe as possible.  For
> example, there is no support for ranks != 1, simply because the main
> use case this aims at addressing is abstracting out std::vector vs
> stack arrays in APIs.)
> 
> [1] https://sourceware.org/ml/gdb-patches/2017-07/msg00280.html
> 
> Note that the net increase shown below is mostly caused by the new
> unit tests.  If we only count patches 2 and 3, then we get instead:
> 
>   33 files changed, 537 insertions(+), 820 deletions(-)

FYI, I'm pushing this in now.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] Introduce gdb::array_view
  2017-08-21 19:27 ` [PATCH 1/3] Introduce gdb::array_view Pedro Alves
@ 2017-09-04 16:19   ` Pedro Alves
  0 siblings, 0 replies; 6+ messages in thread
From: Pedro Alves @ 2017-09-04 16:19 UTC (permalink / raw)
  To: GDB Patches

Here's what I pushed.  It's basically the same except for some
unit tests improvements around some comments and simplifications.

From 7c44b49cb63662b76c6301fdc8e022d7aca655bf Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 4 Sep 2017 17:10:12 +0100
Subject: [PATCH] Introduce gdb::array_view

An array_view is an abstraction that provides a non-owning view over a
sequence of contiguous objects.

A way to put it is that array_view is to std::vector (and std::array
and built-in arrays with rank==1) like std::string_view is to
std::string.

The main intent of array_view is to use it as function input parameter
type, making it possible to pass in any sequence of contiguous
objects, irrespective of whether the objects live on the stack or heap
and what actual container owns them.  Implicit construction from the
element type is supported too, making it easy to call functions that
expect an array of elements when you only have one element (usually on
the stack).  For example:

 struct A { .... };
 void function (gdb::array_view<A> as);

 std::vector<A> std_vec = ...;
 std::array<A, N> std_array = ...;
 A array[] = {...};
 A elem;

 function (std_vec);
 function (std_array);
 function (array);
 function (elem);

Views can be either mutable or const.  A const view is simply created
by specifying a const T as array_view template parameter, in which
case operator[] of non-const array_view objects ends up returning
const references.  (Making the array_view itself const is analogous to
making a pointer itself be const.  I.e., disables re-seating the
view/pointer.)  Normally functions will pass around array_views by
value.

Uses of gdb::array_view (other than the ones in the unit tests) will
be added in a follow up patch.

gdb/ChangeLog
2017-09-04  Pedro Alves  <palves@redhat.com>

	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
	unittests/array-view-selftests.c.
	(SUBDIR_UNITTESTS_OBS): Add array-view-selftests.o.
	* common/array-view.h: New file.
	* unittests/array-view-selftests.c: New file.
---
 gdb/ChangeLog                        |   8 +
 gdb/Makefile.in                      |   2 +
 gdb/common/array-view.h              | 179 +++++++++++++
 gdb/unittests/array-view-selftests.c | 494 +++++++++++++++++++++++++++++++++++
 4 files changed, 683 insertions(+)
 create mode 100644 gdb/common/array-view.h
 create mode 100644 gdb/unittests/array-view-selftests.c

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 2d12899..f0e9797 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,13 @@
 2017-09-04  Pedro Alves  <palves@redhat.com>
 
+	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
+	unittests/array-view-selftests.c.
+	(SUBDIR_UNITTESTS_OBS): Add array-view-selftests.o.
+	* common/array-view.h: New file.
+	* unittests/array-view-selftests.c: New file.
+
+2017-09-04  Pedro Alves  <palves@redhat.com>
+
 	* cli/cli-cmds.c (edit_command): Pass message to
 	ambiguous_line_spec.
 	(list_command): Pass message to ambiguous_line_spec.  Say
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 85de646..d9094fd 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -526,6 +526,7 @@ SUBDIR_PYTHON_LDFLAGS =
 SUBDIR_PYTHON_CFLAGS =
 
 SUBDIR_UNITTESTS_SRCS = \
+	unittests/array-view-selftests.c \
 	unittests/environ-selftests.c \
 	unittests/function-view-selftests.c \
 	unittests/offset-type-selftests.c \
@@ -534,6 +535,7 @@ SUBDIR_UNITTESTS_SRCS = \
 	unittests/scoped_restore-selftests.c
 
 SUBDIR_UNITTESTS_OBS = \
+	array-view-selftests.o \
 	environ-selftests.o \
 	function-view-selftests.o \
 	offset-type-selftests.o \
diff --git a/gdb/common/array-view.h b/gdb/common/array-view.h
new file mode 100644
index 0000000..85d189c
--- /dev/null
+++ b/gdb/common/array-view.h
@@ -0,0 +1,179 @@
+/* Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef COMMON_ARRAY_VIEW_H
+#define COMMON_ARRAY_VIEW_H
+
+#include "traits.h"
+#include <type_traits>
+
+/* An array_view is an abstraction that provides a non-owning view
+   over a sequence of contiguous objects.
+
+   A way to put it is that array_view is to std::vector (and
+   std::array and built-in arrays with rank==1) like std::string_view
+   is to std::string.
+
+   The main intent of array_view is to use it as function input
+   parameter type, making it possible to pass in any sequence of
+   contiguous objects, irrespective of whether the objects live on the
+   stack or heap and what actual container owns them.  Implicit
+   construction from the element type is supported too, making it easy
+   to call functions that expect an array of elements when you only
+   have one element (usually on the stack).  For example:
+
+    struct A { .... };
+    void function (gdb::array_view<A> as);
+
+    std::vector<A> std_vec = ...;
+    std::array<A, N> std_array = ...;
+    A array[] = {...};
+    A elem;
+
+    function (std_vec);
+    function (std_array);
+    function (array);
+    function (elem);
+
+   Views can be either mutable or const.  A const view is simply
+   created by specifying a const T as array_view template parameter,
+   in which case operator[] of non-const array_view objects ends up
+   returning const references.  Making the array_view itself const is
+   analogous to making a pointer itself be const.  I.e., disables
+   re-seating the view/pointer.
+
+   Since array_view objects are small (pointer plus size), and
+   designed to be trivially copyable, they should generally be passed
+   around by value.
+
+   You can find unit tests covering the whole API in
+   unittests/array-view-selftests.c.  */
+
+namespace gdb {
+
+template <typename T>
+class array_view
+{
+  /* True iff decayed T is the same as decayed U.  E.g., we want to
+     say that 'T&' is the same as 'const T'.  */
+  template <typename U>
+  using IsDecayedT = typename std::is_same<typename std::decay<T>::type,
+					   typename std::decay<U>::type>;
+
+  /* True iff decayed T is the same as decayed U, and 'U *' is
+     implicitly convertible to 'T *'.  This is a requirement for
+     several methods.  */
+  template <typename U>
+  using DecayedConvertible = gdb::And<IsDecayedT<U>,
+				      std::is_convertible<U *, T *>>;
+
+public:
+  using value_type = T;
+  using reference = T &;
+  using const_reference = const T &;
+  using size_type = size_t;
+
+  /* Default construction creates an empty view.  */
+  constexpr array_view () noexcept
+    : m_array (nullptr), m_size (0)
+  {}
+
+  /* Create an array view over a single object of the type of an
+     array_view element.  The created view as size==1.  This is
+     templated on U to allow constructing a array_view<const T> over a
+     (non-const) T.  The "convertible" requirement makes sure that you
+     can't create an array_view<T> over a const T.  */
+  template<typename U,
+	   typename = Requires<DecayedConvertible<U>>>
+  constexpr array_view (U &elem) noexcept
+    : m_array (&elem), m_size (1)
+  {}
+
+  /* Same as above, for rvalue references.  */
+  template<typename U,
+	   typename = Requires<DecayedConvertible<U>>>
+  constexpr array_view (U &&elem) noexcept
+    : m_array (&elem), m_size (1)
+  {}
+
+  /* Create an array view from a pointer to an array and an element
+     count.  */
+  template<typename U,
+	   typename = Requires<DecayedConvertible<U>>>
+  constexpr array_view (U *array, size_t size) noexcept
+    : m_array (array), m_size (size)
+  {}
+
+  /* Create an array view from a range.  This is templated on both U
+     an V to allow passing in a mix of 'const T *' and 'T *'.  */
+  template<typename U, typename V,
+	   typename = Requires<DecayedConvertible<U>>,
+	   typename = Requires<DecayedConvertible<V>>>
+  constexpr array_view (U *begin, V *end) noexcept
+    : m_array (begin), m_size (end - begin)
+  {}
+
+  /* Create an array view from an array.  */
+  template<typename U, size_t Size,
+	   typename = Requires<DecayedConvertible<U>>>
+  constexpr array_view (U (&array)[Size]) noexcept
+    : m_array (array), m_size (Size)
+  {}
+
+  /* Create an array view from a contiguous container.  E.g.,
+     std::vector and std::array.  */
+  template<typename Container,
+	   typename = Requires<gdb::Not<IsDecayedT<Container>>>,
+	   typename
+	     = Requires<std::is_convertible
+			<decltype (std::declval<Container> ().data ()),
+			 T *>>,
+	   typename
+	     = Requires<std::is_convertible
+			<decltype (std::declval<Container> ().size ()),
+			 size_type>>>
+  constexpr array_view (Container &&c) noexcept
+    : m_array (c.data ()), m_size (c.size ())
+  {}
+
+  /* Observer methods.  Some of these can't be constexpr until we
+     require C++14.  */
+  /*constexpr14*/ T *data () noexcept { return m_array; }
+  constexpr const T *data () const noexcept { return m_array; }
+
+  /*constexpr14*/ T *begin () noexcept { return m_array; }
+  constexpr const T *begin () const noexcept { return m_array; }
+
+  /*constexpr14*/ T *end () noexcept { return m_array + m_size; }
+  constexpr const T *end () const noexcept { return m_array + m_size; }
+
+  /*constexpr14*/ reference operator[] (size_t index) noexcept
+  { return m_array[index]; }
+  constexpr const_reference operator[] (size_t index) const noexcept
+  { return m_array[index]; }
+
+  constexpr size_type size () const noexcept { return m_size; }
+  constexpr bool empty () const noexcept { return m_size == 0; }
+
+private:
+  T *m_array;
+  size_type m_size;
+};
+
+} /* namespace gdb */
+
+#endif
diff --git a/gdb/unittests/array-view-selftests.c b/gdb/unittests/array-view-selftests.c
new file mode 100644
index 0000000..c4c95bc
--- /dev/null
+++ b/gdb/unittests/array-view-selftests.c
@@ -0,0 +1,494 @@
+/* Self tests for array_view for GDB, the GNU debugger.
+
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "selftest.h"
+#include "common/array-view.h"
+
+namespace selftests {
+namespace array_view_tests {
+
+/* Triviality checks.  */
+#define CHECK_TRAIT(TRAIT)			\
+  static_assert (std::TRAIT<gdb::array_view<gdb_byte>>::value, "")
+
+#if HAVE_IS_TRIVIALLY_COPYABLE
+
+CHECK_TRAIT (is_trivially_copyable);
+CHECK_TRAIT (is_trivially_move_assignable);
+CHECK_TRAIT (is_trivially_move_constructible);
+CHECK_TRAIT (is_trivially_destructible);
+
+#endif
+
+#undef CHECK_TRAIT
+
+/* Wrapper around std::is_convertible to make the code using it a bit
+   shorter.  (With C++14 we'd use a variable template instead.)  */
+
+template<typename From, typename To>
+static constexpr bool
+is_convertible ()
+{
+  return std::is_convertible<From, To>::value;
+}
+
+/* Check for implicit conversion to immutable and mutable views.  */
+
+static constexpr bool
+check_convertible ()
+{
+  using T = gdb_byte;
+  using gdb::array_view;
+
+  return (true
+	  /* immutable array_view */
+	  &&  is_convertible<const T (&) [1],	array_view<const T>> ()
+	  &&  is_convertible<T (&) [1], 	array_view<const T>> ()
+	  &&  is_convertible<const T, 		array_view<const T>> ()
+	  &&  is_convertible<T, 		array_view<const T>> ()
+
+	  /* mutable array_view */
+	  &&  is_convertible<T (&) [1], 	array_view<T>> ()
+	  && !is_convertible<const T (&) [1],	array_view<T>> ()
+	  &&  is_convertible<T, 		array_view<T>> ()
+	  && !is_convertible<const T,		array_view<T>> ()
+
+	  /* While float is implicitly convertible to gdb_byte, we
+	     don't want implicit float->array_view<gdb_byte>
+	     conversion.  */
+	  && !is_convertible<float, 		array_view<const T>> ()
+	  && !is_convertible<float, 		array_view<T>> ());
+}
+
+static_assert (check_convertible (), "");
+
+namespace no_slicing
+{
+struct A { int i; };
+struct B : A { int j; };
+struct C : A { int l; };
+
+/* Check that there's no array->view conversion for arrays of derived
+   types or subclasses.  */
+static constexpr bool
+check ()
+{
+  using gdb::array_view;
+
+  return (true
+
+	  /* array->view  */
+
+	  &&  is_convertible <A (&)[1], array_view<A>> ()
+	  && !is_convertible <B (&)[1], array_view<A>> ()
+	  && !is_convertible <C (&)[1], array_view<A>> ()
+
+	  && !is_convertible <A (&)[1], array_view<B>> ()
+	  &&  is_convertible <B (&)[1], array_view<B>> ()
+	  && !is_convertible <C (&)[1], array_view<B>> ()
+
+	  /* elem->view  */
+
+	  &&  is_convertible <A, array_view<A>> ()
+	  && !is_convertible <B, array_view<A>> ()
+	  && !is_convertible <C, array_view<A>> ()
+
+	  && !is_convertible <A, array_view<B>> ()
+	  &&  is_convertible <B, array_view<B>> ()
+	  && !is_convertible <C, array_view<B>> ());
+}
+
+} /* namespace no_slicing */
+
+static_assert (no_slicing::check (), "");
+
+/* Check that array_view implicitly converts from std::vector.  */
+
+static constexpr bool
+check_convertible_from_std_vector ()
+{
+  using gdb::array_view;
+  using T = gdb_byte;
+
+  /* Note there's no such thing as std::vector<const T>.  */
+
+  return (true
+	  &&  is_convertible <std::vector<T>, array_view<T>> ()
+	  &&  is_convertible <std::vector<T>, array_view<const T>> ());
+}
+
+static_assert (check_convertible_from_std_vector (), "");
+
+/* Check that array_view implicitly converts from std::array.  */
+
+static constexpr bool
+check_convertible_from_std_array ()
+{
+  using gdb::array_view;
+  using T = gdb_byte;
+
+  /* Note: a non-const T view can't refer to a const T array.  */
+
+  return (true
+	  &&  is_convertible <std::array<T, 1>,		array_view<T>> ()
+	  &&  is_convertible <std::array<T, 1>,		array_view<const T>> ()
+	  && !is_convertible <std::array<const T, 1>,	array_view<T>> ()
+	  &&  is_convertible <std::array<const T, 1>,	array_view<const T>> ());
+}
+
+static_assert (check_convertible_from_std_array (), "");
+
+/* Check that VIEW views C (a container like std::vector/std::array)
+   correctly.  */
+
+template<typename View, typename Container>
+static bool
+check_container_view (const View &view, const Container &c)
+{
+  if (view.empty ())
+    return false;
+  if (view.size () != c.size ())
+    return false;
+  if (view.data () != c.data ())
+    return false;
+  for (size_t i = 0; i < c.size (); i++)
+    {
+      if (&view[i] != &c[i])
+	return false;
+      if (view[i] != c[i])
+	return false;
+    }
+  return true;
+}
+
+/* Check that VIEW views E (an object of the type of a view element)
+   correctly.  */
+
+template<typename View, typename Elem>
+static bool
+check_elem_view (const View &view, const Elem &e)
+{
+  if (view.empty ())
+    return false;
+  if (view.size () != 1)
+    return false;
+  if (view.data () != &e)
+    return false;
+  if (&view[0] != &e)
+    return false;
+  if (view[0] != e)
+    return false;
+  return true;
+}
+
+/* Check for operator[].  The first overload is taken iff
+   'view<T>()[0] = T()' is a valid expression.  */
+
+template<typename View,
+	 typename = decltype (std::declval<View> ()[0]
+			      = std::declval<typename View::value_type> ())>
+static bool
+check_op_subscript (const View &view)
+{
+  return true;
+}
+
+/* This overload is taken iff 'view<T>()[0] = T()' is not a valid
+   expression.  */
+
+static bool
+check_op_subscript (...)
+{
+  return false;
+}
+
+/* Check construction with pointer + size.  This is a template in
+   order to test both gdb_byte and const gdb_byte.  */
+
+template<typename T>
+static void
+check_ptr_size_ctor ()
+{
+  T data[] = {0x11, 0x22, 0x33, 0x44};
+
+  gdb::array_view<T> view (data + 1, 2);
+
+  SELF_CHECK (!view.empty ());
+  SELF_CHECK (view.size () == 2);
+  SELF_CHECK (view.data () == &data[1]);
+  SELF_CHECK (view[0] == data[1]);
+  SELF_CHECK (view[1] == data[2]);
+
+  gdb::array_view<const T> cview (data + 1, 2);
+  SELF_CHECK (!cview.empty ());
+  SELF_CHECK (cview.size () == 2);
+  SELF_CHECK (cview.data () == &data[1]);
+  SELF_CHECK (cview[0] == data[1]);
+  SELF_CHECK (cview[1] == data[2]);
+}
+
+/* Asserts std::is_constructible.  */
+
+template<typename T, typename... Args>
+static constexpr bool
+require_not_constructible ()
+{
+  static_assert (!std::is_constructible<T, Args...>::value, "");
+
+  /* constexpr functions can't return void in C++11 (N3444).  */
+  return true;
+};
+
+/* Check the array_view<T>(PTR, SIZE) ctor, when T is a pointer.  */
+
+void
+check_ptr_size_ctor2 ()
+{
+  struct A {};
+  A an_a;
+
+  A *array[] = { &an_a };
+  const A * const carray[] = { &an_a };
+
+  gdb::array_view<A *> v1 = {array, ARRAY_SIZE (array)};
+  gdb::array_view<A *> v2 = {array, (char) ARRAY_SIZE (array)};
+  gdb::array_view<A * const> v3 = {array, ARRAY_SIZE (array)};
+  gdb::array_view<const A * const> cv1 = {carray, ARRAY_SIZE (carray)};
+
+  require_not_constructible<gdb::array_view<A *>, decltype (carray), size_t> ();
+
+  SELF_CHECK (v1[0] == array[0]);
+  SELF_CHECK (v2[0] == array[0]);
+  SELF_CHECK (v3[0] == array[0]);
+
+  SELF_CHECK (!v1.empty ());
+  SELF_CHECK (v1.size () == 1);
+  SELF_CHECK (v1.data () == &array[0]);
+
+  SELF_CHECK (cv1[0] == carray[0]);
+
+  SELF_CHECK (!cv1.empty ());
+  SELF_CHECK (cv1.size () == 1);
+  SELF_CHECK (cv1.data () == &carray[0]);
+}
+
+/* Check construction with a pair of pointers.  This is a template in
+   order to test both gdb_byte and const gdb_byte.  */
+
+template<typename T>
+static void
+check_ptr_ptr_ctor ()
+{
+  T data[] = {0x11, 0x22, 0x33, 0x44};
+
+  gdb::array_view<T> view (data + 1, data + 3);
+
+  SELF_CHECK (!view.empty ());
+  SELF_CHECK (view.size () == 2);
+  SELF_CHECK (view.data () == &data[1]);
+  SELF_CHECK (view[0] == data[1]);
+  SELF_CHECK (view[1] == data[2]);
+
+  gdb_byte array[] = {0x11, 0x22, 0x33, 0x44};
+  const gdb_byte *p1 = array;
+  gdb_byte *p2 = array + ARRAY_SIZE (array);
+  gdb::array_view<const gdb_byte> view2 (p1, p2);
+}
+
+/* Check construction with a pair of pointers of mixed constness.  */
+
+static void
+check_ptr_ptr_mixed_cv ()
+{
+  gdb_byte array[] = {0x11, 0x22, 0x33, 0x44};
+  const gdb_byte *cp = array;
+  gdb_byte *p = array;
+  gdb::array_view<const gdb_byte> view1 (cp, p);
+  gdb::array_view<const gdb_byte> view2 (p, cp);
+  SELF_CHECK (view1.empty ());
+  SELF_CHECK (view2.empty ());
+}
+
+/* Check range-for support (i.e., begin()/end()).  This is a template
+   in order to test both gdb_byte and const gdb_byte.  */
+
+template<typename T>
+static void
+check_range_for ()
+{
+  T data[] = {1, 2, 3, 4};
+  gdb::array_view<T> view (data);
+
+  typename std::decay<T>::type sum = 0;
+  for (auto &elem : view)
+    sum += elem;
+  SELF_CHECK (sum == 1 + 2 + 3 + 4);
+}
+
+/* Entry point.  */
+
+static void
+run_tests ()
+{
+  /* Empty views.  */
+  {
+    constexpr gdb::array_view<gdb_byte> view1;
+    constexpr gdb::array_view<const gdb_byte> view2;
+
+    static_assert (view1.empty (), "");
+    static_assert (view1.data () == nullptr, "");
+    static_assert (view1.size () == 0, "");
+    static_assert (view2.empty (), "");
+    static_assert (view2.size () == 0, "");
+    static_assert (view2.data () == nullptr, "");
+  }
+
+  std::vector<gdb_byte> vec = {0x11, 0x22, 0x33, 0x44 };
+  std::array<gdb_byte, 4> array = {{0x11, 0x22, 0x33, 0x44}};
+
+  /* Various tests of views over std::vector.  */
+  {
+    gdb::array_view<gdb_byte> view = vec;
+    SELF_CHECK (check_container_view (view, vec));
+    gdb::array_view<const gdb_byte> cview = vec;
+    SELF_CHECK (check_container_view (cview, vec));
+  }
+
+  /* Likewise, over std::array.  */
+  {
+    gdb::array_view<gdb_byte> view = array;
+    SELF_CHECK (check_container_view (view, array));
+    gdb::array_view<gdb_byte> cview = array;
+    SELF_CHECK (check_container_view (cview, array));
+  }
+
+  /* op=(std::vector/std::array/elem) */
+  {
+    gdb::array_view<gdb_byte> view;
+
+    view = vec;
+    SELF_CHECK (check_container_view (view, vec));
+    view = std::move (vec);
+    SELF_CHECK (check_container_view (view, vec));
+
+    view = array;
+    SELF_CHECK (check_container_view (view, array));
+    view = std::move (array);
+    SELF_CHECK (check_container_view (view, array));
+
+    gdb_byte elem = 0;
+    view = elem;
+    SELF_CHECK (check_elem_view (view, elem));
+    view = std::move (elem);
+    SELF_CHECK (check_elem_view (view, elem));
+  }
+
+  /* Test copy/move ctor and mutable->immutable conversion.  */
+  {
+    gdb_byte data[] = {0x11, 0x22, 0x33, 0x44};
+    gdb::array_view<gdb_byte> view1 = data;
+    gdb::array_view<gdb_byte> view2 = view1;
+    gdb::array_view<gdb_byte> view3 = std::move (view1);
+    gdb::array_view<const gdb_byte> cview1 = data;
+    gdb::array_view<const gdb_byte> cview2 = cview1;
+    gdb::array_view<const gdb_byte> cview3 = std::move (cview1);
+    SELF_CHECK (view1[0] == data[0]);
+    SELF_CHECK (view2[0] == data[0]);
+    SELF_CHECK (view3[0] == data[0]);
+    SELF_CHECK (cview1[0] == data[0]);
+    SELF_CHECK (cview2[0] == data[0]);
+    SELF_CHECK (cview3[0] == data[0]);
+  }
+
+  /* Same, but op=(view).  */
+  {
+    gdb_byte data[] = {0x55, 0x66, 0x77, 0x88};
+    gdb::array_view<gdb_byte> view1;
+    gdb::array_view<gdb_byte> view2;
+    gdb::array_view<gdb_byte> view3;
+    gdb::array_view<const gdb_byte> cview1;
+    gdb::array_view<const gdb_byte> cview2;
+    gdb::array_view<const gdb_byte> cview3;
+
+    view1 = data;
+    view2 = view1;
+    view3 = std::move (view1);
+    cview1 = data;
+    cview2 = cview1;
+    cview3 = std::move (cview1);
+    SELF_CHECK (view1[0] == data[0]);
+    SELF_CHECK (view2[0] == data[0]);
+    SELF_CHECK (view3[0] == data[0]);
+    SELF_CHECK (cview1[0] == data[0]);
+    SELF_CHECK (cview2[0] == data[0]);
+    SELF_CHECK (cview3[0] == data[0]);
+  }
+
+  /* op[] */
+  {
+    std::vector<gdb_byte> vec = {0x11, 0x22};
+    gdb::array_view<gdb_byte> view = vec;
+    gdb::array_view<const gdb_byte> cview = vec;
+
+    /* Check that op[] on a non-const view of non-const T returns a
+       mutable reference.  */
+    view[0] = 0x33;
+    SELF_CHECK (vec[0] == 0x33);
+
+    /* OTOH, check that assigning through op[] on a view of const T
+       wouldn't compile.  */
+    SELF_CHECK (!check_op_subscript (cview));
+    /* For completeness.  */
+    SELF_CHECK (check_op_subscript (view));
+  }
+
+  check_ptr_size_ctor<const gdb_byte> ();
+  check_ptr_size_ctor<gdb_byte> ();
+  check_ptr_size_ctor2 ();
+  check_ptr_ptr_ctor<const gdb_byte> ();
+  check_ptr_ptr_ctor<gdb_byte> ();
+  check_ptr_ptr_mixed_cv ();
+
+  check_range_for<gdb_byte> ();
+  check_range_for<const gdb_byte> ();
+
+  /* Check that the right ctor overloads are taken when the element is
+     a container.  */
+  {
+    using Vec = std::vector<gdb_byte>;
+    Vec vecs[3];
+
+    gdb::array_view<Vec> view_array = vecs;
+    SELF_CHECK (view_array.size () == 3);
+
+    Vec elem;
+    gdb::array_view<Vec> view_elem = elem;
+    SELF_CHECK (view_elem.size () == 1);
+  }
+}
+
+} /* namespace array_view_tests */
+} /* namespace selftests */
+
+void
+_initialize_array_view_selftests ()
+{
+  selftests::register_test (selftests::array_view_tests::run_tests);
+}
-- 
2.5.5


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-09-04 16:19 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-21 19:27 [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector Pedro Alves
2017-08-21 19:27 ` [PATCH 1/3] Introduce gdb::array_view Pedro Alves
2017-09-04 16:19   ` Pedro Alves
2017-08-21 19:28 ` [PATCH 3/3] Kill init_sal Pedro Alves
2017-08-21 19:28 ` [PATCH 2/3] struct symtabs_and_lines -> std::vector<symtab_and_line> Pedro Alves
2017-09-04 16:15 ` [PATCH 0/3] Introduce gdb::array_view, symtabs_and_lines -> std::vector Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).