[RFA,v2,2/4] Initial support for variant parts

Message ID 20180222203018.23551-3-tom@tromey.com
State New, archived
Headers

Commit Message

Tom Tromey Feb. 22, 2018, 8:30 p.m. UTC
  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-22  Tom Tromey  <tom@tromey.com>

	* 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) <DYN_PROP_DISCRIMINATED>: New
	enumerator.
	(struct main_type) <flag_discriminated_union>: New field.
---
 gdb/ChangeLog  | 10 ++++++++++
 gdb/gdbtypes.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/valops.c   | 44 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/value.h    |  8 ++++++++
 4 files changed, 113 insertions(+)
  

Comments

Joel Brobecker Feb. 26, 2018, 6:49 a.m. UTC | #1
On Thu, Feb 22, 2018 at 01:30:16PM -0700, Tom Tromey wrote:
> 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-22  Tom Tromey  <tom@tromey.com>
> 
> 	* 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) <DYN_PROP_DISCRIMINATED>: New
> 	enumerator.
> 	(struct main_type) <flag_discriminated_union>: New field.

OK, with just one minor typo below...

> +  /* 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 correct despite the use of

correct -> correctly

Thanks!
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 734bbdf832..2b9d886b76 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,15 @@ 
 2018-02-22  Tom Tromey  <tom@tromey.com>
 
+	* 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) <DYN_PROP_DISCRIMINATED>: New
+	enumerator.
+	(struct main_type) <flag_discriminated_union>: New field.
+
+2018-02-22  Tom Tromey  <tom@tromey.com>
+
 	* 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 613257c47d..62cd8b6f82 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -304,6 +304,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.  */
 
@@ -373,6 +381,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.  */
@@ -431,6 +472,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.  */
@@ -650,6 +694,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 e038c04fd1..808a98beb7 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 correct 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 5676d245b9..0bc51304ea 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) */