[05/11] Rewrite the existing variant part code

Message ID 20200408175452.30637-6-tromey@adacore.com
State New
Headers show
Series
  • Variant part support, plus more
Related show

Commit Message

Tom Tromey April 8, 2020, 5:54 p.m.
This rewrites the existing variant part code to follow the new model
implemented in the previous patch.  The old variant part code is
removed.

This only affects Rust for the moment.  I tested this using various
version of the Rust compiler, including one that emits old-style enum
debuginfo, exercising the quirks code.

gdb/ChangeLog
2020-04-08  Tom Tromey  <tromey@adacore.com>

	* dwarf2/read.c (struct variant_field): Rewrite.
	(struct variant_part_builder): New.
	(struct nextfield): Remove "variant" field.  Add "offset".
	(struct field_info): Add "current_variant_part" and
	"variant_parts".
	(alloc_discriminant_info): Remove.
	(alloc_rust_variant): New function.
	(quirk_rust_enum): Update.
	(dwarf2_add_field): Set "offset" member.  Don't handle
	DW_TAG_variant_part.
	(offset_map_type): New typedef.
	(convert_variant_range, create_one_variant)
	(create_one_variant_part, create_variant_parts)
	(add_variant_property): New functions.
	(dwarf2_attach_fields_to_type): Call add_variant_property.
	(read_structure_type): Don't handle DW_TAG_variant_part.
	(handle_variant_part, handle_variant): New functions.
	(handle_struct_member_die): Use them.
	(process_structure_scope): Don't handle variant parts.
	* gdbtypes.h (TYPE_FLAG_DISCRIMINATED_UNION): Remove.
	(struct discriminant_info): Remove.
	(enum dynamic_prop_node_kind) <DYN_PROP_DISCRIMINATED>: Remove.
	(struct main_type) <flag_discriminated_union>: Remove.
	* rust-lang.c (rust_enum_p, rust_empty_enum_p): Rewrite.
	(rust_enum_variant): Return int.  Remove "contents".  Rewrite.
	(rust_print_enum, rust_print_struct_def, rust_evaluate_subexp):
	Update.
	* valops.c (value_union_variant): Remove.
	* value.h (value_union_variant): Don't declare.
---
 gdb/ChangeLog     |  32 ++
 gdb/dwarf2/read.c | 723 ++++++++++++++++++++++++++++++----------------
 gdb/gdbtypes.h    |  51 ----
 gdb/rust-lang.c   | 115 +++-----
 gdb/valops.c      |  44 ---
 gdb/value.h       |   8 -
 6 files changed, 558 insertions(+), 415 deletions(-)

-- 
2.21.1

Patch

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index da702205c60..c0bced60384 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1082,29 +1082,52 @@  struct partial_die_info : public allocate_on_obstack
    and friends.  */
 static int bits_per_byte = 8;
 
-/* When reading a variant or variant part, we track a bit more
-   information about the field, and store it in an object of this
-   type.  */
+struct variant_part_builder;
+
+/* When reading a variant, we track a bit more information about the
+   field, and store it in an object of this type.  */
 
 struct variant_field
 {
-  /* If we see a DW_TAG_variant, then this will be the discriminant
-     value.  */
-  ULONGEST discriminant_value;
+  int first_field = -1;
+  int last_field = -1;
+
+  /* A variant can contain other variant parts.  */
+  std::vector<variant_part_builder> variant_parts;
+
   /* If we see a DW_TAG_variant, then this will be set if this is the
      default branch.  */
-  bool default_branch;
-  /* While reading a DW_TAG_variant_part, this will be set if this
-     field is the discriminant.  */
-  bool is_discriminant;
+  bool default_branch = false;
+  /* If we see a DW_AT_discr_value, then this will be the discriminant
+     value.  */
+  ULONGEST discriminant_value = 0;
+  /* If we see a DW_AT_discr_list, then this is a pointer to the list
+     data.  */
+  struct dwarf_block *discr_list_data = nullptr;
+};
+
+/* This represents a DW_TAG_variant_part.  */
+
+struct variant_part_builder
+{
+  /* The offset of the discriminant field.  */
+  sect_offset discriminant_offset {};
+
+  /* Variants that are direct children of this variant part.  */
+  std::vector<variant_field> variants;
+
+  /* True if we're currently reading a variant.  */
+  bool processing_variant = false;
 };
 
 struct nextfield
 {
   int accessibility = 0;
   int virtuality = 0;
-  /* Extra information to describe a variant or variant part.  */
-  struct variant_field variant {};
+  /* Variant parts need to find the discriminant, which is a DIE
+     reference.  We track the section offset of each field to make
+     this link.  */
+  sect_offset offset;
   struct field field {};
 };
 
@@ -1139,6 +1162,13 @@  struct field_info
        list.  */
     std::vector<struct decl_field> nested_types_list;
 
+    /* If non-null, this is the variant part we are currently
+       reading.  */
+    variant_part_builder *current_variant_part = nullptr;
+    /* This holds all the top-level variant parts attached to the type
+       we're reading.  */
+    std::vector<variant_part_builder> variant_parts;
+
     /* Return the total number of fields (including baseclasses).  */
     int nfields () const
     {
@@ -9073,37 +9103,72 @@  rust_fully_qualify (struct obstack *obstack, const char *p1, const char *p2)
   return obconcat (obstack, p1, "::", p2, (char *) NULL);
 }
 
-/* A helper that allocates a struct discriminant_info to attach to a
-   union type.  */
+/* A helper that allocates a variant part to attach to a Rust enum
+   type.  OBSTACK is where the results should be allocated.  TYPE is
+   the type we're processing.  DISCRIMINANT_INDEX is the index of the
+   discriminant.  It must be the index of one of the fields of TYPE.
+   DEFAULT_INDEX is the index of the default field; or -1 if there is
+   no default.  RANGES is indexed by "effective" field number (the
+   field index, but omitting the discriminant and default fields) and
+   must hold the discriminant values used by the variants.  Note that
+   RANGES must have a lifetime at least as long as OBSTACK -- either
+   already allocated on it, or static.  */
 
