From 9c6a1327ad9a92b8584f0501dd25bf8ba9e84ac6 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Fri, 24 Apr 2020 13:40:31 -0600 Subject: [PATCH] Rewrite the existing variant part code 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-24 Tom Tromey * 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) : Remove. (struct main_type) : 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/ChangeLog b/gdb/ChangeLog index 305b133b4fb..962c997bc95 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,35 @@ +2020-04-24 Tom Tromey + + * 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) : Remove. + (struct main_type) : 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. + 2020-04-24 Tom Tromey * ada-lang.c (ada_discrete_type_high_bound, ada_discrete_type_low) diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index e89ed743543..dd808e08e21 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_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 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 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_parts; + /* Return the total number of fields (including baseclasses). */ int nfields () const { @@ -9116,37 +9146,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 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 (variants, n_variants); - return disc; + void *storage = obstack_alloc (obstack, sizeof (gdb::array_view)); + gdb::array_view *prop_value + = new (storage) gdb::array_view (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. @@ -9210,55 +9275,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) = "<>"; - 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) = "<>"; + 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) = "<>"; + /* 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. */ @@ -9268,31 +9322,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) = "<>"; - SET_FIELD_BITPOS (TYPE_FIELD (type, 0), 0); - - alloc_discriminant_info (union_type, -1, 0); } else { @@ -9333,33 +9369,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) = "<>"; - - /* Install the union in the outer struct type. */ - TYPE_FIELD_TYPE (type, 0) = union_type; - TYPE_FIELD_NAME (type, 0) = "<>"; - 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) = "<>"; /* We need a way to find the correct discriminant given a variant name. For convenience we build a map here. */ @@ -9375,9 +9398,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) { @@ -9385,25 +9412,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 (ranges, + n_fields - 1)); } } @@ -14202,6 +14236,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); @@ -14360,35 +14396,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 = "<>"; - - /* 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"); } @@ -14455,6 +14462,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 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 +convert_variant_range (struct obstack *obstack, const variant_field &variant, + bool is_unsigned) +{ + std::vector 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 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 (result, ranges.size ()); +} + +static const gdb::array_view create_variant_parts + (struct obstack *obstack, + const offset_map_type &offset_map, + struct field_info *fi, + const std::vector &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 (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 +create_variant_parts (struct obstack *obstack, + const offset_map_type &offset_map, + struct field_info *fi, + const std::vector &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 (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 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 *) + 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 @@ -14500,22 +14702,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) @@ -15085,11 +15273,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; @@ -15163,6 +15346,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 *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 *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 ¤t = fi->current_variant_part->variants.back (); + current.variant_parts.emplace_back (); + new_part = ¤t.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 *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. */ @@ -15173,8 +15480,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 @@ -15211,41 +15517,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 @@ -15262,39 +15537,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; @@ -15305,10 +15548,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 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 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 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 04cf22cced1..aa995e6eecd 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 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 (); -- 2.30.2