[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

Comments

Tom de Vries June 4, 2020, 8:13 a.m. | #1
On 08-04-2020 19:54, Tom Tromey wrote:
> 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.

> 


This causes regressions with target board readnow.

Before:
...
Running target readnow
  ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/union.exp ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/modules.exp ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/unsized.exp ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/simple.exp ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/watch.exp ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/traits.exp ...
FAIL: gdb.rust/traits.exp: print *td
FAIL: gdb.rust/traits.exp: print *tu
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/expr.exp ...
Running
/data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/rust-style.exp ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/methods.exp ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/generics.exp ...
FAIL: gdb.rust/generics.exp: print identity::<u32>(23u32)
FAIL: gdb.rust/generics.exp: ptype identity::<u32>(23u32)
FAIL: gdb.rust/generics.exp: print identity::<f64>(23)
FAIL: gdb.rust/generics.exp: ptype identity::<f64>(23)
FAIL: gdb.rust/generics.exp: print identity::< Hold<i32> >(e)
FAIL: gdb.rust/generics.exp: print identity::<generics::Hold<i32> >(e)
FAIL: gdb.rust/generics.exp: print identity::<Hold<i32>>(e)

                === gdb Summary ===

# of expected passes            289
# of unexpected failures        9
# of expected failures          5
...

After:
...
Running target readnow
  ...
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/union.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/union/union into
/data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/modules.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/modules/modules
into /data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/unsized.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/unsized/unsized
into /data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/simple.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/simple/simple
into /data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/watch.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/watch/watch into
/data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/traits.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/traits/traits
into /data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/expr.exp ...
Running
/data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/rust-style.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/rust-style/rust-style
into /data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/methods.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/methods/methods
into /data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open
Running /data/gdb_versions/devel/src/gdb/testsuite/gdb.rust/generics.exp ...
ERROR: Couldn't load
/data/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.rust/generics/generics
into /data/gdb_versions/devel/build/gdb/testsuite/../../gdb/gdb (eof).
ERROR: Couldn't send delete breakpoints to GDB.
ERROR: breakpoints not deleted
ERROR: : spawn id exp9 not open
    while executing
"expect {
-i exp9 -timeout 30
        -re "$break_message \[0-9\]* at .*: file .*, line
$decimal.\r\n$gdb_prompt $" {}
        -re "$break_message \[0-9\]*: file .*..."
    ("uplevel" body line 1)
    invoked from within
"uplevel $body" NONE : spawn id exp9 not open

                === gdb Summary ===

# of expected passes            95
# of unresolved testcases       18
...

Thanks,
- Tom

> 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(-)

> 

> 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 ();

>
Tom Tromey June 15, 2020, 5:30 p.m. | #2
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:


Tom> On 08-04-2020 19:54, Tom Tromey wrote:
>> 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.


Tom> This causes regressions with target board readnow.

I finally looked at this, but I can't reproduce it.
Which version of the Rust compiler are you using?
Perhaps that makes a difference.

thanks,
Tom
Tom de Vries June 17, 2020, 4:11 p.m. | #3
On 6/15/20 7:30 PM, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:

> 

> Tom> On 08-04-2020 19:54, Tom Tromey wrote:

>>> 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.

> 

> Tom> This causes regressions with target board readnow.

> 

> I finally looked at this, but I can't reproduce it.

> Which version of the Rust compiler are you using?

> Perhaps that makes a difference.


I just did a clean reinstall of my laptop, which also ended up being an
os update (openSUSE Leap 15.1 -> 15.2 beta). So I may no longer have the
configuration to reproduce this.

I was using rustc 1.36.0, with system llvm 7.

After the reinstall, I have rustc 1.36.0 with system llvm 9.

The rust compiler tests for llvm version and does things differently in
consequence.

[ And to complicate things, the llvm version also changes with rust
compiler version:
...
$ git diff 1.32.0 1.33.0 -- librustc_codegen_llvm/debuginfo/metadata.rs
@@ -1173,7 +1164,10 @@ fn use_enum_fallback(cx: &CodegenCx) -> bool {
     // On MSVC we have to use the fallback mode, because LLVM doesn't
     // lower variant parts to PDB.
     return cx.sess().target.target.options.is_like_msvc
-        || llvm_util::get_major_version() < 7;
+        // LLVM version 7 did not release with an important bug fix;
+        // but the required patch is in the LLVM 8.  Rust LLVM reports
+        // 8 as well.
+        || llvm_util::get_major_version() < 8;
 }

 // Describes the members of an enum value: An enum is described as a
union of
... ]

So, it may be a case of having to use 1.36.0 and an older llvm version
to trigger this.

Thanks,
- Tom
Tom Tromey June 17, 2020, 4:45 p.m. | #4
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:


Tom> So, it may be a case of having to use 1.36.0 and an older llvm version
Tom> to trigger this.

Ok.  I installed 1.36.0 via rustup, but that is using LLVM 8.
So, can you just send me one of the executables from your test run?
Then I can debug using that.

thanks,
Tom
Tom Tromey June 19, 2020, 8:51 p.m. | #5
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:


Tom> That seems sufficient to recreate the assert:
Tom> ...
Tom> $ gdb -batch outputs/gdb.rust/union/union -ex "break union.rs:34"
Tom> warning: Missing auto-load script at offset 0 in section
Tom> .debug_gdb_scripts of file union.
Tom> Use `info auto-load python-scripts [REGEXP]' to list them.
Tom> src/gdb/dwarf2/read.c:9446: internal-error: void
Tom> alloc_rust_variant(obstack*, type*, int, int,
Tom> gdb::array_view<discriminant_range>): Assertion `range_idx ==
Tom> ranges.size ()' failed.
Tom> ...

Tom> Exec attached.

Thanks.  Can you try this patch:

https://sourceware.org/pipermail/gdb-patches/2020-June/169744.html

Tom

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 ();