forked from Imagelibrary/binutils-gdb
Compare commits
3 Commits
users/ibha
...
users/palv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32ef0ecca5 | ||
|
|
3b64e7030f | ||
|
|
d4049ba6cb |
@@ -435,6 +435,7 @@ SELFTESTS_SRCS = \
|
||||
unittests/command-def-selftests.c \
|
||||
unittests/common-utils-selftests.c \
|
||||
unittests/copy_bitwise-selftests.c \
|
||||
unittests/enum-flags-selftests.c \
|
||||
unittests/environ-selftests.c \
|
||||
unittests/filtered_iterator-selftests.c \
|
||||
unittests/format_pieces-selftests.c \
|
||||
|
||||
@@ -265,7 +265,7 @@ ftrace_new_function (struct btrace_thread_info *btinfo,
|
||||
static void
|
||||
ftrace_update_caller (struct btrace_function *bfun,
|
||||
struct btrace_function *caller,
|
||||
enum btrace_function_flag flags)
|
||||
btrace_function_flags flags)
|
||||
{
|
||||
if (bfun->up != 0)
|
||||
ftrace_debug (bfun, "updating caller");
|
||||
@@ -283,7 +283,7 @@ static void
|
||||
ftrace_fixup_caller (struct btrace_thread_info *btinfo,
|
||||
struct btrace_function *bfun,
|
||||
struct btrace_function *caller,
|
||||
enum btrace_function_flag flags)
|
||||
btrace_function_flags flags)
|
||||
{
|
||||
unsigned int prev, next;
|
||||
|
||||
|
||||
@@ -254,7 +254,8 @@ convert_qualified (compile_c_instance *context, struct type *type)
|
||||
if (TYPE_RESTRICT (type))
|
||||
quals |= GCC_QUALIFIER_RESTRICT;
|
||||
|
||||
return context->plugin ().build_qualified_type (unqual_converted, quals);
|
||||
return context->plugin ().build_qualified_type (unqual_converted,
|
||||
quals.raw ());
|
||||
}
|
||||
|
||||
/* Convert a complex type to its gcc representation. */
|
||||
|
||||
@@ -208,7 +208,7 @@ convert_one_symbol (compile_cplus_instance *instance,
|
||||
|
||||
/* Define the decl. */
|
||||
instance->plugin ().build_decl
|
||||
("variable", name.c_str (), kind, sym_type,
|
||||
("variable", name.c_str (), kind.raw (), sym_type,
|
||||
symbol_name.get (), addr, filename, line);
|
||||
|
||||
/* Pop scope for non-local symbols. */
|
||||
@@ -323,7 +323,7 @@ convert_symbol_bmsym (compile_cplus_instance *instance,
|
||||
sym_type = instance->convert_type (type);
|
||||
instance->plugin ().push_namespace ("");
|
||||
instance->plugin ().build_decl
|
||||
("minsym", msym->natural_name (), kind, sym_type, nullptr, addr,
|
||||
("minsym", msym->natural_name (), kind.raw (), sym_type, nullptr, addr,
|
||||
nullptr, 0);
|
||||
instance->plugin ().pop_binding_level ("");
|
||||
}
|
||||
|
||||
@@ -668,7 +668,7 @@ compile_cplus_convert_method (compile_cplus_instance *instance,
|
||||
type and corresponding qualifier flags. */
|
||||
gcc_type func_type = compile_cplus_convert_func (instance, method_type, true);
|
||||
gcc_type class_type = instance->convert_type (parent_type);
|
||||
gcc_cp_qualifiers_flags quals = (enum gcc_cp_qualifiers) 0;
|
||||
gcc_cp_qualifiers_flags quals = 0;
|
||||
|
||||
if (TYPE_CONST (method_type))
|
||||
quals |= GCC_CP_QUALIFIER_CONST;
|
||||
@@ -681,7 +681,7 @@ compile_cplus_convert_method (compile_cplus_instance *instance,
|
||||
gcc_cp_ref_qualifiers_flags rquals = GCC_CP_REF_QUAL_NONE;
|
||||
|
||||
return instance->plugin ().build_method_type
|
||||
(class_type, func_type, quals, rquals);
|
||||
(class_type, func_type, quals.raw (), rquals.raw ());
|
||||
}
|
||||
|
||||
/* Convert a member or method pointer represented by TYPE. */
|
||||
@@ -745,7 +745,7 @@ compile_cplus_convert_struct_or_union_methods (compile_cplus_instance *instance,
|
||||
(sym_kind
|
||||
| get_method_access_flag (type, i, j)
|
||||
| GCC_CP_FLAG_VIRTUAL_FUNCTION
|
||||
| GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION),
|
||||
| GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION).raw (),
|
||||
method_type, nullptr, 0, nullptr, 0);
|
||||
continue;
|
||||
}
|
||||
@@ -787,7 +787,7 @@ compile_cplus_convert_struct_or_union_methods (compile_cplus_instance *instance,
|
||||
|
||||
instance->plugin ().build_decl
|
||||
(kind, overloaded_name.get (),
|
||||
sym_kind | get_method_access_flag (type, i, j),
|
||||
(sym_kind | get_method_access_flag (type, i, j)).raw (),
|
||||
method_type, nullptr, address, filename, line);
|
||||
}
|
||||
}
|
||||
@@ -1060,7 +1060,7 @@ compile_cplus_instance::convert_qualified_base (gcc_type base,
|
||||
gcc_type result = base;
|
||||
|
||||
if (quals != 0)
|
||||
result = plugin ().build_qualified_type (base, quals);
|
||||
result = plugin ().build_qualified_type (base, quals.raw ());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -17292,10 +17292,9 @@ read_tag_pointer_type (struct die_info *die, struct dwarf2_cu *cu)
|
||||
{
|
||||
if (gdbarch_address_class_type_flags_p (gdbarch))
|
||||
{
|
||||
int type_flags;
|
||||
|
||||
type_flags = gdbarch_address_class_type_flags
|
||||
(gdbarch, byte_size, addr_class);
|
||||
type_instance_flags type_flags
|
||||
= gdbarch_address_class_type_flags (gdbarch, byte_size,
|
||||
addr_class);
|
||||
gdb_assert ((type_flags & ~TYPE_INSTANCE_FLAG_ADDRESS_CLASS_ALL)
|
||||
== 0);
|
||||
type = make_type_with_address_space (type, type_flags);
|
||||
|
||||
@@ -659,7 +659,7 @@ fake_method::fake_method (type_instance_flags flags,
|
||||
TYPE_LENGTH (type) = 1;
|
||||
type->set_code (TYPE_CODE_METHOD);
|
||||
TYPE_CHAIN (type) = type;
|
||||
TYPE_INSTANCE_FLAGS (type) = flags;
|
||||
SET_TYPE_INSTANCE_FLAGS (type, flags);
|
||||
if (num_types > 0)
|
||||
{
|
||||
if (param_types[num_types - 1] == NULL)
|
||||
|
||||
@@ -3501,7 +3501,7 @@ gdbarch_address_class_type_flags_p (struct gdbarch *gdbarch)
|
||||
return gdbarch->address_class_type_flags != NULL;
|
||||
}
|
||||
|
||||
int
|
||||
type_instance_flags
|
||||
gdbarch_address_class_type_flags (struct gdbarch *gdbarch, int byte_size, int dwarf2_addr_class)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
@@ -3566,8 +3566,8 @@ gdbarch_address_class_name_to_type_flags_p (struct gdbarch *gdbarch)
|
||||
return gdbarch->address_class_name_to_type_flags != NULL;
|
||||
}
|
||||
|
||||
int
|
||||
gdbarch_address_class_name_to_type_flags (struct gdbarch *gdbarch, const char *name, int *type_flags_ptr)
|
||||
bool
|
||||
gdbarch_address_class_name_to_type_flags (struct gdbarch *gdbarch, const char *name, type_instance_flags *type_flags_ptr)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
gdb_assert (gdbarch->address_class_name_to_type_flags != NULL);
|
||||
|
||||
@@ -848,8 +848,8 @@ extern void set_gdbarch_have_nonsteppable_watchpoint (struct gdbarch *gdbarch, i
|
||||
|
||||
extern int gdbarch_address_class_type_flags_p (struct gdbarch *gdbarch);
|
||||
|
||||
typedef int (gdbarch_address_class_type_flags_ftype) (int byte_size, int dwarf2_addr_class);
|
||||
extern int gdbarch_address_class_type_flags (struct gdbarch *gdbarch, int byte_size, int dwarf2_addr_class);
|
||||
typedef type_instance_flags (gdbarch_address_class_type_flags_ftype) (int byte_size, int dwarf2_addr_class);
|
||||
extern type_instance_flags gdbarch_address_class_type_flags (struct gdbarch *gdbarch, int byte_size, int dwarf2_addr_class);
|
||||
extern void set_gdbarch_address_class_type_flags (struct gdbarch *gdbarch, gdbarch_address_class_type_flags_ftype *address_class_type_flags);
|
||||
|
||||
extern int gdbarch_address_class_type_flags_to_name_p (struct gdbarch *gdbarch);
|
||||
@@ -866,13 +866,13 @@ extern bool gdbarch_execute_dwarf_cfa_vendor_op (struct gdbarch *gdbarch, gdb_by
|
||||
extern void set_gdbarch_execute_dwarf_cfa_vendor_op (struct gdbarch *gdbarch, gdbarch_execute_dwarf_cfa_vendor_op_ftype *execute_dwarf_cfa_vendor_op);
|
||||
|
||||
/* Return the appropriate type_flags for the supplied address class.
|
||||
This function should return 1 if the address class was recognized and
|
||||
type_flags was set, zero otherwise. */
|
||||
This function should return true if the address class was recognized and
|
||||
type_flags was set, false otherwise. */
|
||||
|
||||
extern int gdbarch_address_class_name_to_type_flags_p (struct gdbarch *gdbarch);
|
||||
|
||||
typedef int (gdbarch_address_class_name_to_type_flags_ftype) (struct gdbarch *gdbarch, const char *name, int *type_flags_ptr);
|
||||
extern int gdbarch_address_class_name_to_type_flags (struct gdbarch *gdbarch, const char *name, int *type_flags_ptr);
|
||||
typedef bool (gdbarch_address_class_name_to_type_flags_ftype) (struct gdbarch *gdbarch, const char *name, type_instance_flags *type_flags_ptr);
|
||||
extern bool gdbarch_address_class_name_to_type_flags (struct gdbarch *gdbarch, const char *name, type_instance_flags *type_flags_ptr);
|
||||
extern void set_gdbarch_address_class_name_to_type_flags (struct gdbarch *gdbarch, gdbarch_address_class_name_to_type_flags_ftype *address_class_name_to_type_flags);
|
||||
|
||||
/* Is a register in a group */
|
||||
|
||||
@@ -689,16 +689,16 @@ v;int;cannot_step_breakpoint;;;0;0;;0
|
||||
# See comment in target.h about continuable, steppable and
|
||||
# non-steppable watchpoints.
|
||||
v;int;have_nonsteppable_watchpoint;;;0;0;;0
|
||||
F;int;address_class_type_flags;int byte_size, int dwarf2_addr_class;byte_size, dwarf2_addr_class
|
||||
F;type_instance_flags;address_class_type_flags;int byte_size, int dwarf2_addr_class;byte_size, dwarf2_addr_class
|
||||
M;const char *;address_class_type_flags_to_name;int type_flags;type_flags
|
||||
# Execute vendor-specific DWARF Call Frame Instruction. OP is the instruction.
|
||||
# FS are passed from the generic execute_cfa_program function.
|
||||
m;bool;execute_dwarf_cfa_vendor_op;gdb_byte op, struct dwarf2_frame_state *fs;op, fs;;default_execute_dwarf_cfa_vendor_op;;0
|
||||
|
||||
# Return the appropriate type_flags for the supplied address class.
|
||||
# This function should return 1 if the address class was recognized and
|
||||
# type_flags was set, zero otherwise.
|
||||
M;int;address_class_name_to_type_flags;const char *name, int *type_flags_ptr;name, type_flags_ptr
|
||||
# This function should return true if the address class was recognized and
|
||||
# type_flags was set, false otherwise.
|
||||
M;bool;address_class_name_to_type_flags;const char *name, type_instance_flags *type_flags_ptr;name, type_flags_ptr
|
||||
# Is a register in a group
|
||||
m;int;register_reggroup_p;int regnum, struct reggroup *reggroup;regnum, reggroup;;default_register_reggroup_p;;0
|
||||
# Fetch the pointer to the ith function argument.
|
||||
|
||||
@@ -574,11 +574,11 @@ lookup_function_type_with_arguments (struct type *type,
|
||||
/* Identify address space identifier by name --
|
||||
return the integer flag defined in gdbtypes.h. */
|
||||
|
||||
int
|
||||
type_instance_flags
|
||||
address_space_name_to_int (struct gdbarch *gdbarch,
|
||||
const char *space_identifier)
|
||||
{
|
||||
int type_flags;
|
||||
type_instance_flags type_flags;
|
||||
|
||||
/* Check for known address space delimiters. */
|
||||
if (!strcmp (space_identifier, "code"))
|
||||
@@ -598,7 +598,8 @@ address_space_name_to_int (struct gdbarch *gdbarch,
|
||||
gdbtypes.h -- return the string version of the adress space name. */
|
||||
|
||||
const char *
|
||||
address_space_int_to_name (struct gdbarch *gdbarch, int space_flag)
|
||||
address_space_int_to_name (struct gdbarch *gdbarch,
|
||||
type_instance_flags space_flag)
|
||||
{
|
||||
if (space_flag & TYPE_INSTANCE_FLAG_CODE_SPACE)
|
||||
return "code";
|
||||
@@ -617,7 +618,7 @@ address_space_int_to_name (struct gdbarch *gdbarch, int space_flag)
|
||||
STORAGE must be in the same obstack as TYPE. */
|
||||
|
||||
static struct type *
|
||||
make_qualified_type (struct type *type, int new_flags,
|
||||
make_qualified_type (struct type *type, type_instance_flags new_flags,
|
||||
struct type *storage)
|
||||
{
|
||||
struct type *ntype;
|
||||
@@ -657,7 +658,7 @@ make_qualified_type (struct type *type, int new_flags,
|
||||
TYPE_CHAIN (type) = ntype;
|
||||
|
||||
/* Now set the instance flags and return the new type. */
|
||||
TYPE_INSTANCE_FLAGS (ntype) = new_flags;
|
||||
SET_TYPE_INSTANCE_FLAGS (ntype, new_flags);
|
||||
|
||||
/* Set length of new type to that of the original type. */
|
||||
TYPE_LENGTH (ntype) = TYPE_LENGTH (type);
|
||||
@@ -675,13 +676,14 @@ make_qualified_type (struct type *type, int new_flags,
|
||||
representations. */
|
||||
|
||||
struct type *
|
||||
make_type_with_address_space (struct type *type, int space_flag)
|
||||
make_type_with_address_space (struct type *type,
|
||||
type_instance_flags space_flag)
|
||||
{
|
||||
int new_flags = ((TYPE_INSTANCE_FLAGS (type)
|
||||
& ~(TYPE_INSTANCE_FLAG_CODE_SPACE
|
||||
| TYPE_INSTANCE_FLAG_DATA_SPACE
|
||||
| TYPE_INSTANCE_FLAG_ADDRESS_CLASS_ALL))
|
||||
| space_flag);
|
||||
type_instance_flags new_flags = ((TYPE_INSTANCE_FLAGS (type)
|
||||
& ~(TYPE_INSTANCE_FLAG_CODE_SPACE
|
||||
| TYPE_INSTANCE_FLAG_DATA_SPACE
|
||||
| TYPE_INSTANCE_FLAG_ADDRESS_CLASS_ALL))
|
||||
| space_flag);
|
||||
|
||||
return make_qualified_type (type, new_flags, NULL);
|
||||
}
|
||||
@@ -705,9 +707,9 @@ make_cv_type (int cnst, int voltl,
|
||||
{
|
||||
struct type *ntype; /* New type */
|
||||
|
||||
int new_flags = (TYPE_INSTANCE_FLAGS (type)
|
||||
& ~(TYPE_INSTANCE_FLAG_CONST
|
||||
| TYPE_INSTANCE_FLAG_VOLATILE));
|
||||
type_instance_flags new_flags = (TYPE_INSTANCE_FLAGS (type)
|
||||
& ~(TYPE_INSTANCE_FLAG_CONST
|
||||
| TYPE_INSTANCE_FLAG_VOLATILE));
|
||||
|
||||
if (cnst)
|
||||
new_flags |= TYPE_INSTANCE_FLAG_CONST;
|
||||
@@ -1410,7 +1412,6 @@ void
|
||||
make_vector_type (struct type *array_type)
|
||||
{
|
||||
struct type *inner_array, *elt_type;
|
||||
int flags;
|
||||
|
||||
/* Find the innermost array type, in case the array is
|
||||
multi-dimensional. */
|
||||
@@ -1421,7 +1422,8 @@ make_vector_type (struct type *array_type)
|
||||
elt_type = TYPE_TARGET_TYPE (inner_array);
|
||||
if (elt_type->code () == TYPE_CODE_INT)
|
||||
{
|
||||
flags = TYPE_INSTANCE_FLAGS (elt_type) | TYPE_INSTANCE_FLAG_NOTTEXT;
|
||||
type_instance_flags flags
|
||||
= TYPE_INSTANCE_FLAGS (elt_type) | TYPE_INSTANCE_FLAG_NOTTEXT;
|
||||
elt_type = make_qualified_type (elt_type, flags, NULL);
|
||||
TYPE_TARGET_TYPE (inner_array) = elt_type;
|
||||
}
|
||||
@@ -2732,12 +2734,13 @@ struct type *
|
||||
check_typedef (struct type *type)
|
||||
{
|
||||
struct type *orig_type = type;
|
||||
/* While we're removing typedefs, we don't want to lose qualifiers.
|
||||
E.g., const/volatile. */
|
||||
int instance_flags = TYPE_INSTANCE_FLAGS (type);
|
||||
|
||||
gdb_assert (type);
|
||||
|
||||
/* While we're removing typedefs, we don't want to lose qualifiers.
|
||||
E.g., const/volatile. */
|
||||
type_instance_flags instance_flags = TYPE_INSTANCE_FLAGS (type);
|
||||
|
||||
while (type->code () == TYPE_CODE_TYPEDEF)
|
||||
{
|
||||
if (!TYPE_TARGET_TYPE (type))
|
||||
@@ -2778,10 +2781,13 @@ check_typedef (struct type *type)
|
||||
outer cast in a chain of casting win), instead of assuming
|
||||
"it can't happen". */
|
||||
{
|
||||
const int ALL_SPACES = (TYPE_INSTANCE_FLAG_CODE_SPACE
|
||||
| TYPE_INSTANCE_FLAG_DATA_SPACE);
|
||||
const int ALL_CLASSES = TYPE_INSTANCE_FLAG_ADDRESS_CLASS_ALL;
|
||||
int new_instance_flags = TYPE_INSTANCE_FLAGS (type);
|
||||
const type_instance_flags ALL_SPACES
|
||||
= (TYPE_INSTANCE_FLAG_CODE_SPACE
|
||||
| TYPE_INSTANCE_FLAG_DATA_SPACE);
|
||||
const type_instance_flags ALL_CLASSES
|
||||
= TYPE_INSTANCE_FLAG_ADDRESS_CLASS_ALL;
|
||||
type_instance_flags new_instance_flags
|
||||
= TYPE_INSTANCE_FLAGS (type);
|
||||
|
||||
/* Treat code vs data spaces and address classes separately. */
|
||||
if ((instance_flags & ALL_SPACES) != 0)
|
||||
@@ -5026,7 +5032,7 @@ recursive_dump_type (struct type *type, int spaces)
|
||||
gdb_print_host_address (TYPE_CHAIN (type), gdb_stdout);
|
||||
printf_filtered ("\n");
|
||||
printfi_filtered (spaces, "instance_flags 0x%x",
|
||||
TYPE_INSTANCE_FLAGS (type));
|
||||
(unsigned) TYPE_INSTANCE_FLAGS (type));
|
||||
if (TYPE_CONST (type))
|
||||
{
|
||||
puts_filtered (" TYPE_CONST");
|
||||
@@ -5300,7 +5306,7 @@ copy_type_recursive (struct objfile *objfile,
|
||||
if (type->name ())
|
||||
new_type->set_name (xstrdup (type->name ()));
|
||||
|
||||
TYPE_INSTANCE_FLAGS (new_type) = TYPE_INSTANCE_FLAGS (type);
|
||||
SET_TYPE_INSTANCE_FLAGS (new_type, TYPE_INSTANCE_FLAGS (type));
|
||||
TYPE_LENGTH (new_type) = TYPE_LENGTH (type);
|
||||
|
||||
/* Copy the fields. */
|
||||
@@ -5427,7 +5433,7 @@ copy_type (const struct type *type)
|
||||
gdb_assert (TYPE_OBJFILE_OWNED (type));
|
||||
|
||||
new_type = alloc_type_copy (type);
|
||||
TYPE_INSTANCE_FLAGS (new_type) = TYPE_INSTANCE_FLAGS (type);
|
||||
SET_TYPE_INSTANCE_FLAGS (new_type, TYPE_INSTANCE_FLAGS (type));
|
||||
TYPE_LENGTH (new_type) = TYPE_LENGTH (type);
|
||||
memcpy (TYPE_MAIN_TYPE (new_type), TYPE_MAIN_TYPE (type),
|
||||
sizeof (struct main_type));
|
||||
|
||||
@@ -1585,7 +1585,10 @@ extern void allocate_gnat_aux_type (struct type *);
|
||||
TYPE_ZALLOC (type, \
|
||||
sizeof (*TYPE_MAIN_TYPE (type)->type_specific.func_stuff)))
|
||||
|
||||
#define TYPE_INSTANCE_FLAGS(thistype) (thistype)->instance_flags
|
||||
#define TYPE_INSTANCE_FLAGS(thistype) \
|
||||
type_instance_flags ((enum type_instance_flag_value) (thistype)->instance_flags)
|
||||
#define SET_TYPE_INSTANCE_FLAGS(thistype, flags) \
|
||||
(thistype)->instance_flags = flags
|
||||
#define TYPE_MAIN_TYPE(thistype) (thistype)->main_type
|
||||
#define TYPE_TARGET_TYPE(thistype) TYPE_MAIN_TYPE(thistype)->target_type
|
||||
#define TYPE_POINTER_TYPE(thistype) (thistype)->pointer_type
|
||||
@@ -2117,12 +2120,14 @@ extern struct type *make_atomic_type (struct type *);
|
||||
|
||||
extern void replace_type (struct type *, struct type *);
|
||||
|
||||
extern int address_space_name_to_int (struct gdbarch *, const char *);
|
||||
extern type_instance_flags address_space_name_to_int (struct gdbarch *,
|
||||
const char *);
|
||||
|
||||
extern const char *address_space_int_to_name (struct gdbarch *, int);
|
||||
extern const char *address_space_int_to_name (struct gdbarch *,
|
||||
type_instance_flags);
|
||||
|
||||
extern struct type *make_type_with_address_space (struct type *type,
|
||||
int space_identifier);
|
||||
extern struct type *make_type_with_address_space
|
||||
(struct type *type, type_instance_flags space_identifier);
|
||||
|
||||
extern struct type *lookup_memberptr_type (struct type *, struct type *);
|
||||
|
||||
|
||||
@@ -924,7 +924,7 @@ parse_string_or_char (const char *tokptr, const char **outptr,
|
||||
}
|
||||
++tokptr;
|
||||
|
||||
value->type = C_STRING | (quote == '\'' ? C_CHAR : 0); /*FIXME*/
|
||||
value->type = (int) C_STRING | (quote == '\'' ? C_CHAR : 0); /*FIXME*/
|
||||
value->ptr = (char *) obstack_base (&tempbuf);
|
||||
value->length = obstack_object_size (&tempbuf);
|
||||
|
||||
|
||||
@@ -1928,7 +1928,7 @@ record_btrace_target::get_tailcall_unwinder ()
|
||||
/* Return a human-readable string for FLAG. */
|
||||
|
||||
static const char *
|
||||
btrace_thread_flag_to_str (enum btrace_thread_flag flag)
|
||||
btrace_thread_flag_to_str (btrace_thread_flags flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
@@ -2221,7 +2221,7 @@ record_btrace_target::commit_resume ()
|
||||
static void
|
||||
record_btrace_cancel_resume (struct thread_info *tp)
|
||||
{
|
||||
enum btrace_thread_flag flags;
|
||||
btrace_thread_flags flags;
|
||||
|
||||
flags = tp->btrace.flags & (BTHR_MOVE | BTHR_STOP);
|
||||
if (flags == 0)
|
||||
@@ -2229,7 +2229,7 @@ record_btrace_cancel_resume (struct thread_info *tp)
|
||||
|
||||
DEBUG ("cancel resume thread %s (%s): %x (%s)",
|
||||
print_thread_id (tp),
|
||||
target_pid_to_str (tp->ptid).c_str (), flags,
|
||||
target_pid_to_str (tp->ptid).c_str (), flags.raw (),
|
||||
btrace_thread_flag_to_str (flags));
|
||||
|
||||
tp->btrace.flags &= ~(BTHR_MOVE | BTHR_STOP);
|
||||
@@ -2449,7 +2449,7 @@ record_btrace_step_thread (struct thread_info *tp)
|
||||
{
|
||||
struct btrace_thread_info *btinfo;
|
||||
struct target_waitstatus status;
|
||||
enum btrace_thread_flag flags;
|
||||
btrace_thread_flags flags;
|
||||
|
||||
btinfo = &tp->btrace;
|
||||
|
||||
@@ -2457,7 +2457,7 @@ record_btrace_step_thread (struct thread_info *tp)
|
||||
btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);
|
||||
|
||||
DEBUG ("stepping thread %s (%s): %x (%s)", print_thread_id (tp),
|
||||
target_pid_to_str (tp->ptid).c_str (), flags,
|
||||
target_pid_to_str (tp->ptid).c_str (), flags.raw (),
|
||||
btrace_thread_flag_to_str (flags));
|
||||
|
||||
/* We can't step without an execution history. */
|
||||
|
||||
@@ -4397,7 +4397,7 @@ cleanup_undefined_types_noname (struct objfile *objfile)
|
||||
and needs to be copied over from the reference type.
|
||||
Since replace_type expects them to be identical, we need
|
||||
to set these flags manually before hand. */
|
||||
TYPE_INSTANCE_FLAGS (nat.type) = TYPE_INSTANCE_FLAGS (*type);
|
||||
SET_TYPE_INSTANCE_FLAGS (nat.type, TYPE_INSTANCE_FLAGS (*type));
|
||||
replace_type (nat.type, *type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ type_stack::follow_types (struct type *follow_type)
|
||||
int done = 0;
|
||||
int make_const = 0;
|
||||
int make_volatile = 0;
|
||||
int make_addr_space = 0;
|
||||
type_instance_flags make_addr_space = 0;
|
||||
bool make_restrict = false;
|
||||
bool make_atomic = false;
|
||||
int array_size;
|
||||
@@ -128,7 +128,7 @@ type_stack::follow_types (struct type *follow_type)
|
||||
make_volatile = 1;
|
||||
break;
|
||||
case tp_space_identifier:
|
||||
make_addr_space = pop_int ();
|
||||
make_addr_space = (enum type_instance_flag_value) pop_int ();
|
||||
break;
|
||||
case tp_atomic:
|
||||
make_atomic = true;
|
||||
|
||||
586
gdb/unittests/enum-flags-selftests.c
Normal file
586
gdb/unittests/enum-flags-selftests.c
Normal file
@@ -0,0 +1,586 @@
|
||||
/* Self tests for enum-flags for GDB, the GNU debugger.
|
||||
|
||||
Copyright (C) 2016-2020 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 "gdbsupport/enum-flags.h"
|
||||
#include "gdbsupport/valid-expr.h"
|
||||
#include "gdbsupport/selftest.h"
|
||||
|
||||
namespace selftests {
|
||||
namespace enum_flags_tests {
|
||||
|
||||
/* The (real) enum types used in CHECK_VALID. Their names match the
|
||||
template parameter names of the templates defined by CHECK_VALID to
|
||||
make it simpler to use. They could be named differently. */
|
||||
|
||||
/* A "real enum". */
|
||||
enum RE
|
||||
{
|
||||
RE_FLAG1 = 1 << 1,
|
||||
RE_FLAG2 = 1 << 2,
|
||||
};
|
||||
|
||||
/* Another "real enum". */
|
||||
enum RE2
|
||||
{
|
||||
RE2_FLAG1 = 1 << 1,
|
||||
RE2_FLAG2 = 1 << 2,
|
||||
};
|
||||
|
||||
/* An unsigned "real enum". */
|
||||
enum URE : unsigned
|
||||
{
|
||||
URE_FLAG1 = 1 << 1,
|
||||
URE_FLAG2 = 1 << 2,
|
||||
URE_FLAG3 = 0xffffffff,
|
||||
};
|
||||
|
||||
/* A non-flags enum. */
|
||||
enum NF
|
||||
{
|
||||
NF_FLAG1 = 1 << 1,
|
||||
NF_FLAG2 = 1 << 2,
|
||||
};
|
||||
|
||||
/* The corresponding "enum flags" types. */
|
||||
DEF_ENUM_FLAGS_TYPE (RE, EF);
|
||||
DEF_ENUM_FLAGS_TYPE (RE2, EF2);
|
||||
DEF_ENUM_FLAGS_TYPE (URE, UEF);
|
||||
|
||||
#if HAVE_IS_TRIVIALLY_COPYABLE
|
||||
|
||||
/* So that std::vectors of types that have enum_flags fields can
|
||||
reallocate efficiently memcpy. */
|
||||
gdb_static_assert (std::is_trivially_copyable<EF>::value);
|
||||
|
||||
#endif
|
||||
|
||||
/* A couple globals used as lvalues in the CHECK_VALID expressions
|
||||
below. Their names (and types) match the uppercase type names
|
||||
exposed by CHECK_VALID just to make the expressions easier to
|
||||
follow. */
|
||||
static RE re ATTRIBUTE_UNUSED;
|
||||
static EF ef ATTRIBUTE_UNUSED;
|
||||
|
||||
/* First, compile-time tests that:
|
||||
|
||||
- make sure that incorrect operations with mismatching enum types
|
||||
are caught at compile time.
|
||||
|
||||
- make sure that the same operations but involving the right enum
|
||||
types do compile and that they return the correct type.
|
||||
*/
|
||||
|
||||
#define CHECK_VALID(VALID, EXPR_TYPE, EXPR) \
|
||||
CHECK_VALID_EXPR_6 (EF, RE, EF2, RE2, UEF, URE, VALID, EXPR_TYPE, EXPR)
|
||||
|
||||
typedef std::underlying_type<RE>::type und;
|
||||
|
||||
/* Test construction / conversion from/to different types. */
|
||||
|
||||
/* RE/EF -> underlying (explicit) */
|
||||
CHECK_VALID (true, und, und (RE ()))
|
||||
CHECK_VALID (true, und, und (EF ()))
|
||||
|
||||
/* RE/EF -> int (explicit) */
|
||||
CHECK_VALID (true, int, int (RE ()))
|
||||
CHECK_VALID (true, int, int (EF ()))
|
||||
|
||||
/* other -> RE */
|
||||
|
||||
/* You can construct a raw enum value from an int explicitly to punch
|
||||
a hole in the type system if need to. */
|
||||
CHECK_VALID (true, RE, RE (1))
|
||||
CHECK_VALID (true, RE, RE (RE2 ()))
|
||||
CHECK_VALID (false, void, RE (EF2 ()))
|
||||
CHECK_VALID (true, RE, RE (RE ()))
|
||||
CHECK_VALID (false, void, RE (EF ()))
|
||||
|
||||
/* other -> EF. */
|
||||
|
||||
/* As expected, enum-flags is a stronger type than the backing raw
|
||||
enum. Unlike with raw enums, you can't construct an enum flags
|
||||
from an integer nor from an unrelated enum type explicitly. Add an
|
||||
intermediate conversion via the raw enum if you really need it. */
|
||||
CHECK_VALID (false, void, EF (1))
|
||||
CHECK_VALID (false, void, EF (1u))
|
||||
CHECK_VALID (false, void, EF (RE2 ()))
|
||||
CHECK_VALID (false, void, EF (EF2 ()))
|
||||
CHECK_VALID (true, EF, EF (RE ()))
|
||||
CHECK_VALID (true, EF, EF (EF ()))
|
||||
|
||||
/* Test operators. */
|
||||
|
||||
/* operator OP (raw_enum, int) */
|
||||
|
||||
CHECK_VALID (false, void, RE () | 1)
|
||||
CHECK_VALID (false, void, RE () & 1)
|
||||
CHECK_VALID (false, void, RE () ^ 1)
|
||||
|
||||
/* operator OP (int, raw_enum) */
|
||||
|
||||
CHECK_VALID (false, void, 1 | RE ())
|
||||
CHECK_VALID (false, void, 1 & RE ())
|
||||
CHECK_VALID (false, void, 1 ^ RE ())
|
||||
|
||||
/* operator OP (enum_flags, int) */
|
||||
|
||||
CHECK_VALID (false, void, EF () | 1)
|
||||
CHECK_VALID (false, void, EF () & 1)
|
||||
CHECK_VALID (false, void, EF () ^ 1)
|
||||
|
||||
/* operator OP (int, enum_flags) */
|
||||
|
||||
CHECK_VALID (false, void, 1 | EF ())
|
||||
CHECK_VALID (false, void, 1 & EF ())
|
||||
CHECK_VALID (false, void, 1 ^ EF ())
|
||||
|
||||
/* operator OP (raw_enum, raw_enum) */
|
||||
|
||||
CHECK_VALID (false, void, RE () | RE2 ())
|
||||
CHECK_VALID (false, void, RE () & RE2 ())
|
||||
CHECK_VALID (false, void, RE () ^ RE2 ())
|
||||
CHECK_VALID (true, RE, RE () | RE ())
|
||||
CHECK_VALID (true, RE, RE () & RE ())
|
||||
CHECK_VALID (true, RE, RE () ^ RE ())
|
||||
|
||||
/* operator OP (enum_flags, raw_enum) */
|
||||
|
||||
CHECK_VALID (false, void, EF () | RE2 ())
|
||||
CHECK_VALID (false, void, EF () & RE2 ())
|
||||
CHECK_VALID (false, void, EF () ^ RE2 ())
|
||||
CHECK_VALID (true, EF, EF () | RE ())
|
||||
CHECK_VALID (true, EF, EF () & RE ())
|
||||
CHECK_VALID (true, EF, EF () ^ RE ())
|
||||
|
||||
/* operator OP= (raw_enum, raw_enum), rvalue ref on the lhs. */
|
||||
|
||||
CHECK_VALID (false, void, RE () |= RE2 ())
|
||||
CHECK_VALID (false, void, RE () &= RE2 ())
|
||||
CHECK_VALID (false, void, RE () ^= RE2 ())
|
||||
CHECK_VALID (true, RE&, RE () |= RE ())
|
||||
CHECK_VALID (true, RE&, RE () &= RE ())
|
||||
CHECK_VALID (true, RE&, RE () ^= RE ())
|
||||
|
||||
/* operator OP= (raw_enum, raw_enum), lvalue ref on the lhs. */
|
||||
|
||||
CHECK_VALID (false, void, re |= RE2 ())
|
||||
CHECK_VALID (false, void, re &= RE2 ())
|
||||
CHECK_VALID (false, void, re ^= RE2 ())
|
||||
CHECK_VALID (true, RE&, re |= RE ())
|
||||
CHECK_VALID (true, RE&, re &= RE ())
|
||||
CHECK_VALID (true, RE&, re ^= RE ())
|
||||
|
||||
/* operator OP= (enum_flags, raw_enum), rvalue ref on the lhs. */
|
||||
|
||||
CHECK_VALID (false, void, EF () |= RE2 ())
|
||||
CHECK_VALID (false, void, EF () &= RE2 ())
|
||||
CHECK_VALID (false, void, EF () ^= RE2 ())
|
||||
CHECK_VALID (true, EF&, EF () |= RE ())
|
||||
CHECK_VALID (true, EF&, EF () &= RE ())
|
||||
CHECK_VALID (true, EF&, EF () ^= RE ())
|
||||
|
||||
/* operator OP= (enum_flags, raw_enum), lvalue ref on the lhs. */
|
||||
|
||||
CHECK_VALID (false, void, ef |= RE2 ())
|
||||
CHECK_VALID (false, void, ef &= RE2 ())
|
||||
CHECK_VALID (false, void, ef ^= RE2 ())
|
||||
CHECK_VALID (true, EF&, ef |= EF ())
|
||||
CHECK_VALID (true, EF&, ef &= EF ())
|
||||
CHECK_VALID (true, EF&, ef ^= EF ())
|
||||
|
||||
/* operator OP= (enum_flags, enum_flags), rvalue ref on the lhs. */
|
||||
|
||||
CHECK_VALID (false, void, EF () |= EF2 ())
|
||||
CHECK_VALID (false, void, EF () &= EF2 ())
|
||||
CHECK_VALID (false, void, EF () ^= EF2 ())
|
||||
CHECK_VALID (true, EF&, EF () |= EF ())
|
||||
CHECK_VALID (true, EF&, EF () &= EF ())
|
||||
CHECK_VALID (true, EF&, EF () ^= EF ())
|
||||
|
||||
/* operator OP= (enum_flags, enum_flags), lvalue ref on the lhs. */
|
||||
|
||||
CHECK_VALID (false, void, ef |= EF2 ())
|
||||
CHECK_VALID (false, void, ef &= EF2 ())
|
||||
CHECK_VALID (false, void, ef ^= EF2 ())
|
||||
CHECK_VALID (true, EF&, ef |= EF ())
|
||||
CHECK_VALID (true, EF&, ef &= EF ())
|
||||
CHECK_VALID (true, EF&, ef ^= EF ())
|
||||
|
||||
/* operator~ (raw_enum) */
|
||||
|
||||
CHECK_VALID (false, void, ~RE ())
|
||||
CHECK_VALID (true, URE, ~URE ())
|
||||
|
||||
/* operator~ (enum_flags) */
|
||||
|
||||
CHECK_VALID (false, void, ~EF ())
|
||||
CHECK_VALID (true, UEF, ~UEF ())
|
||||
|
||||
/* Check ternary operator. This exercises implicit conversions. */
|
||||
|
||||
CHECK_VALID (true, EF, true ? EF () : RE ())
|
||||
CHECK_VALID (true, EF, true ? RE () : EF ())
|
||||
|
||||
/* These are valid, but it's not a big deal since you won't be able to
|
||||
assign the resulting integer to an enum or an enum_flags without a
|
||||
cast.
|
||||
|
||||
The latter two tests are disabled on older GCCs because they
|
||||
incorrectly fail with gcc 4.8 and 4.9 at least. Running the test
|
||||
outside a SFINAE context shows:
|
||||
|
||||
invalid user-defined conversion from ‘EF’ to ‘RE2’
|
||||
|
||||
They've been confirmed to compile/pass with gcc 5.3, gcc 7.1 and
|
||||
clang 3.7. */
|
||||
|
||||
CHECK_VALID (true, int, true ? EF () : EF2 ())
|
||||
CHECK_VALID (true, int, true ? EF2 () : EF ())
|
||||
#if GCC_VERSION >= 5003 || defined __clang__
|
||||
CHECK_VALID (true, int, true ? EF () : RE2 ())
|
||||
CHECK_VALID (true, int, true ? RE2 () : EF ())
|
||||
#endif
|
||||
|
||||
/* Same, but with an unsigned enum. */
|
||||
|
||||
typedef unsigned int uns;
|
||||
|
||||
CHECK_VALID (true, uns, true ? EF () : UEF ())
|
||||
CHECK_VALID (true, uns, true ? UEF () : EF ())
|
||||
#if GCC_VERSION >= 5003 || defined __clang__
|
||||
CHECK_VALID (true, uns, true ? EF () : URE ())
|
||||
CHECK_VALID (true, uns, true ? URE () : EF ())
|
||||
#endif
|
||||
|
||||
/* Unfortunately this can't work due to the way C++ computes the
|
||||
return type of the ternary conditional operator. int isn't
|
||||
implicitly convertible to the raw enum type, so the type of the
|
||||
expression is int. And then int is not implicitly convertible to
|
||||
enum_flags.
|
||||
|
||||
GCC 4.8 fails to compile this test with:
|
||||
error: operands to ?: have different types ‘enum_flags<RE>’ and ‘int’
|
||||
Confirmed to work with gcc 4.9, 5.3 and clang 3.7.
|
||||
*/
|
||||
#if GCC_VERSION >= 4009 || defined __clang__
|
||||
CHECK_VALID (false, void, true ? EF () : 0)
|
||||
CHECK_VALID (false, void, true ? 0 : EF ())
|
||||
#endif
|
||||
|
||||
/* Check that the ++/--/<</>>/<<=/>>= operators are deleted. */
|
||||
|
||||
CHECK_VALID (false, void, RE ()++)
|
||||
CHECK_VALID (false, void, ++RE ())
|
||||
CHECK_VALID (false, void, --RE ())
|
||||
CHECK_VALID (false, void, RE ()--)
|
||||
|
||||
CHECK_VALID (false, void, RE () << 1)
|
||||
CHECK_VALID (false, void, RE () >> 1)
|
||||
CHECK_VALID (false, void, EF () << 1)
|
||||
CHECK_VALID (false, void, EF () >> 1)
|
||||
|
||||
CHECK_VALID (false, void, RE () <<= 1)
|
||||
CHECK_VALID (false, void, RE () >>= 1)
|
||||
CHECK_VALID (false, void, EF () <<= 1)
|
||||
CHECK_VALID (false, void, EF () >>= 1)
|
||||
|
||||
/* Test comparison operators. */
|
||||
|
||||
CHECK_VALID (false, void, EF () == EF2 ())
|
||||
CHECK_VALID (false, void, EF () == RE2 ())
|
||||
CHECK_VALID (false, void, RE () == EF2 ())
|
||||
|
||||
CHECK_VALID (true, bool, EF (RE (1)) == EF (RE (1)))
|
||||
CHECK_VALID (true, bool, EF (RE (1)) == RE (1))
|
||||
CHECK_VALID (true, bool, RE (1) == EF (RE (1)))
|
||||
|
||||
CHECK_VALID (false, void, EF () != EF2 ())
|
||||
CHECK_VALID (false, void, EF () != RE2 ())
|
||||
CHECK_VALID (false, void, RE () != EF2 ())
|
||||
|
||||
/* On clang, disable -Wenum-compare due to "error: comparison of two
|
||||
values with different enumeration types [-Werror,-Wenum-compare]".
|
||||
clang doesn't suppress -Wenum-compare in SFINAE contexts. Not a
|
||||
big deal since misuses like these in GDB will be caught by -Werror
|
||||
anyway. This check is here mainly for completeness. */
|
||||
#if defined __clang__
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wenum-compare"
|
||||
#endif
|
||||
CHECK_VALID (true, bool, RE () == RE2 ())
|
||||
CHECK_VALID (true, bool, RE () != RE2 ())
|
||||
#if defined __clang__
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
CHECK_VALID (true, bool, EF (RE (1)) != EF (RE (2)))
|
||||
CHECK_VALID (true, bool, EF (RE (1)) != RE (2))
|
||||
CHECK_VALID (true, bool, RE (1) != EF (RE (2)))
|
||||
|
||||
CHECK_VALID (true, bool, EF () == 0)
|
||||
|
||||
/* Check we didn't disable/delete comparison between non-flags enums
|
||||
and unrelated types by mistake. */
|
||||
CHECK_VALID (true, bool, NF (1) == NF (1))
|
||||
CHECK_VALID (true, bool, NF (1) == int (1))
|
||||
CHECK_VALID (true, bool, NF (1) == char (1))
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/* Follows misc tests that exercise the API. Some are compile time,
|
||||
when possible, others are run time. */
|
||||
|
||||
enum test_flag
|
||||
{
|
||||
FLAG1 = 1 << 1,
|
||||
FLAG2 = 1 << 2,
|
||||
FLAG3 = 1 << 3,
|
||||
};
|
||||
|
||||
enum test_uflag : unsigned
|
||||
{
|
||||
UFLAG1 = 1 << 1,
|
||||
UFLAG2 = 1 << 2,
|
||||
UFLAG3 = 1 << 3,
|
||||
};
|
||||
|
||||
DEF_ENUM_FLAGS_TYPE (test_flag, test_flags);
|
||||
DEF_ENUM_FLAGS_TYPE (test_uflag, test_uflags);
|
||||
|
||||
static void
|
||||
self_test ()
|
||||
{
|
||||
/* Check that default construction works. */
|
||||
{
|
||||
constexpr test_flags f;
|
||||
|
||||
gdb_static_assert (f == 0);
|
||||
}
|
||||
|
||||
/* Check that assignment from zero works. */
|
||||
{
|
||||
test_flags f (FLAG1);
|
||||
|
||||
SELF_CHECK (f == FLAG1);
|
||||
|
||||
f = 0;
|
||||
|
||||
SELF_CHECK (f == 0);
|
||||
}
|
||||
|
||||
/* Check that construction from zero works. */
|
||||
{
|
||||
constexpr test_flags zero1 = 0;
|
||||
constexpr test_flags zero2 (0);
|
||||
constexpr test_flags zero3 {0};
|
||||
constexpr test_flags zero4 = {0};
|
||||
|
||||
gdb_static_assert (zero1 == 0);
|
||||
gdb_static_assert (zero2 == 0);
|
||||
gdb_static_assert (zero3 == 0);
|
||||
gdb_static_assert (zero4 == 0);
|
||||
}
|
||||
|
||||
/* Check construction from enum value. */
|
||||
{
|
||||
gdb_static_assert (test_flags (FLAG1) == FLAG1);
|
||||
gdb_static_assert (test_flags (FLAG2) != FLAG1);
|
||||
}
|
||||
|
||||
/* Check copy/assignment. */
|
||||
{
|
||||
constexpr test_flags src = FLAG1;
|
||||
|
||||
constexpr test_flags f1 = src;
|
||||
constexpr test_flags f2 (src);
|
||||
constexpr test_flags f3 {src};
|
||||
constexpr test_flags f4 = {src};
|
||||
|
||||
gdb_static_assert (f1 == FLAG1);
|
||||
gdb_static_assert (f2 == FLAG1);
|
||||
gdb_static_assert (f3 == FLAG1);
|
||||
gdb_static_assert (f4 == FLAG1);
|
||||
}
|
||||
|
||||
/* Check moving. */
|
||||
{
|
||||
test_flags src = FLAG1;
|
||||
test_flags dst = 0;
|
||||
|
||||
dst = std::move (src);
|
||||
SELF_CHECK (dst == FLAG1);
|
||||
}
|
||||
|
||||
/* Check construction from an 'or' of multiple bits. For this to
|
||||
work, operator| must be overridden to return an enum type. The
|
||||
builtin version would return int instead and then the conversion
|
||||
to test_flags would fail. */
|
||||
{
|
||||
constexpr test_flags f = FLAG1 | FLAG2;
|
||||
gdb_static_assert (f == (FLAG1 | FLAG2));
|
||||
}
|
||||
|
||||
/* Similarly, check that "FLAG1 | FLAG2" on the rhs of an assignment
|
||||
operator works. */
|
||||
{
|
||||
test_flags f = 0;
|
||||
f |= FLAG1 | FLAG2;
|
||||
SELF_CHECK (f == (FLAG1 | FLAG2));
|
||||
|
||||
f &= FLAG1 | FLAG2;
|
||||
SELF_CHECK (f == (FLAG1 | FLAG2));
|
||||
|
||||
f ^= FLAG1 | FLAG2;
|
||||
SELF_CHECK (f == 0);
|
||||
}
|
||||
|
||||
/* Check explicit conversion to int works. */
|
||||
{
|
||||
constexpr int some_bits (FLAG1 | FLAG2);
|
||||
|
||||
/* And comparison with int works too. */
|
||||
gdb_static_assert (some_bits == (FLAG1 | FLAG2));
|
||||
gdb_static_assert (some_bits == test_flags (FLAG1 | FLAG2));
|
||||
}
|
||||
|
||||
/* Check operator| and operator|=. Particularly interesting is
|
||||
making sure that putting the enum value on the lhs side of the
|
||||
expression works (FLAG | f). */
|
||||
{
|
||||
test_flags f = FLAG1;
|
||||
f |= FLAG2;
|
||||
SELF_CHECK (f == (FLAG1 | FLAG2));
|
||||
}
|
||||
{
|
||||
test_flags f = FLAG1;
|
||||
f = f | FLAG2;
|
||||
SELF_CHECK (f == (FLAG1 | FLAG2));
|
||||
}
|
||||
{
|
||||
test_flags f = FLAG1;
|
||||
f = FLAG2 | f;
|
||||
SELF_CHECK (f == (FLAG1 | FLAG2));
|
||||
}
|
||||
|
||||
/* Check the &/&= operators. */
|
||||
{
|
||||
test_flags f = FLAG1 & FLAG2;
|
||||
SELF_CHECK (f == 0);
|
||||
|
||||
f = FLAG1 | FLAG2;
|
||||
f &= FLAG2;
|
||||
SELF_CHECK (f == FLAG2);
|
||||
|
||||
f = FLAG1 | FLAG2;
|
||||
f = f & FLAG2;
|
||||
SELF_CHECK (f == FLAG2);
|
||||
|
||||
f = FLAG1 | FLAG2;
|
||||
f = FLAG2 & f;
|
||||
SELF_CHECK (f == FLAG2);
|
||||
}
|
||||
|
||||
/* Check the ^/^= operators. */
|
||||
{
|
||||
constexpr test_flags f = FLAG1 ^ FLAG2;
|
||||
gdb_static_assert (f == (FLAG1 ^ FLAG2));
|
||||
}
|
||||
|
||||
{
|
||||
test_flags f = FLAG1 ^ FLAG2;
|
||||
f ^= FLAG3;
|
||||
SELF_CHECK (f == (FLAG1 | FLAG2 | FLAG3));
|
||||
f = f ^ FLAG3;
|
||||
SELF_CHECK (f == (FLAG1 | FLAG2));
|
||||
f = FLAG3 ^ f;
|
||||
SELF_CHECK (f == (FLAG1 | FLAG2 | FLAG3));
|
||||
}
|
||||
|
||||
/* Check operator~. Note this only compiles with unsigned
|
||||
flags. */
|
||||
{
|
||||
constexpr test_uflags f1 = ~UFLAG1;
|
||||
constexpr test_uflags f2 = ~f1;
|
||||
gdb_static_assert (f2 == UFLAG1);
|
||||
}
|
||||
|
||||
/* Check the ternary operator. */
|
||||
|
||||
{
|
||||
/* raw enum, raw enum */
|
||||
constexpr test_flags f1 = true ? FLAG1 : FLAG2;
|
||||
gdb_static_assert (f1 == FLAG1);
|
||||
constexpr test_flags f2 = false ? FLAG1 : FLAG2;
|
||||
gdb_static_assert (f2 == FLAG2);
|
||||
}
|
||||
|
||||
{
|
||||
/* enum flags, raw enum */
|
||||
constexpr test_flags src = FLAG1;
|
||||
constexpr test_flags f1 = true ? src : FLAG2;
|
||||
gdb_static_assert (f1 == FLAG1);
|
||||
constexpr test_flags f2 = false ? src : FLAG2;
|
||||
gdb_static_assert (f2 == FLAG2);
|
||||
}
|
||||
|
||||
{
|
||||
/* enum flags, enum flags */
|
||||
constexpr test_flags src1 = FLAG1;
|
||||
constexpr test_flags src2 = FLAG2;
|
||||
constexpr test_flags f1 = true ? src1 : src2;
|
||||
gdb_static_assert (f1 == src1);
|
||||
constexpr test_flags f2 = false ? src1 : src2;
|
||||
gdb_static_assert (f2 == src2);
|
||||
}
|
||||
|
||||
/* Check that we can use flags in switch expressions (requires
|
||||
unambiguous conversion to integer). Also check that we can use
|
||||
operator| in switch cases, where only constants are allowed.
|
||||
This should work because operator| is constexpr. */
|
||||
{
|
||||
test_flags f = FLAG1 | FLAG2;
|
||||
bool ok = false;
|
||||
|
||||
switch (f)
|
||||
{
|
||||
case FLAG1:
|
||||
break;
|
||||
case FLAG2:
|
||||
break;
|
||||
case FLAG1 | FLAG2:
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
|
||||
SELF_CHECK (ok);
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace enum_flags_tests */
|
||||
} /* namespace selftests */
|
||||
|
||||
void _initialize_enum_flags_selftests ();
|
||||
|
||||
void
|
||||
_initialize_enum_flags_selftests ()
|
||||
{
|
||||
selftests::register_test ("enum-flags",
|
||||
selftests::enum_flags_tests::self_test);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -51,23 +53,31 @@
|
||||
|
||||
#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 *)
|
||||
|
||||
/* Until we can rely on std::underlying type being universally
|
||||
available (C++11), roll our own for enums. */
|
||||
/* 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. */
|
||||
|
||||
/* 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,6 +96,33 @@ struct enum_underlying_type
|
||||
type;
|
||||
};
|
||||
|
||||
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 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
|
||||
{
|
||||
@@ -93,122 +130,287 @@ 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 ()
|
||||
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
|
||||
/* 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
|
||||
/* Get the underlying value as a raw enum. */
|
||||
constexpr enum_type raw () 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
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
return enum_flags<enum_type> (e1) & e2;
|
||||
}
|
||||
/* Generate binary operators. */
|
||||
|
||||
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;
|
||||
}
|
||||
#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;
|
||||
|
||||
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;
|
||||
}
|
||||
/* 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.
|
||||
|
||||
template <typename enum_type>
|
||||
typename enum_flags_type<enum_type>::type
|
||||
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>> \
|
||||
constexpr enum_type & \
|
||||
OPERATOR_OP (enum_type &&e1, enum_type e2) \
|
||||
{ return e1 = e1 OP e2; } \
|
||||
\
|
||||
/* 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>> \
|
||||
constexpr enum_type & \
|
||||
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> (e);
|
||||
using underlying = typename enum_flags<enum_type>::underlying_type;
|
||||
return (enum_type) ~underlying (e);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
using underlying = typename enum_flags<enum_type>::underlying_type;
|
||||
return (enum_type) ~underlying (e);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* In C, the flags type is just a typedef for the enum type. */
|
||||
|
||||
@@ -52,6 +52,73 @@ struct make_void { typedef void type; };
|
||||
template<typename... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
/* Implementation of the detection idiom:
|
||||
|
||||
- http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf
|
||||
- http://en.cppreference.com/w/cpp/experimental/is_detected
|
||||
|
||||
*/
|
||||
|
||||
struct nonesuch
|
||||
{
|
||||
nonesuch () = delete;
|
||||
~nonesuch () = delete;
|
||||
nonesuch (const nonesuch &) = delete;
|
||||
void operator= (const nonesuch &) = delete;
|
||||
};
|
||||
|
||||
namespace detection_detail {
|
||||
/* Implementation of the detection idiom (negative case). */
|
||||
template<typename Default, typename AlwaysVoid,
|
||||
template<typename...> class Op, typename... Args>
|
||||
struct detector
|
||||
{
|
||||
using value_t = std::false_type;
|
||||
using type = Default;
|
||||
};
|
||||
|
||||
/* Implementation of the detection idiom (positive case). */
|
||||
template<typename Default, template<typename...> class Op, typename... Args>
|
||||
struct detector<Default, void_t<Op<Args...>>, Op, Args...>
|
||||
{
|
||||
using value_t = std::true_type;
|
||||
using type = Op<Args...>;
|
||||
};
|
||||
|
||||
/* Detect whether Op<Args...> is a valid type, use Default if not. */
|
||||
template<typename Default, template<typename...> class Op,
|
||||
typename... Args>
|
||||
using detected_or = detector<Default, void, Op, Args...>;
|
||||
|
||||
/* Op<Args...> if that is a valid type, otherwise Default. */
|
||||
template<typename Default, template<typename...> class Op,
|
||||
typename... Args>
|
||||
using detected_or_t
|
||||
= typename detected_or<Default, Op, Args...>::type;
|
||||
|
||||
} /* detection_detail */
|
||||
|
||||
template<template<typename...> class Op, typename... Args>
|
||||
using is_detected
|
||||
= typename detection_detail::detector<nonesuch, void, Op, Args...>::value_t;
|
||||
|
||||
template<template<typename...> class Op, typename... Args>
|
||||
using detected_t
|
||||
= typename detection_detail::detector<nonesuch, void, Op, Args...>::type;
|
||||
|
||||
template<typename Default, template<typename...> class Op, typename... Args>
|
||||
using detected_or = detection_detail::detected_or<Default, Op, Args...>;
|
||||
|
||||
template<typename Default, template<typename...> class Op, typename... Args>
|
||||
using detected_or_t = typename detected_or<Default, Op, Args...>::type;
|
||||
|
||||
template<typename Expected, template<typename...> class Op, typename... Args>
|
||||
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
|
||||
|
||||
template<typename To, template<typename...> class Op, typename... Args>
|
||||
using is_detected_convertible
|
||||
= std::is_convertible<detected_t<Op, Args...>, To>;
|
||||
|
||||
/* A few trait helpers, mainly stolen from libstdc++. Uppercase
|
||||
because "and/or", etc. are reserved keywords. */
|
||||
|
||||
|
||||
@@ -58,26 +58,12 @@
|
||||
#define CHECK_VALID_EXPR_INT(TYPENAMES, TYPES, VALID, EXPR_TYPE, EXPR) \
|
||||
namespace CONCAT (check_valid_expr, __LINE__) { \
|
||||
\
|
||||
template<typename, typename, typename = void> \
|
||||
struct is_valid_expression \
|
||||
: std::false_type {}; \
|
||||
\
|
||||
template <TYPENAMES> \
|
||||
struct is_valid_expression<TYPES, gdb::void_t<decltype (EXPR)>> \
|
||||
: std::true_type {}; \
|
||||
using archetype = decltype (EXPR); \
|
||||
\
|
||||
static_assert (is_valid_expression<TYPES>::value == VALID, \
|
||||
static_assert (gdb::is_detected_exact<EXPR_TYPE, \
|
||||
archetype, TYPES>::value == VALID, \
|
||||
""); \
|
||||
\
|
||||
template<TYPENAMES, typename = void> \
|
||||
struct is_same_type \
|
||||
: std::is_same<EXPR_TYPE, void> {}; \
|
||||
\
|
||||
template <TYPENAMES> \
|
||||
struct is_same_type<TYPES, gdb::void_t<decltype (EXPR)>> \
|
||||
: std::is_same<EXPR_TYPE, decltype (EXPR)> {}; \
|
||||
\
|
||||
static_assert (is_same_type<TYPES>::value, ""); \
|
||||
} /* namespace */
|
||||
|
||||
/* A few convenience macros that support expressions involving a
|
||||
@@ -105,4 +91,19 @@
|
||||
ESC_PARENS (T1, T2, T3, T4), \
|
||||
VALID, EXPR_TYPE, EXPR)
|
||||
|
||||
#define CHECK_VALID_EXPR_5(T1, T2, T3, T4, T5, VALID, EXPR_TYPE, EXPR) \
|
||||
CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1, typename T2, \
|
||||
typename T3, typename T4, \
|
||||
typename T5), \
|
||||
ESC_PARENS (T1, T2, T3, T4, T5), \
|
||||
VALID, EXPR_TYPE, EXPR)
|
||||
|
||||
#define CHECK_VALID_EXPR_6(T1, T2, T3, T4, T5, T6, \
|
||||
VALID, EXPR_TYPE, EXPR) \
|
||||
CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1, typename T2, \
|
||||
typename T3, typename T4, \
|
||||
typename T5, typename T6), \
|
||||
ESC_PARENS (T1, T2, T3, T4, T5, T6), \
|
||||
VALID, EXPR_TYPE, EXPR)
|
||||
|
||||
#endif /* COMMON_VALID_EXPR_H */
|
||||
|
||||
Reference in New Issue
Block a user