-static struct discriminant_info *
-alloc_discriminant_info (struct type *type, int discriminant_index,
-			 int default_index)
-{
-  gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION);
-  gdb_assert (discriminant_index == -1
-	      || (discriminant_index >= 0
-		  && discriminant_index < TYPE_NFIELDS (type)));
+static void
+alloc_rust_variant (struct obstack *obstack, struct type *type,
+		    int discriminant_index, int default_index,
+		    gdb::array_view<discriminant_range> ranges)
+{
+  /* When DISCRIMINANT_INDEX == -1, we have a univariant enum.  Those
+     must be handled by the caller.  */
+  gdb_assert (discriminant_index >= 0
+	      && discriminant_index < TYPE_NFIELDS (type));
   gdb_assert (default_index == -1
 	      || (default_index >= 0 && default_index < TYPE_NFIELDS (type)));
 
-  TYPE_FLAG_DISCRIMINATED_UNION (type) = 1;
+  /* We have one variant for each non-discriminant field.  */
+  int n_variants = TYPE_NFIELDS (type) - 1;
 
-  struct discriminant_info *disc
-    = ((struct discriminant_info *)
-       TYPE_ZALLOC (type,
-		    offsetof (struct discriminant_info, discriminants)
-		    + TYPE_NFIELDS (type) * sizeof (disc->discriminants[0])));
-  disc->default_index = default_index;
-  disc->discriminant_index = discriminant_index;
+  variant *variants = new (obstack) variant[n_variants];
+  int var_idx = 0;
+  int range_idx = 0;
+  for (int i = 0; i < TYPE_NFIELDS (type); ++i)
+    {
+      if (i == discriminant_index)
+	continue;
 
-  struct dynamic_prop prop;
-  prop.kind = PROP_UNDEFINED;
-  prop.data.baton = disc;
+      variants[var_idx].first_field = i;
+      variants[var_idx].last_field = i + 1;
+
+      /* The default field does not need a range, but other fields do.
+	 We skipped the discriminant above.  */
+      if (i != default_index)
+	{
+	  variants[var_idx].discriminants = ranges.slice (range_idx, 1);
+	  ++range_idx;
+	}
+
+      ++var_idx;
+    }
+
+  gdb_assert (range_idx == ranges.size ());
+  gdb_assert (var_idx == n_variants);
 
-  add_dyn_prop (DYN_PROP_DISCRIMINATED, prop, type);
+  variant_part *part = new (obstack) variant_part;
+  part->discriminant_index = discriminant_index;
+  part->is_unsigned = TYPE_UNSIGNED (TYPE_FIELD_TYPE (type,
+						      discriminant_index));
+  part->variants = gdb::array_view<variant> (variants, n_variants);
 
-  return disc;
+  void *storage = obstack_alloc (obstack, sizeof (gdb::array_view<variant_part>));
+  gdb::array_view<variant_part> *prop_value
+    = new (storage) gdb::array_view<variant_part> (part, 1);
+
+  struct dynamic_prop prop;
+  prop.kind = PROP_VARIANT_PARTS;
+  prop.data.variant_parts = prop_value;
+
+  add_dyn_prop (DYN_PROP_VARIANT_PARTS, prop, type);
 }
 
 /* Some versions of rustc emitted enums in an unusual way.
@@ -9167,55 +9232,44 @@  quirk_rust_enum (struct type *type, struct objfile *objfile)
 	  field_type = TYPE_FIELD_TYPE (field_type, index);
 	}
 
-      /* Make a union to hold the variants.  */
-      struct type *union_type = alloc_type (objfile);
-      TYPE_CODE (union_type) = TYPE_CODE_UNION;
-      TYPE_NFIELDS (union_type) = 3;
-      TYPE_FIELDS (union_type)
+      /* Smash this type to be a structure type.  We have to do this
+	 because the type has already been recorded.  */
+      TYPE_CODE (type) = TYPE_CODE_STRUCT;
+      TYPE_NFIELDS (type) = 3;
+      /* Save the field we care about.  */
+      struct field saved_field = TYPE_FIELD (type, 0);
+      TYPE_FIELDS (type)
 	= (struct field *) TYPE_ZALLOC (type, 3 * sizeof (struct field));
-      TYPE_LENGTH (union_type) = TYPE_LENGTH (type);
-      set_type_align (union_type, TYPE_RAW_ALIGN (type));
 
-      /* Put the discriminant must at index 0.  */
-      TYPE_FIELD_TYPE (union_type, 0) = field_type;
-      TYPE_FIELD_ARTIFICIAL (union_type, 0) = 1;
-      TYPE_FIELD_NAME (union_type, 0) = "<<discriminant>>";
-      SET_FIELD_BITPOS (TYPE_FIELD (union_type, 0), bit_offset);
+      /* Put the discriminant at index 0.  */
+      TYPE_FIELD_TYPE (type, 0) = field_type;
+      TYPE_FIELD_ARTIFICIAL (type, 0) = 1;
+      TYPE_FIELD_NAME (type, 0) = "<<discriminant>>";
+      SET_FIELD_BITPOS (TYPE_FIELD (type, 0), bit_offset);
 
       /* The order of fields doesn't really matter, so put the real
 	 field at index 1 and the data-less field at index 2.  */
-      struct discriminant_info *disc
-	= alloc_discriminant_info (union_type, 0, 1);
-      TYPE_FIELD (union_type, 1) = TYPE_FIELD (type, 0);
-      TYPE_FIELD_NAME (union_type, 1)
-	= rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (union_type, 1)));
-      TYPE_NAME (TYPE_FIELD_TYPE (union_type, 1))
+      TYPE_FIELD (type, 1) = saved_field;
+      TYPE_FIELD_NAME (type, 1)
+	= rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (type, 1)));
+      TYPE_NAME (TYPE_FIELD_TYPE (type, 1))
 	= rust_fully_qualify (&objfile->objfile_obstack, TYPE_NAME (type),
-			      TYPE_FIELD_NAME (union_type, 1));
+			      TYPE_FIELD_NAME (type, 1));
 
       const char *dataless_name
 	= rust_fully_qualify (&objfile->objfile_obstack, TYPE_NAME (type),
 			      name);
       struct type *dataless_type = init_type (objfile, TYPE_CODE_VOID, 0,
 					      dataless_name);
