mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 04:24:43 +00:00
This updates the copyright headers to include 2025. I did this by running gdb/copyright.py and then manually modifying a few files as noted by the script. Approved-By: Eli Zaretskii <eliz@gnu.org>
521 lines
18 KiB
C++
521 lines
18 KiB
C++
/* Copyright (C) 2015-2025 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 GDBSUPPORT_ENUM_FLAGS_H
|
|
#define GDBSUPPORT_ENUM_FLAGS_H
|
|
|
|
#include "traits.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
|
|
*/
|
|
|
|
/* Use this to mark an enum as flags enum. It defines FLAGS_TYPE as
|
|
enum_flags wrapper class for ENUM, and enables the global operator
|
|
overloads for ENUM. */
|
|
#define DEF_ENUM_FLAGS_TYPE(enum_type, flags_type) \
|
|
using flags_type = enum_flags<enum_type>; \
|
|
void is_enum_flags_enum_type (enum_type *)
|
|
|
|
/* To enable the global enum_flags operators for enum, declare an
|
|
"is_enum_flags_enum_type" overload that has exactly one parameter,
|
|
of type a pointer to that enum class. E.g.,:
|
|
|
|
void is_enum_flags_enum_type (enum some_flag *);
|
|
|
|
The function does not need to be defined, only declared.
|
|
DEF_ENUM_FLAGS_TYPE declares this.
|
|
|
|
A function declaration is preferred over a traits type, because the
|
|
former allows calling the DEF_ENUM_FLAGS_TYPE macro inside a
|
|
namespace to define the corresponding enum flags type in that
|
|
namespace. The compiler finds the corresponding
|
|
is_enum_flags_enum_type function via ADL. */
|
|
|
|
namespace enum_flags_detail
|
|
{
|
|
|
|
/* 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;
|
|
|
|
/* gdb::Requires trait helpers. */
|
|
template <typename enum_type>
|
|
using EnumIsUnsigned
|
|
= std::is_unsigned<typename std::underlying_type<enum_type>::type>;
|
|
|
|
/* Helper to detect whether an enum has a fixed underlying type. This can be
|
|
achieved by using a scoped enum (in which case the type is "int") or
|
|
an explicit underlying type. C-style enums that are unscoped or do not
|
|
have an explicit underlying type have an implementation-defined underlying
|
|
type.
|
|
|
|
https://timsong-cpp.github.io/cppwp/n4659/dcl.enum#5
|
|
|
|
We need this trait in order to ensure that operator~ below does NOT
|
|
operate on old-style enums. This is because we apply operator~ on
|
|
the value and then cast the result to the enum_type. This is however
|
|
Undefined Behavior if the result does not fit in the range of possible
|
|
values for the enum. For enums with fixed underlying type, the entire
|
|
range of the integer is available. However, for old-style enums, the range
|
|
is only the smallest bit-field that can hold all the values of the
|
|
enumeration, typically much smaller than the underlying integer:
|
|
|
|
https://timsong-cpp.github.io/cppwp/n4659/expr.static.cast#10
|
|
https://timsong-cpp.github.io/cppwp/n4659/dcl.enum#8
|
|
|
|
To implement this, we leverage the fact that, since C++17, enums with
|
|
fixed underlying type can be list-initialized from an integer:
|
|
https://timsong-cpp.github.io/cppwp/n4659/dcl.init.list#3.7
|
|
|
|
Old-style enums cannot be initialized like that, leading to ill-formed
|
|
code.
|
|
|
|
We then use this together with SFINAE to create the desired trait.
|
|
|
|
*/
|
|
template <typename enum_type, typename = void>
|
|
struct EnumHasFixedUnderlyingType : std::false_type
|
|
{
|
|
static_assert(std::is_enum<enum_type>::value);
|
|
};
|
|
|
|
/* Specialization that is active only if enum_type can be
|
|
list-initialized from an integer (0). Only enums with fixed
|
|
underlying type satisfy this property in C++17. */
|
|
template <typename enum_type>
|
|
struct EnumHasFixedUnderlyingType<enum_type, std::void_t<decltype(enum_type{0})>> : std::true_type
|
|
{
|
|
static_assert(std::is_enum<enum_type>::value);
|
|
};
|
|
|
|
template <typename enum_type>
|
|
using EnumIsSafeForBitwiseComplement = std::conjunction<
|
|
EnumIsUnsigned<enum_type>,
|
|
EnumHasFixedUnderlyingType<enum_type>
|
|
>;
|
|
|
|
template <typename enum_type>
|
|
using EnumIsUnsafeForBitwiseComplement = std::negation<EnumIsSafeForBitwiseComplement<enum_type>>;
|
|
|
|
}
|
|
|
|
template <typename E>
|
|
class enum_flags
|
|
{
|
|
public:
|
|
using enum_type = E;
|
|
using underlying_type = typename std::underlying_type<enum_type>::type;
|
|
|
|
/* For to_string. Maps one enumerator of E to a string. */
|
|
struct string_mapping
|
|
{
|
|
E flag;
|
|
const char *str;
|
|
};
|
|
|
|
/* Convenience for to_string implementations, to build a
|
|
string_mapping array. */
|
|
#define MAP_ENUM_FLAG(ENUM_FLAG) { ENUM_FLAG, #ENUM_FLAG }
|
|
|
|
public:
|
|
/* Allow default construction. */
|
|
constexpr enum_flags ()
|
|
: m_enum_value ((enum_type) 0)
|
|
{}
|
|
|
|
/* The default move/copy ctor/assignment do the right thing. */
|
|
|
|
/* If you get an error saying these two overloads are ambiguous,
|
|
then you tried to mix values of different enum types. */
|
|
constexpr enum_flags (enum_type e)
|
|
: m_enum_value (e)
|
|
{}
|
|
constexpr enum_flags (enum_flags_detail::zero_type *zero)
|
|
: m_enum_value ((enum_type) 0)
|
|
{}
|
|
|
|
enum_flags &operator&= (enum_flags e) &
|
|
{
|
|
m_enum_value = (enum_type) (m_enum_value & e.m_enum_value);
|
|
return *this;
|
|
}
|
|
enum_flags &operator|= (enum_flags e) &
|
|
{
|
|
m_enum_value = (enum_type) (m_enum_value | e.m_enum_value);
|
|
return *this;
|
|
}
|
|
enum_flags &operator^= (enum_flags e) &
|
|
{
|
|
m_enum_value = (enum_type) (m_enum_value ^ e.m_enum_value);
|
|
return *this;
|
|
}
|
|
|
|
/* Delete rval versions. */
|
|
void operator&= (enum_flags e) && = delete;
|
|
void operator|= (enum_flags e) && = delete;
|
|
void operator^= (enum_flags e) && = delete;
|
|
|
|
/* Like raw enums, allow conversion to the underlying type. */
|
|
constexpr operator underlying_type () const
|
|
{
|
|
return m_enum_value;
|
|
}
|
|
|
|
/* Get the underlying value as a raw enum. */
|
|
constexpr enum_type raw () const
|
|
{
|
|
return m_enum_value;
|
|
}
|
|
|
|
/* Binary operations involving some unrelated type (which would be a
|
|
bug) are implemented as non-members, and deleted. */
|
|
|
|
/* Convert this object to a std::string, using MAPPING as
|
|
enumerator-to-string mapping array. This is not meant to be
|
|
called directly. Instead, enum_flags specializations should have
|
|
their own to_string function wrapping this one, thus hiding the
|
|
mapping array from callers.
|
|
|
|
Note: this is defined outside the template class so it can use
|
|
the global operators for enum_type, which are only defined after
|
|
the template class. */
|
|
template<size_t N>
|
|
std::string to_string (const string_mapping (&mapping)[N]) const;
|
|
|
|
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;
|
|
};
|
|
|
|
template <typename E>
|
|
using is_enum_flags_enum_type_t
|
|
= decltype (is_enum_flags_enum_type (std::declval<E *> ()));
|
|
|
|
/* Global operator overloads. */
|
|
|
|
/* Generate binary operators. */
|
|
|
|
#define ENUM_FLAGS_GEN_BINOP(OPERATOR_OP, OP) \
|
|
\
|
|
/* Raw enum on both LHS/RHS. Returns raw enum type. */ \
|
|
template <typename enum_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_type \
|
|
OPERATOR_OP (enum_type e1, enum_type e2) \
|
|
{ \
|
|
using underlying = typename enum_flags<enum_type>::underlying_type; \
|
|
return (enum_type) (underlying (e1) OP underlying (e2)); \
|
|
} \
|
|
\
|
|
/* enum_flags on the LHS. */ \
|
|
template <typename enum_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_flags<enum_type> \
|
|
OPERATOR_OP (enum_flags<enum_type> e1, enum_type e2) \
|
|
{ return e1.raw () OP e2; } \
|
|
\
|
|
/* enum_flags on the RHS. */ \
|
|
template <typename enum_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_flags<enum_type> \
|
|
OPERATOR_OP (enum_type e1, enum_flags<enum_type> e2) \
|
|
{ return e1 OP e2.raw (); } \
|
|
\
|
|
/* enum_flags on both LHS/RHS. */ \
|
|
template <typename enum_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_flags<enum_type> \
|
|
OPERATOR_OP (enum_flags<enum_type> e1, enum_flags<enum_type> e2) \
|
|
{ return e1.raw () OP e2.raw (); } \
|
|
\
|
|
/* Delete cases involving unrelated types. */ \
|
|
\
|
|
template <typename enum_type, typename unrelated_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_flags<enum_type> \
|
|
OPERATOR_OP (enum_type e1, unrelated_type e2) = delete; \
|
|
\
|
|
template <typename enum_type, typename unrelated_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_flags<enum_type> \
|
|
OPERATOR_OP (unrelated_type e1, enum_type e2) = delete; \
|
|
\
|
|
template <typename enum_type, typename unrelated_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_flags<enum_type> \
|
|
OPERATOR_OP (enum_flags<enum_type> e1, unrelated_type e2) = delete; \
|
|
\
|
|
template <typename enum_type, typename unrelated_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_flags<enum_type> \
|
|
OPERATOR_OP (unrelated_type e1, enum_flags<enum_type> e2) = delete;
|
|
|
|
/* Generate non-member compound assignment operators. Only the raw
|
|
enum versions are defined here. The enum_flags versions are
|
|
defined as member functions, simply because it's less code that
|
|
way.
|
|
|
|
Note we delete operators that would allow e.g.,
|
|
|
|
"enum_type | 1" or "enum_type1 | enum_type2"
|
|
|
|
because that would allow a mistake like :
|
|
enum flags1 { F1_FLAGS1 = 1 };
|
|
enum flags2 { F2_FLAGS2 = 2 };
|
|
enum flags1 val;
|
|
switch (val) {
|
|
case F1_FLAGS1 | F2_FLAGS2:
|
|
...
|
|
|
|
If you really need to 'or' enumerators of different flag types,
|
|
cast to integer first.
|
|
*/
|
|
#define ENUM_FLAGS_GEN_COMPOUND_ASSIGN(OPERATOR_OP, OP) \
|
|
/* lval reference version. */ \
|
|
template <typename enum_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_type & \
|
|
OPERATOR_OP (enum_type &e1, enum_type e2) \
|
|
{ return e1 = e1 OP e2; } \
|
|
\
|
|
/* rval reference version. */ \
|
|
template <typename enum_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
void \
|
|
OPERATOR_OP (enum_type &&e1, enum_type e2) = delete; \
|
|
\
|
|
/* Delete compound assignment from unrelated types. */ \
|
|
\
|
|
template <typename enum_type, typename other_enum_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
constexpr enum_type & \
|
|
OPERATOR_OP (enum_type &e1, other_enum_type e2) = delete; \
|
|
\
|
|
template <typename enum_type, typename other_enum_type, \
|
|
typename = is_enum_flags_enum_type_t<enum_type>> \
|
|
void \
|
|
OPERATOR_OP (enum_type &&e1, other_enum_type e2) = delete;
|
|
|
|
ENUM_FLAGS_GEN_BINOP (operator|, |)
|
|
ENUM_FLAGS_GEN_BINOP (operator&, &)
|
|
ENUM_FLAGS_GEN_BINOP (operator^, ^)
|
|
|
|
ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator|=, |)
|
|
ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator&=, &)
|
|
ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator^=, ^)
|
|
|
|
/* Allow comparison with enum_flags, raw enum, and integers, only.
|
|
The latter case allows "== 0". As side effect, it allows comparing
|
|
with integer variables too, but that's not a common mistake to
|
|
make. It's important to disable comparison with unrelated types to
|
|
prevent accidentally comparing with unrelated enum values, which
|
|
are convertible to integer, and thus coupled with enum_flags
|
|
conversion to underlying type too, would trigger the built-in 'bool
|
|
operator==(unsigned, int)' operator. */
|
|
|
|
#define ENUM_FLAGS_GEN_COMP(OPERATOR_OP, OP) \
|
|
\
|
|
/* enum_flags OP enum_flags */ \
|
|
\
|
|
template <typename enum_type> \
|
|
constexpr bool \
|
|
OPERATOR_OP (enum_flags<enum_type> lhs, enum_flags<enum_type> rhs) \
|
|
{ return lhs.raw () OP rhs.raw (); } \
|
|
\
|
|
/* enum_flags OP other */ \
|
|
\
|
|
template <typename enum_type> \
|
|
constexpr bool \
|
|
OPERATOR_OP (enum_flags<enum_type> lhs, enum_type rhs) \
|
|
{ return lhs.raw () OP rhs; } \
|
|
\
|
|
template <typename enum_type> \
|
|
constexpr bool \
|
|
OPERATOR_OP (enum_flags<enum_type> lhs, int rhs) \
|
|
{ return lhs.raw () OP rhs; } \
|
|
\
|
|
template <typename enum_type, typename U> \
|
|
constexpr bool \
|
|
OPERATOR_OP (enum_flags<enum_type> lhs, U rhs) = delete; \
|
|
\
|
|
/* other OP enum_flags */ \
|
|
\
|
|
template <typename enum_type> \
|
|
constexpr bool \
|
|
OPERATOR_OP (enum_type lhs, enum_flags<enum_type> rhs) \
|
|
{ return lhs OP rhs.raw (); } \
|
|
\
|
|
template <typename enum_type> \
|
|
constexpr bool \
|
|
OPERATOR_OP (int lhs, enum_flags<enum_type> rhs) \
|
|
{ return lhs OP rhs.raw (); } \
|
|
\
|
|
template <typename enum_type, typename U> \
|
|
constexpr bool \
|
|
OPERATOR_OP (U lhs, enum_flags<enum_type> rhs) = delete;
|
|
|
|
ENUM_FLAGS_GEN_COMP (operator==, ==)
|
|
ENUM_FLAGS_GEN_COMP (operator!=, !=)
|
|
|
|
/* Unary operators for the raw flags enum. */
|
|
|
|
/* We require underlying type to be unsigned when using operator~ --
|
|
if it were not unsigned, undefined behavior could result. However,
|
|
asserting this in the class itself would require too many
|
|
unnecessary changes to usages of otherwise OK enum types. */
|
|
template <typename enum_type,
|
|
typename = is_enum_flags_enum_type_t<enum_type>,
|
|
typename
|
|
= gdb::Requires<enum_flags_detail::EnumIsSafeForBitwiseComplement<enum_type>>>
|
|
constexpr enum_type
|
|
operator~ (enum_type e)
|
|
{
|
|
using underlying = typename enum_flags<enum_type>::underlying_type;
|
|
/* Cast to ULONGEST first, to prevent integer promotions from enums
|
|
with fixed underlying type std::uint8_t or std::uint16_t to
|
|
signed int. This ensures we apply the bitwise complement on an
|
|
unsigned type. */
|
|
return (enum_type)(underlying) ~ULONGEST (e);
|
|
}
|
|
|
|
template <typename enum_type,
|
|
typename = is_enum_flags_enum_type_t<enum_type>,
|
|
typename = gdb::Requires<enum_flags_detail::EnumIsUnsafeForBitwiseComplement<enum_type>>>
|
|
constexpr void operator~ (enum_type e) = delete;
|
|
|
|
template <typename enum_type,
|
|
typename = is_enum_flags_enum_type_t<enum_type>,
|
|
typename
|
|
= gdb::Requires<enum_flags_detail::EnumIsSafeForBitwiseComplement<enum_type>>>
|
|
constexpr enum_flags<enum_type>
|
|
operator~ (enum_flags<enum_type> e)
|
|
{
|
|
using underlying = typename enum_flags<enum_type>::underlying_type;
|
|
/* Cast to ULONGEST first, to prevent integer promotions from enums
|
|
with fixed underlying type std::uint8_t or std::uint16_t to
|
|
signed int. This ensures we apply the bitwise complement on an
|
|
unsigned type. */
|
|
return (enum_type)(underlying) ~ULONGEST (e);
|
|
}
|
|
|
|
template <typename enum_type,
|
|
typename = is_enum_flags_enum_type_t<enum_type>,
|
|
typename = gdb::Requires<enum_flags_detail::EnumIsUnsafeForBitwiseComplement<enum_type>>>
|
|
constexpr void operator~ (enum_flags<enum_type> e) = delete;
|
|
|
|
/* Delete operator<< and operator>>. */
|
|
|
|
template <typename enum_type, typename any_type,
|
|
typename = is_enum_flags_enum_type_t<enum_type>>
|
|
void operator<< (const enum_type &, const any_type &) = delete;
|
|
|
|
template <typename enum_type, typename any_type,
|
|
typename = is_enum_flags_enum_type_t<enum_type>>
|
|
void operator<< (const enum_flags<enum_type> &, const any_type &) = delete;
|
|
|
|
template <typename enum_type, typename any_type,
|
|
typename = is_enum_flags_enum_type_t<enum_type>>
|
|
void operator>> (const enum_type &, const any_type &) = delete;
|
|
|
|
template <typename enum_type, typename any_type,
|
|
typename = is_enum_flags_enum_type_t<enum_type>>
|
|
void operator>> (const enum_flags<enum_type> &, const any_type &) = delete;
|
|
|
|
template<typename E>
|
|
template<size_t N>
|
|
std::string
|
|
enum_flags<E>::to_string (const string_mapping (&mapping)[N]) const
|
|
{
|
|
enum_type flags = raw ();
|
|
std::string res = hex_string (flags);
|
|
res += " [";
|
|
|
|
bool need_space = false;
|
|
for (const auto &entry : mapping)
|
|
{
|
|
if ((flags & entry.flag) != 0)
|
|
{
|
|
/* Work with an unsigned version of the underlying type,
|
|
because if enum_type's underlying type is signed, op~
|
|
won't be defined for it, and, bitwise operations on
|
|
signed types are implementation defined. */
|
|
using uns = typename std::make_unsigned<underlying_type>::type;
|
|
flags &= (enum_type) ~(uns) entry.flag;
|
|
|
|
if (need_space)
|
|
res += " ";
|
|
res += entry.str;
|
|
|
|
need_space = true;
|
|
}
|
|
}
|
|
|
|
/* If there were flags not included in the mapping, print them as
|
|
a hex number. */
|
|
if (flags != 0)
|
|
{
|
|
if (need_space)
|
|
res += " ";
|
|
res += hex_string (flags);
|
|
}
|
|
|
|
res += "]";
|
|
|
|
return res;
|
|
}
|
|
|
|
#endif /* GDBSUPPORT_ENUM_FLAGS_H */
|