From 7c22600aabfd10e190e98fff0b7c2d69cd191325 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Fri, 9 Feb 2018 13:31:42 -0700 Subject: Initial support for variant parts This adds some initial support for variant parts to gdbtypes.h. A variant part is represented as a union. The union has a flag indicating that it has a discriminant, and information about the discriminant is attached using the dynamic property system. 2018-02-26 Tom Tromey * value.h (value_union_variant): Declare. * valops.c (value_union_variant): New function. * gdbtypes.h (TYPE_FLAG_DISCRIMINATED_UNION): New macro. (struct discriminant_info): New. (enum dynamic_prop_node_kind) : New enumerator. (struct main_type) : New field. --- gdb/ChangeLog | 10 ++++++++++ gdb/gdbtypes.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/valops.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ gdb/value.h | 8 ++++++++ 4 files changed, 113 insertions(+) (limited to 'gdb') diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 0d01668..294afcf 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,15 @@ 2018-02-26 Tom Tromey + * value.h (value_union_variant): Declare. + * valops.c (value_union_variant): New function. + * gdbtypes.h (TYPE_FLAG_DISCRIMINATED_UNION): New macro. + (struct discriminant_info): New. + (enum dynamic_prop_node_kind) : New + enumerator. + (struct main_type) : New field. + +2018-02-26 Tom Tromey + * Makefile.in (SUBDIR_UNITTESTS_SRCS): Add unittests/unpack-selftests.c. * unittests/unpack-selftests.c: New file. diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index e18ae6e..552a2a2 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -312,6 +312,14 @@ DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags); #define TYPE_FLAG_ENUM(t) (TYPE_MAIN_TYPE (t)->flag_flag_enum) +/* * True if this type is a discriminated union type. Only valid for + TYPE_CODE_UNION. A discriminated union stores a reference to the + discriminant field along with the discriminator values in a dynamic + property. */ + +#define TYPE_FLAG_DISCRIMINATED_UNION(t) \ + (TYPE_MAIN_TYPE (t)->flag_discriminated_union) + /* * Constant type. If this is set, the corresponding type has a const modifier. */ @@ -381,6 +389,39 @@ DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags); #define TYPE_ADDRESS_CLASS_ALL(t) (TYPE_INSTANCE_FLAGS(t) \ & TYPE_INSTANCE_FLAG_ADDRESS_CLASS_ALL) +/* * Information needed for a discriminated union. A discriminated + union is handled somewhat differently from an ordinary union. + + One field is designated as the discriminant. Only one other field + is active at a time; which one depends on the value of the + discriminant and the data in this structure. + + Additionally, it is possible to have a univariant discriminated + union. In this case, the union has just a single field, which is + assumed to be the only active variant -- in this case no + discriminant is provided. */ + +struct discriminant_info +{ + /* * The index of the discriminant field. If -1, then this union + must have just a single field. */ + + int discriminant_index; + + /* * The index of the default branch of the union. If -1, then + there is no default branch. */ + + int default_index; + + /* * The discriminant values corresponding to each branch. This has + a number of entries equal to the number of fields in this union. + If discriminant_index is not -1, then that entry in this array is + not used. If default_index is not -1, then that entry in this + array is not used. */ + + ULONGEST discriminants[1]; +}; + enum dynamic_prop_kind { PROP_UNDEFINED, /* Not defined. */ @@ -439,6 +480,9 @@ enum dynamic_prop_node_kind /* A property providing an array's byte stride. */ DYN_PROP_BYTE_STRIDE, + + /* A property holding information about a discriminated union. */ + DYN_PROP_DISCRIMINATED, }; /* * List for dynamic type attributes. */ @@ -658,6 +702,13 @@ struct main_type unsigned int flag_flag_enum : 1; + /* * True if this type is a discriminated union type. Only valid + for TYPE_CODE_UNION. A discriminated union stores a reference to + the discriminant field along with the discriminator values in a + dynamic property. */ + + unsigned int flag_discriminated_union : 1; + /* * A discriminant telling us which field of the type_specific union is being used for this type, if any. */ diff --git a/gdb/valops.c b/gdb/valops.c index e038c04..bf028fd 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -2257,6 +2257,50 @@ value_struct_elt_bitpos (struct value **argp, int bitpos, struct type *ftype, return NULL; } +/* See value.h. */ + +int +value_union_variant (struct type *union_type, const gdb_byte *contents) +{ + gdb_assert (TYPE_CODE (union_type) == TYPE_CODE_UNION + && TYPE_FLAG_DISCRIMINATED_UNION (union_type)); + + struct dynamic_prop *discriminant_prop + = get_dyn_prop (DYN_PROP_DISCRIMINATED, union_type); + gdb_assert (discriminant_prop != nullptr); + + struct discriminant_info *info + = (struct discriminant_info *) discriminant_prop->data.baton; + gdb_assert (info != nullptr); + + /* If this is a univariant union, just return the sole field. */ + if (TYPE_NFIELDS (union_type) == 1) + return 0; + /* This should only happen for univariants, which we already dealt + with. */ + gdb_assert (info->discriminant_index != -1); + + /* Compute the discriminant. Note that unpack_field_as_long handles + sign extension when necessary, as does the DWARF reader -- so + signed discriminants will be handled correctly despite the use of + an unsigned type here. */ + ULONGEST discriminant = unpack_field_as_long (union_type, contents, + info->discriminant_index); + + for (int i = 0; i < TYPE_NFIELDS (union_type); ++i) + { + if (i != info->default_index + && i != info->discriminant_index + && discriminant == info->discriminants[i]) + return i; + } + + if (info->default_index == -1) + error (_("Could not find variant corresponding to discriminant %s"), + pulongest (discriminant)); + return info->default_index; +} + /* Search through the methods of an object (and its bases) to find a specified method. Return the pointer to the fn_field list FN_LIST of overloaded instances defined in the source language. If available diff --git a/gdb/value.h b/gdb/value.h index 5676d24..0bc5130 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -1169,4 +1169,12 @@ extern struct type *result_type_of_xmethod (struct value *method, extern struct value *call_xmethod (struct value *method, int argc, struct value **argv); +/* Given a discriminated union type and some corresponding value + contents, this will return the field index of the currently active + variant. This will throw an exception if no active variant can be + found. */ + +extern int value_union_variant (struct type *union_type, + const gdb_byte *contents); + #endif /* !defined (VALUE_H) */ -- cgit v1.1