-      TYPE_FIELD_TYPE (union_type, 2) = dataless_type;
+      TYPE_FIELD_TYPE (type, 2) = dataless_type;
       /* NAME points into the original discriminant name, which
 	 already has the correct lifetime.  */
-      TYPE_FIELD_NAME (union_type, 2) = name;
-      SET_FIELD_BITPOS (TYPE_FIELD (union_type, 2), 0);
-      disc->discriminants[2] = 0;
-
-      /* Smash this type to be a structure type.  We have to do this
-	 because the type has already been recorded.  */
-      TYPE_CODE (type) = TYPE_CODE_STRUCT;
-      TYPE_NFIELDS (type) = 1;
-      TYPE_FIELDS (type)
-	= (struct field *) TYPE_ZALLOC (type, sizeof (struct field));
+      TYPE_FIELD_NAME (type, 2) = name;
+      SET_FIELD_BITPOS (TYPE_FIELD (type, 2), 0);
 
-      /* Install the variant part.  */
-      TYPE_FIELD_TYPE (type, 0) = union_type;
-      SET_FIELD_BITPOS (TYPE_FIELD (type, 0), 0);
-      TYPE_FIELD_NAME (type, 0) = "<<variants>>";
+      /* Indicate that this is a variant type.  */
+      static discriminant_range ranges[1] = { { 0, 0 } };
+      alloc_rust_variant (&objfile->objfile_obstack, type, 0, 1, ranges);
     }
   /* A union with a single anonymous field is probably an old-style
      univariant enum.  */
@@ -9225,31 +9279,13 @@  quirk_rust_enum (struct type *type, struct objfile *objfile)
 	 because the type has already been recorded.  */
       TYPE_CODE (type) = TYPE_CODE_STRUCT;
 
-      /* Make a union to hold the variants.  */
-      struct type *union_type = alloc_type (objfile);
-      TYPE_CODE (union_type) = TYPE_CODE_UNION;
-      TYPE_NFIELDS (union_type) = TYPE_NFIELDS (type);
-      TYPE_LENGTH (union_type) = TYPE_LENGTH (type);
-      set_type_align (union_type, TYPE_RAW_ALIGN (type));
-      TYPE_FIELDS (union_type) = TYPE_FIELDS (type);
-
-      struct type *field_type = TYPE_FIELD_TYPE (union_type, 0);
+      struct type *field_type = TYPE_FIELD_TYPE (type, 0);
       const char *variant_name
 	= rust_last_path_segment (TYPE_NAME (field_type));
-      TYPE_FIELD_NAME (union_type, 0) = variant_name;
+      TYPE_FIELD_NAME (type, 0) = variant_name;
       TYPE_NAME (field_type)
 	= rust_fully_qualify (&objfile->objfile_obstack,
 			      TYPE_NAME (type), variant_name);
-
-      /* Install the union in the outer struct type.  */
-      TYPE_NFIELDS (type) = 1;
-      TYPE_FIELDS (type)
-	= (struct field *) TYPE_ZALLOC (union_type, sizeof (struct field));
-      TYPE_FIELD_TYPE (type, 0) = union_type;
-      TYPE_FIELD_NAME (type, 0) = "<<variants>>";
-      SET_FIELD_BITPOS (TYPE_FIELD (type, 0), 0);
-
-      alloc_discriminant_info (union_type, -1, 0);
     }
   else
     {
@@ -9290,33 +9326,20 @@  quirk_rust_enum (struct type *type, struct objfile *objfile)
 	 because the type has already been recorded.  */
       TYPE_CODE (type) = TYPE_CODE_STRUCT;
 
-      /* Make a union to hold the variants.  */
+      /* Make space for the discriminant field.  */
       struct field *disr_field = &TYPE_FIELD (disr_type, 0);
-      struct type *union_type = alloc_type (objfile);
-      TYPE_CODE (union_type) = TYPE_CODE_UNION;
-      TYPE_NFIELDS (union_type) = 1 + TYPE_NFIELDS (type);
-      TYPE_LENGTH (union_type) = TYPE_LENGTH (type);
-      set_type_align (union_type, TYPE_RAW_ALIGN (type));
-      TYPE_FIELDS (union_type)
-	= (struct field *) TYPE_ZALLOC (union_type,
-					(TYPE_NFIELDS (union_type)
-					 * sizeof (struct field)));
-
-      memcpy (TYPE_FIELDS (union_type) + 1, TYPE_FIELDS (type),
+      field *new_fields
+	= (struct field *) TYPE_ZALLOC (type, (TYPE_NFIELDS (type)
+					       * sizeof (struct field)));
+      memcpy (new_fields + 1, TYPE_FIELDS (type),
 	      TYPE_NFIELDS (type) * sizeof (struct field));
+      TYPE_FIELDS (type) = new_fields;
+      TYPE_NFIELDS (type) = TYPE_NFIELDS (type) + 1;
 
       /* Install the discriminant at index 0 in the union.  */
-      TYPE_FIELD (union_type, 0) = *disr_field;
-      TYPE_FIELD_ARTIFICIAL (union_type, 0) = 1;
-      TYPE_FIELD_NAME (union_type, 0) = "<<discriminant>>";
-
-      /* Install the union in the outer struct type.  */
-      TYPE_FIELD_TYPE (type, 0) = union_type;
-      TYPE_FIELD_NAME (type, 0) = "<<variants>>";
-      TYPE_NFIELDS (type) = 1;
-
-      /* Set the size and offset of the union type.  */
-      SET_FIELD_BITPOS (TYPE_FIELD (type, 0), 0);
+      TYPE_FIELD (type, 0) = *disr_field;
+      TYPE_FIELD_ARTIFICIAL (type, 0) = 1;
+      TYPE_FIELD_NAME (type, 0) = "<<discriminant>>";
 
       /* We need a way to find the correct discriminant given a
 	 variant name.  For convenience we build a map here.  */
@@ -9332,9 +9355,13 @@  quirk_rust_enum (struct type *type, struct objfile *objfile)
 	    }
 	}
 
