AArch64: Add gdbserver MTE support
[deliverable/binutils-gdb.git] / gdbsupport / enum-flags.h
index 825ff4faf2c41e86da12a71ade5b2898c1b4f098..be4d2be6eaf25c8c650494e47f3681db741c2806 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015-2020 Free Software Foundation, Inc.
+/* Copyright (C) 2015-2021 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -18,6 +18,8 @@
 #ifndef COMMON_ENUM_FLAGS_H
 #define COMMON_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.
 
 
 #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
+/* 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)     \
   typedef enum_flags<enum_type> flags_type;            \
-  template<>                                           \
-  struct enum_flags_type<enum_type>                    \
-  {                                                    \
-    typedef enum_flags<enum_type> 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.  */
 
-/* Until we can rely on std::underlying type being universally
-   available (C++11), roll our own for enums.  */
+/* Note that std::underlying_type<enum_type> is not what we want here,
+   since that returns unsigned int even when the enum decays to signed
+   int.  */
 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; };
@@ -86,128 +96,324 @@ struct enum_underlying_type
     type;
 };
 
-template <typename E>
-class enum_flags
+namespace enum_flags_detail
 {
-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:
+/* Private type used to support initializing flag types with zero:
 
-       foo_flags f = 0;
+   foo_flags f = 0;
 
-     but not other integers:
+   but not other integers:
 
-       foo_flags f = 1;
+   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;
+   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;
-  }
+/* gdb::Requires trait helpers.  */
+template <typename enum_type>
+using EnumIsUnsigned
+  = std::is_unsigned<typename enum_underlying_type<enum_type>::type>;
+template <typename enum_type>
+using EnumIsSigned
+  = std::is_signed<typename enum_underlying_type<enum_type>::type>;
+
+}
+
+template <typename E>
+class enum_flags
+{
+public:
+  typedef E enum_type;
+  typedef typename enum_underlying_type<enum_type>::type underlying_type;
 
 public:
   /* Allow default construction.  */
-  enum_flags ()
+  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.  */
-  enum_flags (enum_type e)
+  constexpr enum_flags (enum_type e)
     : m_enum_value (e)
   {}
-  enum_flags (struct enum_flags::zero_type *zero)
+  constexpr enum_flags (enum_flags_detail::zero_type *zero)
     : m_enum_value ((enum_type) 0)
   {}
 
-  enum_flags &operator&= (enum_type e)
+  enum_flags &operator&= (enum_flags e) &
   {
-    m_enum_value = (enum_type) (underlying_value () & e);
+    m_enum_value = (enum_type) (m_enum_value & e.m_enum_value);
     return *this;
   }
-  enum_flags &operator|= (enum_type e)
+  enum_flags &operator|= (enum_flags e) &
   {
-    m_enum_value = (enum_type) (underlying_value () | e);
+    m_enum_value = (enum_type) (m_enum_value | e.m_enum_value);
     return *this;
   }
-  enum_flags &operator^= (enum_type e)
+  enum_flags &operator^= (enum_flags e) &
   {
-    m_enum_value = (enum_type) (underlying_value () ^ e);
+    m_enum_value = (enum_type) (m_enum_value ^ e.m_enum_value);
     return *this;
   }
 
-  operator enum_type () const
+  /* 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;
   }
 
-  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
+  /* Get the underlying value as a raw enum.  */
+  constexpr enum_type raw () const
   {
-    // We only the underlying type to be unsigned when actually 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 otherwise ok enum
-    // types.
-    gdb_static_assert (std::is_unsigned<underlying_type>::value);
-    return (enum_type) ~underlying_value ();
+    return m_enum_value;
   }
 
+  /* Binary operations involving some unrelated type (which would be a
+     bug) are implemented as non-members, and deleted.  */
+
 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.  */
 
-template <typename enum_type>
-typename enum_flags_type<enum_type>::type
-operator& (enum_type e1, enum_type e2)
+/* 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
+   convertion 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::EnumIsUnsigned<enum_type>>>
+constexpr enum_type
+operator~ (enum_type e)
 {
-  return enum_flags<enum_type> (e1) & e2;
+  using underlying = typename enum_flags<enum_type>::underlying_type;
+  return (enum_type) ~underlying (e);
 }
 
-template <typename enum_type>
-typename enum_flags_type<enum_type>::type
-operator| (enum_type e1, enum_type e2)
+template <typename enum_type,
+         typename = is_enum_flags_enum_type_t<enum_type>,
+         typename = gdb::Requires<enum_flags_detail::EnumIsSigned<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::EnumIsUnsigned<enum_type>>>
+constexpr enum_flags<enum_type>
+operator~ (enum_flags<enum_type> e)
 {
-  return enum_flags<enum_type> (e1) | e2;
+  using underlying = typename enum_flags<enum_type>::underlying_type;
+  return (enum_type) ~underlying (e);
 }
 
-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 = is_enum_flags_enum_type_t<enum_type>,
+         typename = gdb::Requires<enum_flags_detail::EnumIsSigned<enum_type>>>
+constexpr void operator~ (enum_flags<enum_type> e) = delete;
 
-template <typename enum_type>
-typename enum_flags_type<enum_type>::type
-operator~ (enum_type e)
-{
-  return ~enum_flags<enum_type> (e);
-}
+/* 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;
 
 #else /* __cplusplus */
 
This page took 0.02839 seconds and 4 git commands to generate.