#include <cmath>
#include <set>
#include <forward_list>
+#include "rust-lang.h"
/* When == 1, print basic high level tracing messages.
When > 1, be more verbose.
whether the DW_AT_ranges attribute came from the skeleton or DWO. */
ULONGEST ranges_base = 0;
+ /* When reading debug info generated by older versions of rustc, we
+ have to rewrite some union types to be struct types with a
+ variant part. This rewriting must be done after the CU is fully
+ read in, because otherwise at the point of rewriting some struct
+ type might not have been fully processed. So, we keep a list of
+ all such types here and process them after expansion. */
+ std::vector<struct type *> rust_unions;
+
/* Mark used when releasing cached dies. */
unsigned int mark : 1;
}
}
+/* Allocate a fully-qualified name consisting of the two parts on the
+ obstack. */
+
+static const char *
+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. */
+
+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 (default_index == -1
+ || (default_index > 0 && default_index < TYPE_NFIELDS (type)));
+
+ TYPE_FLAG_DISCRIMINATED_UNION (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;
+
+ struct dynamic_prop prop;
+ prop.kind = PROP_UNDEFINED;
+ prop.data.baton = disc;
+
+ add_dyn_prop (DYN_PROP_DISCRIMINATED, prop, type);
+
+ return disc;
+}
+
+/* Some versions of rustc emitted enums in an unusual way.
+
+ Ordinary enums were emitted as unions. The first element of each
+ structure in the union was named "RUST$ENUM$DISR". This element
+ held the discriminant.
+
+ These versions of Rust also implemented the "non-zero"
+ optimization. When the enum had two values, and one is empty and
+ the other holds a pointer that cannot be zero, the pointer is used
+ as the discriminant, with a zero value meaning the empty variant.
+ Here, the union's first member is of the form
+ RUST$ENCODED$ENUM$<fieldno>$<fieldno>$...$<variantname>
+ where the fieldnos are the indices of the fields that should be
+ traversed in order to find the field (which may be several fields deep)
+ and the variantname is the name of the variant of the case when the
+ field is zero.
+
+ This function recognizes whether TYPE is of one of these forms,
+ and, if so, smashes it to be a variant type. */
+
+static void
+quirk_rust_enum (struct type *type, struct objfile *objfile)
+{
+ gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION);
+
+ /* We don't need to deal with empty enums. */
+ if (TYPE_NFIELDS (type) == 0)
+ return;
+
+#define RUST_ENUM_PREFIX "RUST$ENCODED$ENUM$"
+ if (TYPE_NFIELDS (type) == 1
+ && startswith (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX))
+ {
+ const char *name = TYPE_FIELD_NAME (type, 0) + strlen (RUST_ENUM_PREFIX);
+
+ /* Decode the field name to find the offset of the
+ discriminant. */
+ ULONGEST bit_offset = 0;
+ struct type *field_type = TYPE_FIELD_TYPE (type, 0);
+ while (name[0] >= '0' && name[0] <= '9')
+ {
+ char *tail;
+ unsigned long index = strtoul (name, &tail, 10);
+ name = tail;
+ if (*name != '$'
+ || index >= TYPE_NFIELDS (field_type)
+ || (TYPE_FIELD_LOC_KIND (field_type, index)
+ != FIELD_LOC_KIND_BITPOS))
+ {
+ complaint (&symfile_complaints,
+ _("Could not parse Rust enum encoding string \"%s\""
+ "[in module %s]"),
+ TYPE_FIELD_NAME (type, 0),
+ objfile_name (objfile));
+ return;
+ }
+ ++name;
+
+ bit_offset += TYPE_FIELD_BITPOS (field_type, index);
+ 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)
+ = (struct field *) TYPE_ZALLOC (type, 3 * sizeof (struct field));
+ TYPE_LENGTH (union_type) = TYPE_LENGTH (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);
+
+ /* 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))
+ = rust_fully_qualify (&objfile->objfile_obstack, TYPE_NAME (type),
+ TYPE_FIELD_NAME (union_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;
+ /* 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));
+
+ /* 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>>";
+ }
+ else if (TYPE_NFIELDS (type) == 1)
+ {
+ /* We assume that a union with a single field is a univariant
+ enum. */
+ /* 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;
+
+ /* 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);
+ TYPE_FIELDS (union_type) = TYPE_FIELDS (type);
+
+ struct type *field_type = TYPE_FIELD_TYPE (union_type, 0);
+ const char *variant_name
+ = rust_last_path_segment (TYPE_NAME (field_type));
+ TYPE_FIELD_NAME (union_type, 0) = variant_name;
+ TYPE_NAME (field_type)
+ = rust_fully_qualify (&objfile->objfile_obstack,
+ TYPE_NAME (field_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
+ {
+ struct type *disr_type = nullptr;
+ for (int i = 0; i < TYPE_NFIELDS (type); ++i)
+ {
+ disr_type = TYPE_FIELD_TYPE (type, i);
+
+ if (TYPE_NFIELDS (disr_type) == 0)
+ {
+ /* Could be data-less variant, so keep going. */
+ }
+ else if (strcmp (TYPE_FIELD_NAME (disr_type, 0),
+ "RUST$ENUM$DISR") != 0)
+ {
+ /* Not a Rust enum. */
+ return;
+ }
+ else
+ {
+ /* Found one. */
+ break;
+ }
+ }
+
+ /* If we got here without a discriminant, then it's probably
+ just a union. */
+ if (disr_type == nullptr)
+ return;
+
+ /* 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;
+
+ /* Make a union to hold the variants. */
+ 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);
+ 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),
+ TYPE_NFIELDS (type) * sizeof (struct field));
+
+ /* 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);
+
+ /* We need a way to find the correct discriminant given a
+ variant name. For convenience we build a map here. */
+ struct type *enum_type = FIELD_TYPE (*disr_field);
+ std::unordered_map<std::string, ULONGEST> discriminant_map;
+ for (int i = 0; i < TYPE_NFIELDS (enum_type); ++i)
+ {
+ if (TYPE_FIELD_LOC_KIND (enum_type, i) == FIELD_LOC_KIND_ENUMVAL)
+ {
+ const char *name
+ = rust_last_path_segment (TYPE_FIELD_NAME (enum_type, i));
+ discriminant_map[name] = TYPE_FIELD_ENUMVAL (enum_type, i);
+ }
+ }
+
+ int n_fields = TYPE_NFIELDS (union_type);
+ struct discriminant_info *disc
+ = alloc_discriminant_info (union_type, 0, -1);
+ /* Skip the discriminant here. */
+ for (int i = 1; i < n_fields; ++i)
+ {
+ /* Find the final word in the name of this variant's type.
+ 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)));
+
+ auto iter = discriminant_map.find (variant_name);
+ if (iter != discriminant_map.end ())
+ disc->discriminants[i] = iter->second;
+
+ /* Remove the discriminant field. */
+ struct type *sub_type = TYPE_FIELD_TYPE (union_type, i);
+ --TYPE_NFIELDS (sub_type);
+ ++TYPE_FIELDS (sub_type);
+ TYPE_FIELD_NAME (union_type, i) = variant_name;
+ TYPE_NAME (sub_type)
+ = rust_fully_qualify (&objfile->objfile_obstack,
+ TYPE_NAME (type), variant_name);
+ }
+ }
+}
+
+/* Rewrite some Rust unions to be structures with variants parts. */
+
+static void
+rust_union_quirks (struct dwarf2_cu *cu)
+{
+ gdb_assert (cu->language == language_rust);
+ for (struct type *type : cu->rust_unions)
+ quirk_rust_enum (type, cu->per_cu->dwarf2_per_objfile->objfile);
+}
+
/* Return the symtab for PER_CU. This works properly regardless of
whether we're using the index or psymtabs. */
physnames. */
compute_delayed_physnames (cu);
+ if (cu->language == language_rust)
+ rust_union_quirks (cu);
+
/* Some compilers don't define a DW_AT_high_pc attribute for the
compilation unit. If the DW_AT_high_pc is missing, synthesize
it, by scanning the DIE's below the compilation unit. */
physnames. */
compute_delayed_physnames (cu);
+ if (cu->language == language_rust)
+ rust_union_quirks (cu);
+
/* TUs share symbol tables.
If this is the first TU to use this symtab, complete the construction
of it with end_expandable_symtab. Otherwise, complete the addition of
}
quirk_gcc_member_function_pointer (type, objfile);
+ if (cu->language == language_rust && die->tag == DW_TAG_union_type)
+ cu->rust_unions.push_back (type);
/* NOTE: carlton/2004-03-16: GCC 3.4 (or at least one of its
snapshots) has been known to create a die giving a declaration