-      int n_fields = TYPE_NFIELDS (union_type);
-      struct discriminant_info *disc
-	= alloc_discriminant_info (union_type, 0, -1);
+      int n_fields = TYPE_NFIELDS (type);
+      /* We don't need a range entry for the discriminant, but we do
+	 need one for every other field, as there is no default
+	 variant.  */
+      discriminant_range *ranges = XOBNEWVEC (&objfile->objfile_obstack,
+					      discriminant_range,
+					      n_fields - 1);
       /* Skip the discriminant here.  */
       for (int i = 1; i < n_fields; ++i)
 	{
@@ -9342,25 +9369,32 @@  quirk_rust_enum (struct type *type, struct objfile *objfile)
 	     That name can be used to look up the correct
 	     discriminant.  */
 	  const char *variant_name
-	    = rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (union_type,
-								  i)));
+	    = rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (type, i)));
 
 	  auto iter = discriminant_map.find (variant_name);
 	  if (iter != discriminant_map.end ())
-	    disc->discriminants[i] = iter->second;
+	    {
+	      ranges[i].low = iter->second;
+	      ranges[i].high = iter->second;
+	    }
 
 	  /* Remove the discriminant field, if it exists.  */
-	  struct type *sub_type = TYPE_FIELD_TYPE (union_type, i);
+	  struct type *sub_type = TYPE_FIELD_TYPE (type, i);
 	  if (TYPE_NFIELDS (sub_type) > 0)
 	    {
 	      --TYPE_NFIELDS (sub_type);
 	      ++TYPE_FIELDS (sub_type);
 	    }
-	  TYPE_FIELD_NAME (union_type, i) = variant_name;
+	  TYPE_FIELD_NAME (type, i) = variant_name;
 	  TYPE_NAME (sub_type)
 	    = rust_fully_qualify (&objfile->objfile_obstack,
 				  TYPE_NAME (type), variant_name);
 	}
+
+      /* Indicate that this is a variant type.  */
+      alloc_rust_variant (&objfile->objfile_obstack, type, 0, 1,
+			  gdb::array_view<discriminant_range> (ranges,
+							       n_fields - 1));
     }
 }
 
@@ -14159,6 +14193,8 @@  dwarf2_add_field (struct field_info *fip, struct die_info *die,
       new_field = &fip->fields.back ();
     }
 
+  new_field->offset = die->sect_off;
+
   attr = dwarf2_attr (die, DW_AT_accessibility, cu);
   if (attr != nullptr)
     new_field->accessibility = DW_UNSND (attr);
@@ -14317,35 +14353,6 @@  dwarf2_add_field (struct field_info *fip, struct die_info *die,
       FIELD_TYPE (*fp) = die_type (die, cu);
       FIELD_NAME (*fp) = TYPE_NAME (fp->type);
     }
-  else if (die->tag == DW_TAG_variant_part)
-    {
-      /* process_structure_scope will treat this DIE as a union.  */
-      process_structure_scope (die, cu);
-
-      /* The variant part is relative to the start of the enclosing
-	 structure.  */
-      SET_FIELD_BITPOS (*fp, 0);
-      fp->type = get_die_type (die, cu);
-      fp->artificial = 1;
-      fp->name = "<<variant>>";
-
-      /* Normally a DW_TAG_variant_part won't have a size, but our
-	 representation requires one, so set it to the maximum of the
-	 child sizes, being sure to account for the offset at which
-	 each child is seen.  */
-      if (TYPE_LENGTH (fp->type) == 0)
-	{
-	  unsigned max = 0;
-	  for (int i = 0; i < TYPE_NFIELDS (fp->type); ++i)
-	    {
-	      unsigned len = ((TYPE_FIELD_BITPOS (fp->type, i) + 7) / 8
-			      + TYPE_LENGTH (TYPE_FIELD_TYPE (fp->type, i)));
-	      if (len > max)
-		max = len;
-	    }
-	  TYPE_LENGTH (fp->type) = max;
-	}
-    }
   else
     gdb_assert_not_reached ("missing case in dwarf2_add_field");
 }
@@ -14412,6 +14419,201 @@  dwarf2_add_type_defn (struct field_info *fip, struct die_info *die,
     fip->nested_types_list.push_back (fp);
 }
 
+/* A convenience typedef that's used when finding the discriminant
+   field for a variant part.  */
+typedef std::unordered_map<sect_offset, int> offset_map_type;
+
+/* Compute the discriminant range for a given variant.  OBSTACK is
+   where the results will be stored.  VARIANT is the variant to
+   process.  IS_UNSIGNED indicates whether the discriminant is signed
+   or unsigned.  */
+
+static const gdb::array_view<discriminant_range>
+convert_variant_range (struct obstack *obstack, const variant_field &variant,
+		       bool is_unsigned)
+{
+  std::vector<discriminant_range> ranges;
+
+  if (variant.default_branch)
+    return {};
+
+  if (variant.discr_list_data == nullptr)
+    {
+      discriminant_range r
+	= {variant.discriminant_value, variant.discriminant_value};
+      ranges.push_back (r);
+    }
+  else
+    {
+      gdb::array_view<const gdb_byte> data (variant.discr_list_data->data,
+					    variant.discr_list_data->size);
+      while (!data.empty ())
+	{
+	  if (data[0] != DW_DSC_range && data[0] != DW_DSC_label)
+	    {
+	      complaint (_("invalid discriminant marker: %d"), data[0]);
+	      break;
+	    }
+	  bool is_range = data[0] == DW_DSC_range;
+	  data = data.slice (1);
+
+	  ULONGEST low, high;
+	  unsigned int bytes_read;
+
+	  if (data.empty ())
+	    {
+	      complaint (_("DW_AT_discr_list missing low value"));
+	      break;
+	    }
+	  if (is_unsigned)
+	    low = read_unsigned_leb128 (nullptr, data.data (), &bytes_read);
+	  else
+	    low = (ULONGEST) read_signed_leb128 (nullptr, data.data (),
+						 &bytes_read);
+	  data = data.slice (bytes_read);
+
+	  if (is_range)
+	    {
+	      if (data.empty ())
+		{
+		  complaint (_("DW_AT_discr_list missing high value"));
+		  break;
+		}
+	      if (is_unsigned)
+		high = read_unsigned_leb128 (nullptr, data.data (),
+					     &bytes_read);
+	      else
+		high = (LONGEST) read_signed_leb128 (nullptr, data.data (),
+						     &bytes_read);
+	      data = data.slice (bytes_read);
+	    }
+	  else
+	    high = low;
+
+	  ranges.push_back ({ low, high });
+	}
+    }
+
+  discriminant_range *result = XOBNEWVEC (obstack, discriminant_range,
+					  ranges.size ());
+  std::copy (ranges.begin (), ranges.end (), result);
+  return gdb::array_view<discriminant_range> (result, ranges.size ());
+}
+
+static const gdb::array_view<variant_part> create_variant_parts
+  (struct obstack *obstack,
+   const offset_map_type &offset_map,
+   struct field_info *fi,
+   const std::vector<variant_part_builder> &variant_parts);
+
+/* Fill in a "struct variant" for a given variant field.  RESULT is
+   the variant to fill in.  OBSTACK is where any needed allocations
+   will be done.  OFFSET_MAP holds the mapping from section offsets to
+   fields for the type.  FI describes the fields of the type we're
+   processing.  FIELD is the variant field we're converting.  */
+
+static void
+create_one_variant (variant &result, struct obstack *obstack,
+		    const offset_map_type &offset_map,
+		    struct field_info *fi, const variant_field &field)
+{
+  result.discriminants = convert_variant_range (obstack, field, false);
+  result.first_field = field.first_field + fi->baseclasses.size ();
+  result.last_field = field.last_field + fi->baseclasses.size ();
+  result.parts = create_variant_parts (obstack, offset_map, fi,
+				       field.variant_parts);
+}
+
+/* Fill in a "struct variant_part" for a given variant part.  RESULT
+   is the variant part to fill in.  OBSTACK is where any needed
+   allocations will be done.  OFFSET_MAP holds the mapping from
+   section offsets to fields for the type.  FI describes the fields of
+   the type we're processing.  BUILDER is the variant part to be
+   converted.  */
+
+static void
+create_one_variant_part (variant_part &result,
+			 struct obstack *obstack,
+			 const offset_map_type &offset_map,
+			 struct field_info *fi,
+			 const variant_part_builder &builder)
+{
+  auto iter = offset_map.find (builder.discriminant_offset);
+  if (iter == offset_map.end ())
+    {
+      result.discriminant_index = -1;
+      /* Doesn't matter.  */
+      result.is_unsigned = false;
+    }
+  else
+    {
+      result.discriminant_index = iter->second;
+      result.is_unsigned
+	= TYPE_UNSIGNED (FIELD_TYPE
+			 (fi->fields[result.discriminant_index].field));
+    }
+
+  size_t n = builder.variants.size ();
+  variant *output = new (obstack) variant[n];
+  for (size_t i = 0; i < n; ++i)
+    create_one_variant (output[i], obstack, offset_map, fi,
+			builder.variants[i]);
+
+  result.variants = gdb::array_view<variant> (output, n);
+}
+
+/* Create a vector of variant parts that can be attached to a type.
+   OBSTACK is where any needed allocations will be done.  OFFSET_MAP
+   holds the mapping from section offsets to fields for the type.  FI
+   describes the fields of the type we're processing.  VARIANT_PARTS
+   is the vector to convert.  */
+
+static const gdb::array_view<variant_part>
+create_variant_parts (struct obstack *obstack,
+		      const offset_map_type &offset_map,
+		      struct field_info *fi,
+		      const std::vector<variant_part_builder> &variant_parts)
+{
+  if (variant_parts.empty ())
+    return {};
+
+  size_t n = variant_parts.size ();
+  variant_part *result = new (obstack) variant_part[n];
+  for (size_t i = 0; i < n; ++i)
+    create_one_variant_part (result[i], obstack, offset_map, fi,
+			     variant_parts[i]);
+
+  return gdb::array_view<variant_part> (result, n);
+}
+
+/* Compute the variant part vector for FIP, attaching it to TYPE when
+   done.  */
+
+static void
+add_variant_property (struct field_info *fip, struct type *type,
+		      struct dwarf2_cu *cu)
+{
+  /* Map section offsets of fields to their field index.  Note the
+     field index here does not take the number of baseclasses into
+     account.  */
+  offset_map_type offset_map;
+  for (int i = 0; i < fip->fields.size (); ++i)
+    offset_map[fip->fields[i].offset] = i;
+
+  struct objfile *objfile = cu->per_cu->dwarf2_per_objfile->objfile;
+  gdb::array_view<variant_part> parts
+    = create_variant_parts (&objfile->objfile_obstack, offset_map, fip,
+			    fip->variant_parts);
+
+  struct dynamic_prop prop;
+  prop.kind = PROP_VARIANT_PARTS;
+  prop.data.variant_parts
+    = ((gdb::array_view<variant_part> *)
+       obstack_copy (&objfile->objfile_obstack, &parts, sizeof (parts)));
+
+  add_dyn_prop (DYN_PROP_VARIANT_PARTS, prop, type);
+}
+
 /* Create the vector of fields, and attach it to the type.  */
 
 static void
@@ -14457,22 +14659,8 @@  dwarf2_attach_fields_to_type (struct field_info *fip, struct type *type,
       TYPE_N_BASECLASSES (type) = fip->baseclasses.size ();
     }
 
-  if (TYPE_FLAG_DISCRIMINATED_UNION (type))
-    {
-      struct discriminant_info *di = alloc_discriminant_info (type, -1, -1);
-
-      for (int index = 0; index < nfields; ++index)
-	{
-	  struct nextfield &field = fip->fields[index];
-
-	  if (field.variant.is_discriminant)
-	    di->discriminant_index = index;
-	  else if (field.variant.default_branch)
-	    di->default_index = index;
-	  else
-	    di->discriminants[index] = field.variant.discriminant_value;
-	}
-    }
+  if (!fip->variant_parts.empty ())
+    add_variant_property (fip, type, cu);
 
   /* Copy the saved-up fields into the field vector.  */
   for (int i = 0; i < nfields; ++i)
@@ -15042,11 +15230,6 @@  read_structure_type (struct die_info *die, struct dwarf2_cu *cu)
     {
       TYPE_CODE (type) = TYPE_CODE_UNION;
     }
-  else if (die->tag == DW_TAG_variant_part)
-    {
-      TYPE_CODE (type) = TYPE_CODE_UNION;
-      TYPE_FLAG_DISCRIMINATED_UNION (type) = 1;
-    }
   else
     {
       TYPE_CODE (type) = TYPE_CODE_STRUCT;
@@ -15120,6 +15303,130 @@  read_structure_type (struct die_info *die, struct dwarf2_cu *cu)
   return type;
 }
 
