Improve gdb::array_view ctor from contiguous containers
While reading the interface of gdb::array_view, I realized that the constructor that builds an array_view on top of a contiguous container (such as std::vector, std::array or even gdb::array_view) can be missused. Lets consider the following code sample: struct Parent { Parent (int a): a { a } {} int a; }; std::ostream &operator<< (std::ostream& os, const Parent & p) { os << "Parent {a=" << p.a << "}"; return os; } struct Child : public Parent { Child (int a, int b): Parent { a }, b { b } {} int b; }; std::ostream &operator<< (std::ostream& os, const Child & p) { os << "Child {a=" << p.a << ", b=" << p.b << "}"; return os; } template <typename T> void print (const gdb::array_view<const T> &p) { std::for_each (p.begin (), p.end (), [](const T &p) { std::cout << p << '\n'; }); } Then with the current interface nothinng prevents this usage of array_view to be done: const std::array<Child, 3> elts = { Child {1, 2}, Child {3, 4}, Child {5, 6} }; print_all<Parent> (elts); This compiles fine and produces the following output: Parent {a=1} Parent {a=2} Parent {a=3} which is obviously wrong. There is nowhere in memory a Parent-like object for which the A member is 2 and this call to print_all<Parent> shold not compile at all (calling print_all<Child> is however fine). This comes down to the fact that a Child* is convertible into a Parent*, and that an array view is constructed to a pointer to the first element and a size. The valid type pointed to that can be used with this constructor are restricted using SFINAE, which requires that a pointer to a member into the underlying container can be converted into a pointer the array_view's data type. This patch proposes to change the constraints on the gdb::array_view ctor which accepts a container now requires that the (decayed) type of the elements in the container match the (decayed) type of the array_view being constructed. Applying this change required minimum adjustment in GDB codebase, which are also included in this patch. Tested by rebuilding.
This commit is contained in:
parent
8b4195d8d1
commit
e92f2b5eef
4 changed files with 29 additions and 7 deletions
|
@ -14936,7 +14936,7 @@ add_variant_property (struct field_info *fip, struct type *type,
|
||||||
offset_map[fip->fields[i].offset] = i;
|
offset_map[fip->fields[i].offset] = i;
|
||||||
|
|
||||||
struct objfile *objfile = cu->per_objfile->objfile;
|
struct objfile *objfile = cu->per_objfile->objfile;
|
||||||
gdb::array_view<variant_part> parts
|
gdb::array_view<const variant_part> parts
|
||||||
= create_variant_parts (&objfile->objfile_obstack, offset_map, fip,
|
= create_variant_parts (&objfile->objfile_obstack, offset_map, fip,
|
||||||
fip->variant_parts);
|
fip->variant_parts);
|
||||||
|
|
||||||
|
|
|
@ -1158,7 +1158,7 @@ maintenance_selftest (const char *args, int from_tty)
|
||||||
auto grp = make_maintenance_selftest_option_group (&opts);
|
auto grp = make_maintenance_selftest_option_group (&opts);
|
||||||
gdb::option::process_options
|
gdb::option::process_options
|
||||||
(&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp);
|
(&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp);
|
||||||
gdb_argv argv (args);
|
const gdb_argv argv (args);
|
||||||
selftests::run_tests (argv.as_array_view (), opts.verbose);
|
selftests::run_tests (argv.as_array_view (), opts.verbose);
|
||||||
#else
|
#else
|
||||||
printf_filtered (_("\
|
printf_filtered (_("\
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "gdbsupport/selftest.h"
|
#include "gdbsupport/selftest.h"
|
||||||
#include "gdbsupport/array-view.h"
|
#include "gdbsupport/array-view.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace selftests {
|
namespace selftests {
|
||||||
namespace array_view_tests {
|
namespace array_view_tests {
|
||||||
|
@ -86,8 +87,8 @@ struct A { int i; };
|
||||||
struct B : A { int j; };
|
struct B : A { int j; };
|
||||||
struct C : A { int l; };
|
struct C : A { int l; };
|
||||||
|
|
||||||
/* Check that there's no array->view conversion for arrays of derived
|
/* Check that there's no array->view conversion for arrays of derived types or
|
||||||
types or subclasses. */
|
subclasses. */
|
||||||
static constexpr bool
|
static constexpr bool
|
||||||
check ()
|
check ()
|
||||||
{
|
{
|
||||||
|
@ -116,9 +117,29 @@ check ()
|
||||||
&& !is_convertible <C, array_view<B>> ());
|
&& !is_convertible <C, array_view<B>> ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check that there's no container->view conversion for containers of derived
|
||||||
|
types or subclasses. */
|
||||||
|
|
||||||
|
template<template<typename ...> typename Container>
|
||||||
|
static constexpr bool
|
||||||
|
check_ctor_from_container ()
|
||||||
|
{
|
||||||
|
using gdb::array_view;
|
||||||
|
|
||||||
|
return ( is_convertible <Container<A>, array_view<A>> ()
|
||||||
|
&& !is_convertible <Container<B>, array_view<A>> ()
|
||||||
|
&& !is_convertible <Container<C>, array_view<A>> ()
|
||||||
|
|
||||||
|
&& !is_convertible <Container<A>, array_view<B>> ()
|
||||||
|
&& is_convertible <Container<B>, array_view<B>> ()
|
||||||
|
&& !is_convertible <Container<C>, array_view<B>> ());
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace no_slicing */
|
} /* namespace no_slicing */
|
||||||
|
|
||||||
static_assert (no_slicing::check (), "");
|
static_assert (no_slicing::check (), "");
|
||||||
|
static_assert (no_slicing::check_ctor_from_container<std::vector> (), "");
|
||||||
|
static_assert (no_slicing::check_ctor_from_container<gdb::array_view> (), "");
|
||||||
|
|
||||||
/* Check that array_view implicitly converts from std::vector. */
|
/* Check that array_view implicitly converts from std::vector. */
|
||||||
|
|
||||||
|
|
|
@ -139,9 +139,10 @@ public:
|
||||||
template<typename Container,
|
template<typename Container,
|
||||||
typename = Requires<gdb::Not<IsDecayedT<Container>>>,
|
typename = Requires<gdb::Not<IsDecayedT<Container>>>,
|
||||||
typename
|
typename
|
||||||
= Requires<std::is_convertible
|
= Requires<DecayedConvertible
|
||||||
<decltype (std::declval<Container> ().data ()),
|
<typename std::remove_pointer
|
||||||
T *>>,
|
<decltype (std::declval<Container> ().data ())
|
||||||
|
>::type>>,
|
||||||
typename
|
typename
|
||||||
= Requires<std::is_convertible
|
= Requires<std::is_convertible
|
||||||
<decltype (std::declval<Container> ().size ()),
|
<decltype (std::declval<Container> ().size ()),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue