diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 305b133b4f..962c997bc9 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 e89ed74354..dd808e08e2 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) +static void +alloc_rust_variant (struct obstack *obstack, struct type *type, + int discriminant_index, int default_index, + gdb::array_view ranges) { - gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION); - gdb_assert (discriminant_index == -1 - || (discriminant_index >= 0 - && discriminant_index < TYPE_NFIELDS (type))); + /* 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; + + 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); + + 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); + + 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_UNDEFINED; - prop.data.baton = disc; + prop.kind = PROP_VARIANT_PARTS; + prop.data.variant_parts = prop_value; - add_dyn_prop (DYN_PROP_DISCRIMINATED, prop, type); - - return disc; + 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; + TYPE_FIELD_NAME (type, 2) = name; + SET_FIELD_BITPOS (TYPE_FIELD (type, 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)); - - /* 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 f686e5407b..134515845f 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 139e4c2f2c..20661e48d9 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); + /* 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; - struct type *union_type = TYPE_FIELD_TYPE (type, 0); - - 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 04cf22cced..aa995e6eec 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 dfe3e8e3ed..ae859ca722 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 ();