+static void handle_struct_member_die
+  (struct die_info *child_die,
+   struct type *type,
+   struct field_info *fi,
+   std::vector<struct symbol *> *template_args,
+   struct dwarf2_cu *cu);
+
+/* A helper for handle_struct_member_die that handles
+   DW_TAG_variant_part.  */
+
+static void
+handle_variant_part (struct die_info *die, struct type *type,
+		     struct field_info *fi,
+		     std::vector<struct symbol *> *template_args,
+		     struct dwarf2_cu *cu)
+{
+  variant_part_builder *new_part;
+  if (fi->current_variant_part == nullptr)
+    {
+      fi->variant_parts.emplace_back ();
+      new_part = &fi->variant_parts.back ();
+    }
+  else if (!fi->current_variant_part->processing_variant)
+    {
+      complaint (_("nested DW_TAG_variant_part seen "
+		   "- DIE at %s [in module %s]"),
+		 sect_offset_str (die->sect_off),
+		 objfile_name (cu->per_cu->dwarf2_per_objfile->objfile));
+      return;
+    }
+  else
+    {
+      variant_field &current = fi->current_variant_part->variants.back ();
+      current.variant_parts.emplace_back ();
+      new_part = &current.variant_parts.back ();
+    }
+
+  /* When we recurse, we want callees to add to this new variant
+     part.  */
+  scoped_restore save_current_variant_part
+    = make_scoped_restore (&fi->current_variant_part, new_part);
+
+  struct attribute *discr = dwarf2_attr (die, DW_AT_discr, cu);
+  if (discr == NULL)
+    {
+      /* It's a univariant form, an extension we support.  */
+    }
+  else if (discr->form_is_ref ())
+    {
+      struct dwarf2_cu *target_cu = cu;
+      struct die_info *target_die = follow_die_ref (die, discr, &target_cu);
+
+      new_part->discriminant_offset = target_die->sect_off;
+    }
+  else
+    {
+      complaint (_("DW_AT_discr does not have DIE reference form"
+		   " - DIE at %s [in module %s]"),
+		 sect_offset_str (die->sect_off),
+		 objfile_name (cu->per_cu->dwarf2_per_objfile->objfile));
+    }
+
+  for (die_info *child_die = die->child;
+       child_die != NULL;
+       child_die = child_die->sibling)
+    handle_struct_member_die (child_die, type, fi, template_args, cu);
+}
+
+/* A helper for handle_struct_member_die that handles
+   DW_TAG_variant.  */
+
+static void
+handle_variant (struct die_info *die, struct type *type,
+		struct field_info *fi,
+		std::vector<struct symbol *> *template_args,
+		struct dwarf2_cu *cu)
+{
+  if (fi->current_variant_part == nullptr)
+    {
+      complaint (_("saw DW_TAG_variant outside DW_TAG_variant_part "
+		   "- DIE at %s [in module %s]"),
+		 sect_offset_str (die->sect_off),
+		 objfile_name (cu->per_cu->dwarf2_per_objfile->objfile));
+      return;
+    }
+  if (fi->current_variant_part->processing_variant)
+    {
+      complaint (_("nested DW_TAG_variant seen "
+		   "- DIE at %s [in module %s]"),
+		 sect_offset_str (die->sect_off),
+		 objfile_name (cu->per_cu->dwarf2_per_objfile->objfile));
+      return;
+    }
+
+  scoped_restore save_processing_variant
+    = make_scoped_restore (&fi->current_variant_part->processing_variant,
+			   true);
+
+  fi->current_variant_part->variants.emplace_back ();
+  variant_field &variant = fi->current_variant_part->variants.back ();
+  variant.first_field = fi->fields.size ();
+
+  /* In a variant we want to get the discriminant and also add a
+     field for our sole member child.  */
+  struct attribute *discr = dwarf2_attr (die, DW_AT_discr_value, cu);
+  if (discr == nullptr)
+    {
+      discr = dwarf2_attr (die, DW_AT_discr_list, cu);
+      if (discr == nullptr || DW_BLOCK (discr)->size == 0)
+	variant.default_branch = true;
+      else
+	variant.discr_list_data = DW_BLOCK (discr);
+    }
+  else
+    variant.discriminant_value = DW_UNSND (discr);
+
+  for (die_info *variant_child = die->child;
+       variant_child != NULL;
+       variant_child = variant_child->sibling)
+    handle_struct_member_die (variant_child, type, fi, template_args, cu);
+
+  variant.last_field = fi->fields.size ();
+}
+
 /* A helper for process_structure_scope that handles a single member
    DIE.  */
 
@@ -15130,8 +15437,7 @@  handle_struct_member_die (struct die_info *child_die, struct type *type,
 			  struct dwarf2_cu *cu)
 {
   if (child_die->tag == DW_TAG_member
-      || child_die->tag == DW_TAG_variable
-      || child_die->tag == DW_TAG_variant_part)
+      || child_die->tag == DW_TAG_variable)
     {
       /* NOTE: carlton/2002-11-05: A C++ static data member
 	 should be a DW_TAG_member that is a declaration, but
@@ -15168,41 +15474,10 @@  handle_struct_member_die (struct die_info *child_die, struct type *type,
       if (arg != NULL)
 	template_args->push_back (arg);
     }
+  else if (child_die->tag == DW_TAG_variant_part)
+    handle_variant_part (child_die, type, fi, template_args, cu);
   else if (child_die->tag == DW_TAG_variant)
-    {
-      /* In a variant we want to get the discriminant and also add a
-	 field for our sole member child.  */
-      struct attribute *discr = dwarf2_attr (child_die, DW_AT_discr_value, cu);
-
-      for (die_info *variant_child = child_die->child;
-	   variant_child != NULL;
-	   variant_child = variant_child->sibling)
-	{
-	  if (variant_child->tag == DW_TAG_member)
-	    {
-	      handle_struct_member_die (variant_child, type, fi,
-					template_args, cu);
-	      /* Only handle the one.  */
-	      break;
-	    }
-	}
-
-      /* We don't handle this but we might as well report it if we see
-	 it.  */
-      if (dwarf2_attr (child_die, DW_AT_discr_list, cu) != nullptr)
-	  complaint (_("DW_AT_discr_list is not supported yet"
-		       " - DIE at %s [in module %s]"),
-		     sect_offset_str (child_die->sect_off),
-		     objfile_name (cu->per_cu->dwarf2_per_objfile->objfile));
-
-      /* The first field was just added, so we can stash the
-	 discriminant there.  */
-      gdb_assert (!fi->fields.empty ());
-      if (discr == NULL)
-	fi->fields.back ().variant.default_branch = true;
-      else
-	fi->fields.back ().variant.discriminant_value = DW_UNSND (discr);
-    }
+    handle_variant (child_die, type, fi, template_args, cu);
 }
 
 /* Finish creating a structure or union type, including filling in
@@ -15219,39 +15494,7 @@  process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
   if (type == NULL)
     type = read_structure_type (die, cu);
 
-  /* When reading a DW_TAG_variant_part, we need to notice when we
-     read the discriminant member, so we can record it later in the
-     discriminant_info.  */
-  bool is_variant_part = TYPE_FLAG_DISCRIMINATED_UNION (type);
-  sect_offset discr_offset {};
   bool has_template_parameters = false;
