mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
23bcc18f47
The delete-memcpy-with-non-trivial-types patch exposed many instances of this problem: src/gdb/btrace.h: In function ‘btrace_insn_s* VEC_btrace_insn_s_quick_insert(VEC_btrace_insn_s*, unsigned int, const btrace_insn_s*, const char*, unsigned int)’: src/gdb/common/vec.h:948:62: error: use of deleted function ‘void* memmove(T*, const U*, size_t) [with T = btrace_insn; U = btrace_insn; <template-parameter-1-3> = void; size_t = long unsigned int]’ memmove (slot_ + 1, slot_, (vec_->num++ - ix_) * sizeof (T)); \ ^ src/gdb/common/vec.h:436:1: note: in expansion of macro ‘DEF_VEC_FUNC_O’ DEF_VEC_FUNC_O(T) \ ^ src/gdb/btrace.h:84:1: note: in expansion of macro ‘DEF_VEC_O’ DEF_VEC_O (btrace_insn_s); ^ [...] src/gdb/common/vec.h:1060:31: error: use of deleted function ‘void* memcpy(T*, const U*, size_t) [with T = btrace_insn; U = btrace_insn; <template-parameter-1-3> = void; size_t = long unsigned int]’ sizeof (T) * vec2_->num); \ ^ src/gdb/common/vec.h:437:1: note: in expansion of macro ‘DEF_VEC_ALLOC_FUNC_O’ DEF_VEC_ALLOC_FUNC_O(T) \ ^ src/gdb/btrace.h:84:1: note: in expansion of macro ‘DEF_VEC_O’ DEF_VEC_O (btrace_insn_s); ^ So, VECs (given it's C roots) rely on memcpy/memcpy of VEC elements to be well defined, in order to grow/reallocate its internal elements array. This means that we can only put trivially copyable types in VECs. E.g., if a type requires using a custom copy/move ctor to relocate, then we can't put it in a VEC (so we use std::vector instead). But, as shown above, we're violating that requirement. btrace_insn is currently not trivially copyable, because it contains an enum_flags field, and that is itself not trivially copyable. This patch corrects that, by simply removing the user-provided copy constructor and assignment operator. The compiler-generated versions work just fine. Note that std::vector relies on std::is_trivially_copyable too to know whether it can reallocate its elements with memcpy/memmove instead of having to call copy/move ctors and dtors, so if we have types in std::vectors that weren't trivially copyable because of enum_flags, this will make such vectors more efficient. gdb/ChangeLog: 2017-04-25 Pedro Alves <palves@redhat.com> * common/enum-flags.h (enum_flags): Don't implement copy ctor and assignment operator.
216 lines
5.9 KiB
C++
216 lines
5.9 KiB
C++
/* Copyright (C) 2015-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_ENUM_FLAGS_H
|
|
#define COMMON_ENUM_FLAGS_H
|
|
|
|
/* Type-safe wrapper for enum flags. enum flags are enums where the
|
|
values are bits that are meant to be ORed together.
|
|
|
|
This allows writing code like the below, while with raw enums this
|
|
would fail to compile without casts to enum type at the assignments
|
|
to 'f':
|
|
|
|
enum some_flag
|
|
{
|
|
flag_val1 = 1 << 1,
|
|
flag_val2 = 1 << 2,
|
|
flag_val3 = 1 << 3,
|
|
flag_val4 = 1 << 4,
|
|
};
|
|
DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags)
|
|
|
|
some_flags f = flag_val1 | flag_val2;
|
|
f |= flag_val3;
|
|
|
|
It's also possible to assign literal zero to an enum flags variable
|
|
(meaning, no flags), dispensing adding an awkward explicit "no
|
|
value" value to the enumeration. For example:
|
|
|
|
some_flags f = 0;
|
|
f |= flag_val3 | flag_val4;
|
|
|
|
Note that literal integers other than zero fail to compile:
|
|
|
|
some_flags f = 1; // error
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
|
|
/* Traits type used to prevent the global operator overloads from
|
|
instantiating for non-flag enums. */
|
|
template<typename T> struct enum_flags_type {};
|
|
|
|
/* Use this to mark an enum as flags enum. It defines FLAGS as
|
|
enum_flags wrapper class for ENUM, and enables the global operator
|
|
overloads for ENUM. */
|
|
#define DEF_ENUM_FLAGS_TYPE(enum_type, flags_type) \
|
|
typedef enum_flags<enum_type> flags_type; \
|
|
template<> \
|
|
struct enum_flags_type<enum_type> \
|
|
{ \
|
|
typedef enum_flags<enum_type> type; \
|
|
}
|
|
|
|
/* Until we can rely on std::underlying type being universally
|
|
available (C++11), roll our own for enums. */
|
|
template<int size, bool sign> class integer_for_size { typedef void type; };
|
|
template<> struct integer_for_size<1, 0> { typedef uint8_t type; };
|
|
template<> struct integer_for_size<2, 0> { typedef uint16_t type; };
|
|
template<> struct integer_for_size<4, 0> { typedef uint32_t type; };
|
|
template<> struct integer_for_size<8, 0> { typedef uint64_t type; };
|
|
template<> struct integer_for_size<1, 1> { typedef int8_t type; };
|
|
template<> struct integer_for_size<2, 1> { typedef int16_t type; };
|
|
template<> struct integer_for_size<4, 1> { typedef int32_t type; };
|
|
template<> struct integer_for_size<8, 1> { typedef int64_t type; };
|
|
|
|
template<typename T>
|
|
struct enum_underlying_type
|
|
{
|
|
typedef typename
|
|
integer_for_size<sizeof (T), static_cast<bool>(T (-1) < T (0))>::type
|
|
type;
|
|
};
|
|
|
|
template <typename E>
|
|
class enum_flags
|
|
{
|
|
public:
|
|
typedef E enum_type;
|
|
typedef typename enum_underlying_type<enum_type>::type underlying_type;
|
|
|
|
private:
|
|
/* Private type used to support initializing flag types with zero:
|
|
|
|
foo_flags f = 0;
|
|
|
|
but not other integers:
|
|
|
|
foo_flags f = 1;
|
|
|
|
The way this works is that we define an implicit constructor that
|
|
takes a pointer to this private type. Since nothing can
|
|
instantiate an object of this type, the only possible pointer to
|
|
pass to the constructor is the NULL pointer, or, zero. */
|
|
struct zero_type;
|
|
|
|
underlying_type
|
|
underlying_value () const
|
|
{
|
|
return m_enum_value;
|
|
}
|
|
|
|
public:
|
|
/* Allow default construction. */
|
|
enum_flags ()
|
|
: m_enum_value ((enum_type) 0)
|
|
{}
|
|
|
|
/* If you get an error saying these two overloads are ambiguous,
|
|
then you tried to mix values of different enum types. */
|
|
enum_flags (enum_type e)
|
|
: m_enum_value (e)
|
|
{}
|
|
enum_flags (struct enum_flags::zero_type *zero)
|
|
: m_enum_value ((enum_type) 0)
|
|
{}
|
|
|
|
enum_flags &operator&= (enum_type e)
|
|
{
|
|
m_enum_value = (enum_type) (underlying_value () & e);
|
|
return *this;
|
|
}
|
|
enum_flags &operator|= (enum_type e)
|
|
{
|
|
m_enum_value = (enum_type) (underlying_value () | e);
|
|
return *this;
|
|
}
|
|
enum_flags &operator^= (enum_type e)
|
|
{
|
|
m_enum_value = (enum_type) (underlying_value () ^ e);
|
|
return *this;
|
|
}
|
|
|
|
operator enum_type () const
|
|
{
|
|
return m_enum_value;
|
|
}
|
|
|
|
enum_flags operator& (enum_type e) const
|
|
{
|
|
return (enum_type) (underlying_value () & e);
|
|
}
|
|
enum_flags operator| (enum_type e) const
|
|
{
|
|
return (enum_type) (underlying_value () | e);
|
|
}
|
|
enum_flags operator^ (enum_type e) const
|
|
{
|
|
return (enum_type) (underlying_value () ^ e);
|
|
}
|
|
enum_flags operator~ () const
|
|
{
|
|
return (enum_type) ~underlying_value ();
|
|
}
|
|
|
|
private:
|
|
/* Stored as enum_type because GDB knows to print the bit flags
|
|
neatly if the enum values look like bit flags. */
|
|
enum_type m_enum_value;
|
|
};
|
|
|
|
/* Global operator overloads. */
|
|
|
|
template <typename enum_type>
|
|
typename enum_flags_type<enum_type>::type
|
|
operator& (enum_type e1, enum_type e2)
|
|
{
|
|
return enum_flags<enum_type> (e1) & e2;
|
|
}
|
|
|
|
template <typename enum_type>
|
|
typename enum_flags_type<enum_type>::type
|
|
operator| (enum_type e1, enum_type e2)
|
|
{
|
|
return enum_flags<enum_type> (e1) | e2;
|
|
}
|
|
|
|
template <typename enum_type>
|
|
typename enum_flags_type<enum_type>::type
|
|
operator^ (enum_type e1, enum_type e2)
|
|
{
|
|
return enum_flags<enum_type> (e1) ^ e2;
|
|
}
|
|
|
|
template <typename enum_type>
|
|
typename enum_flags_type<enum_type>::type
|
|
operator~ (enum_type e)
|
|
{
|
|
return ~enum_flags<enum_type> (e);
|
|
}
|
|
|
|
#else /* __cplusplus */
|
|
|
|
/* In C, the flags type is just a typedef for the enum type. */
|
|
|
|
#define DEF_ENUM_FLAGS_TYPE(enum_type, flags_type) \
|
|
typedef enum_type flags_type
|
|
|
|
#endif /* __cplusplus */
|
|
|
|
#endif /* COMMON_ENUM_FLAGS_H */
|