-
-  if (is_variant_part)
-    {
-      struct attribute *discr = dwarf2_attr (die, DW_AT_discr, cu);
-      if (discr == NULL)
-	{
-	  /* Maybe it's a univariant form, an extension we support.
-	     In this case arrange not to check the offset.  */
-	  is_variant_part = false;
-	}
-      else if (discr->form_is_ref ())
-	{
-	  struct dwarf2_cu *target_cu = cu;
-	  struct die_info *target_die = follow_die_ref (die, discr, &target_cu);
-
-	  discr_offset = target_die->sect_off;
-	}
-      else
-	{
-	  complaint (_("DW_AT_discr does not have DIE reference form"
-		       " - DIE at %s [in module %s]"),
-		     sect_offset_str (die->sect_off),
-		     objfile_name (cu->per_cu->dwarf2_per_objfile->objfile));
-	  is_variant_part = false;
-	}
-    }
-
   if (die->child != NULL && ! die_is_declaration (die, cu))
     {
       struct field_info fi;
@@ -15262,10 +15505,6 @@  process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
       while (child_die && child_die->tag)
 	{
 	  handle_struct_member_die (child_die, type, &fi, &template_args, cu);
-
-	  if (is_variant_part && discr_offset == child_die->sect_off)
-	    fi.fields.back ().variant.is_discriminant = true;
-
 	  child_die = child_die->sibling;
 	}
 
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index f686e5407ba..134515845f2 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -319,14 +319,6 @@  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.  */
 
@@ -482,39 +474,6 @@  struct variant_part : allocate_on_obstack
 };
 
 
-/* * 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.  */
@@ -591,9 +550,6 @@  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,
-
   /* A property holding variant parts.  */
   DYN_PROP_VARIANT_PARTS,
 };
@@ -831,13 +787,6 @@  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/rust-lang.c b/gdb/rust-lang.c
index 139e4c2f2ce..20661e48d96 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -68,38 +68,37 @@  rust_crate_for_block (const struct block *block)
    enum.  */
 
 static bool
-rust_enum_p (const struct type *type)
+rust_enum_p (struct type *type)
 {
-  return (TYPE_CODE (type) == TYPE_CODE_STRUCT
-	  && TYPE_NFIELDS (type) == 1
-	  && TYPE_FLAG_DISCRIMINATED_UNION (TYPE_FIELD_TYPE (type, 0)));
+  /* is_dynamic_type will return true if any field has a dynamic
+     attribute -- but we only want to check the top level.  */
+  return TYPE_HAS_VARIANT_PARTS (type);
 }
 
-/* Return true if TYPE, which must be an enum type, has no
-   variants.  */
+/* Return true if TYPE, which must be an already-resolved enum type,
+   has no variants.  */
 
 static bool
 rust_empty_enum_p (const struct type *type)
 {
-  gdb_assert (rust_enum_p (type));
-  /* In Rust the enum always fills the containing structure.  */
-  gdb_assert (TYPE_FIELD_BITPOS (type, 0) == 0);
-
-  return TYPE_NFIELDS (TYPE_FIELD_TYPE (type, 0)) == 0;
+  return TYPE_NFIELDS (type) == 0;
 }
 
-/* Given an enum type and contents, find which variant is active.  */
+/* Given an already-resolved enum type and contents, find which
+   variant is active.  */
 
-static struct field *
-rust_enum_variant (struct type *type, const gdb_byte *contents)
+static int
+rust_enum_variant (struct type *type)
 {
-  /* In Rust the enum always fills the containing structure.  */
-  gdb_assert (TYPE_FIELD_BITPOS (type, 0) == 0);
-
-  struct type *union_type = TYPE_FIELD_TYPE (type, 0);
+  /* The active variant is simply the first non-artificial field.  */
+  for (int i = 0; i < TYPE_NFIELDS (type); ++i)
+    if (!TYPE_FIELD_ARTIFICIAL (type, i))
+      return i;
 
-  int fieldno = value_union_variant (union_type, contents);
-  return &TYPE_FIELD (union_type, fieldno);
+  /* Perhaps we could get here by trying to print an Ada variant
+     record in Rust mode.  Unlikely, but an error is safer than an
+     assert.  */
+  error (_("Could not find active enum variant"));
 }
 
 /* See rust-lang.h.  */
@@ -471,6 +470,11 @@  rust_print_enum (struct value *val, struct ui_file *stream, int recurse,
 
   opts.deref_ref = 0;
 
+  gdb_assert (rust_enum_p (type));
+  gdb::array_view<const gdb_byte> view (value_contents_for_printing (val),
+					TYPE_LENGTH (value_type (val)));
+  type = resolve_dynamic_type (type, view, value_address (val));
+
   if (rust_empty_enum_p (type))
     {
       /* Print the enum type name here to be more clear.  */
@@ -480,9 +484,9 @@  rust_print_enum (struct value *val, struct ui_file *stream, int recurse,
       return;
     }
 
-  const gdb_byte *valaddr = value_contents_for_printing (val);
-  struct field *variant_field = rust_enum_variant (type, valaddr);
-  struct type *variant_type = FIELD_TYPE (*variant_field);
+  int variant_fieldno = rust_enum_variant (type);
+  val = value_field (val, variant_fieldno);
+  struct type *variant_type = TYPE_FIELD_TYPE (type, variant_fieldno);
 
   int nfields = TYPE_NFIELDS (variant_type);
 
@@ -505,10 +509,6 @@  rust_print_enum (struct value *val, struct ui_file *stream, int recurse,
       fprintf_filtered (stream, "{");
     }
 
-  struct value *union_value = value_field (val, 0);
-  int fieldno = (variant_field - &TYPE_FIELD (value_type (union_value), 0));
-  val = value_field (union_value, fieldno);
-
   bool first_field = true;
   for (int j = 0; j < TYPE_NFIELDS (variant_type); j++)
     {
@@ -698,8 +698,6 @@  rust_print_struct_def (struct type *type, const char *varstring,
   bool is_tuple = rust_tuple_type_p (type);
   bool is_enum = rust_enum_p (type);
 
-  int enum_discriminant_index = -1;
-
   if (for_rust_enum)
     {
       /* Already printing an outer enum, so nothing to print here.  */
@@ -710,25 +708,10 @@  rust_print_struct_def (struct type *type, const char *varstring,
       if (is_enum)
 	{
 	  fputs_filtered ("enum ", stream);
-
-	  if (rust_empty_enum_p (type))
-	    {
-	      if (tagname != NULL)
-		{
-		  fputs_filtered (tagname, stream);
-		  fputs_filtered (" ", stream);
-		}
-	      fputs_filtered ("{}", stream);
-	      return;
-	    }
-
-	  type = TYPE_FIELD_TYPE (type, 0);
-
-	  struct dynamic_prop *discriminant_prop
-	    = get_dyn_prop (DYN_PROP_DISCRIMINATED, type);
-	  struct discriminant_info *info
-	    = (struct discriminant_info *) discriminant_prop->data.baton;
-	  enum_discriminant_index = info->discriminant_index;
+	  struct dynamic_prop *prop = get_dyn_prop (DYN_PROP_VARIANT_PARTS,
+						    type);
+	  if (prop != nullptr && prop->kind == PROP_TYPE)
+	    type = prop->data.original_type;
 	}
       else if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
 	fputs_filtered ("struct ", stream);
@@ -755,7 +738,7 @@  rust_print_struct_def (struct type *type, const char *varstring,
     {
       if (field_is_static (&TYPE_FIELD (type, i)))
 	continue;
-      if (is_enum && i == enum_discriminant_index)
+      if (is_enum && TYPE_FIELD_ARTIFICIAL (type, i))
 	continue;
       fields.push_back (i);
     }
@@ -772,7 +755,7 @@  rust_print_struct_def (struct type *type, const char *varstring,
       QUIT;
 
       gdb_assert (!field_is_static (&TYPE_FIELD (type, i)));
-      gdb_assert (! (is_enum && i == enum_discriminant_index));
+      gdb_assert (! (is_enum && TYPE_FIELD_ARTIFICIAL (type, i)));
 
       if (flags->print_offsets)
 	podata->update (type, i, stream);
@@ -1679,20 +1662,16 @@  rust_evaluate_subexp (struct type *expect_type, struct expression *exp,
 
 	    if (rust_enum_p (type))
 	      {
+		gdb::array_view<const gdb_byte> view (value_contents (lhs),
+						      TYPE_LENGTH (type));
+		type = resolve_dynamic_type (type, view, value_address (lhs));
+
 		if (rust_empty_enum_p (type))
 		  error (_("Cannot access field %d of empty enum %s"),
 			 field_number, TYPE_NAME (type));
 
-		const gdb_byte *valaddr = value_contents (lhs);
-		struct field *variant_field = rust_enum_variant (type, valaddr);
-
-		struct value *union_value = value_primitive_field (lhs, 0, 0,
-								   type);
-
-		int fieldno = (variant_field
-			       - &TYPE_FIELD (value_type (union_value), 0));
-		lhs = value_primitive_field (union_value, 0, fieldno,
-					     value_type (union_value));
+		int fieldno = rust_enum_variant (type);
+		lhs = value_primitive_field (lhs, 0, fieldno, type);
 		outer_type = type;
 		type = value_type (lhs);
 	      }
@@ -1751,20 +1730,16 @@  tuple structs, and tuple-like enum variants"));
         type = value_type (lhs);
         if (TYPE_CODE (type) == TYPE_CODE_STRUCT && rust_enum_p (type))
 	  {
+	    gdb::array_view<const gdb_byte> view (value_contents (lhs),
+						  TYPE_LENGTH (type));
+	    type = resolve_dynamic_type (type, view, value_address (lhs));
+
 	    if (rust_empty_enum_p (type))
 	      error (_("Cannot access field %s of empty enum %s"),
 		     field_name, TYPE_NAME (type));
 
-	    const gdb_byte *valaddr = value_contents (lhs);
-	    struct field *variant_field = rust_enum_variant (type, valaddr);
-
-	    struct value *union_value = value_primitive_field (lhs, 0, 0,
-							       type);
-
-	    int fieldno = (variant_field
-			   - &TYPE_FIELD (value_type (union_value), 0));
-	    lhs = value_primitive_field (union_value, 0, fieldno,
-					 value_type (union_value));
+	    int fieldno = rust_enum_variant (type);
+	    lhs = value_primitive_field (lhs, 0, fieldno, type);
 
 	    struct type *outer_type = type;
 	    type = value_type (lhs);
diff --git a/gdb/valops.c b/gdb/valops.c
index 03c6482ee1e..33fe57562e4 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -2233,50 +2233,6 @@  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 a reference to the fn_field list METHODS of
    overloaded instances defined in the source language.  If available
diff --git a/gdb/value.h b/gdb/value.h
index dfe3e8e3ed3..ae859ca7224 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1226,14 +1226,6 @@  extern struct type *result_type_of_xmethod (struct value *method,
 extern struct value *call_xmethod (struct value *method,
 				   gdb::array_view<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);
-
 /* Destroy the values currently allocated.  This is called when GDB is
    exiting (e.g., on quit_force).  */
 extern void finalize_values ();