#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
#include <string>
#include <vector>
-/* Returns the last segment of a Rust path like foo::bar::baz. Will
- not handle cases where the last segment contains generics. This
- will return NULL if the last segment cannot be found. */
+/* See rust-lang.h. */
-static const char *
-rust_last_path_segment (const char * path)
+const char *
+rust_last_path_segment (const char *path)
{
const char *result = strrchr (path, ':');
if (result == NULL)
- return NULL;
+ return path;
return result + 1;
}
return std::string (scope, cp_find_first_component (scope));
}
-/* Information about the discriminant/variant of an enum */
-
-struct disr_info
-{
- /* Name of field. */
- std::string name;
- /* Field number in union. Negative on error. For an encoded enum,
- the "hidden" member will always be field 1, and the "real" member
- will always be field 0. */
- int field_no;
- /* True if this is an encoded enum that has a single "real" member
- and a single "hidden" member. */
- unsigned int is_encoded : 1;
-};
-
-/* The prefix of a specially-encoded enum. */
+/* Return true if TYPE, which must be a struct type, represents a Rust
+ enum. */
-#define RUST_ENUM_PREFIX "RUST$ENCODED$ENUM$"
-
-/* The number of the real field. */
-
-#define RUST_ENCODED_ENUM_REAL 0
-
-/* The number of the hidden field. */
-
-#define RUST_ENCODED_ENUM_HIDDEN 1
-
-/* Whether or not a TYPE_CODE_UNION value is an untagged union
- as opposed to being a regular Rust enum. */
static bool
-rust_union_is_untagged (struct type *type)
+rust_enum_p (const struct type *type)
{
- /* Unions must have at least one field. */
- if (TYPE_NFIELDS (type) == 0)
- return false;
- /* If the first field is named, but the name has the rust enum prefix,
- it is an enum. */
- if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
- strlen (RUST_ENUM_PREFIX)) == 0)
- return false;
- /* Unions only have named fields. */
- for (int i = 0; i < TYPE_NFIELDS (type); ++i)
- {
- if (strlen (TYPE_FIELD_NAME (type, i)) == 0)
- return false;
- }
- return true;
+ return (TYPE_CODE (type) == TYPE_CODE_STRUCT
+ && TYPE_NFIELDS (type) == 1
+ && TYPE_FLAG_DISCRIMINATED_UNION (TYPE_FIELD_TYPE (type, 0)));
}
-/* Utility function to get discriminant info for a given value. */
+/* Given an enum type and contents, find which variant is active. */
-static struct disr_info
-rust_get_disr_info (struct type *type, const gdb_byte *valaddr,
- int embedded_offset, CORE_ADDR address,
- struct value *val)
+struct field *
+rust_enum_variant (struct type *type, const gdb_byte *contents)
{
- int i;
- struct disr_info ret;
- struct type *disr_type;
- struct value_print_options opts;
- const char *name_segment;
-
- get_no_prettyformat_print_options (&opts);
-
- ret.field_no = -1;
- ret.is_encoded = 0;
-
- if (TYPE_NFIELDS (type) == 0)
- error (_("Encountered void enum value"));
-
- /* If an enum has two values where one is empty and the other holds
- a pointer that cannot be zero; then the Rust compiler optimizes
- away the discriminant and instead uses a zero value in the
- pointer field to indicate the empty variant. */
- if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
- strlen (RUST_ENUM_PREFIX)) == 0)
- {
- char *tail, *token, *saveptr = NULL;
- unsigned long fieldno;
- struct type *member_type;
- LONGEST value;
-
- ret.is_encoded = 1;
-
- if (TYPE_NFIELDS (type) != 1)
- error (_("Only expected one field in %s type"), RUST_ENUM_PREFIX);
-
- /* Optimized enums have only one field. */
- member_type = TYPE_FIELD_TYPE (type, 0);
-
- std::string name (TYPE_FIELD_NAME (type, 0));
- tail = &name[0] + strlen (RUST_ENUM_PREFIX);
-
- /* The location of the value that doubles as a discriminant is
- stored in the name of the field, as
- 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. */
- for (token = strtok_r (tail, "$", &saveptr);
- token != NULL;
- token = strtok_r (NULL, "$", &saveptr))
- {
- if (sscanf (token, "%lu", &fieldno) != 1)
- {
- /* We have reached the enum name, which cannot start
- with a digit. */
- break;
- }
- if (fieldno >= TYPE_NFIELDS (member_type))
- error (_("%s refers to field after end of member type"),
- RUST_ENUM_PREFIX);
-
- embedded_offset += TYPE_FIELD_BITPOS (member_type, fieldno) / 8;
- member_type = TYPE_FIELD_TYPE (member_type, fieldno);
- }
-
- if (token == NULL)
- error (_("Invalid form for %s"), RUST_ENUM_PREFIX);
- value = unpack_long (member_type, valaddr + embedded_offset);
-
- if (value == 0)
- {
- ret.field_no = RUST_ENCODED_ENUM_HIDDEN;
- ret.name = std::string (TYPE_NAME (type)) + "::" + token;
- }
- else
- {
- ret.field_no = RUST_ENCODED_ENUM_REAL;
- ret.name = (std::string (TYPE_NAME (type)) + "::"
- + rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (type, 0))));
- }
-
- return ret;
- }
-
- disr_type = TYPE_FIELD_TYPE (type, 0);
-
- if (TYPE_NFIELDS (disr_type) == 0)
- {
- /* This is a bounds check and should never be hit unless Rust
- has changed its debuginfo format. */
- error (_("Could not find enum discriminant field"));
- }
- else if (TYPE_NFIELDS (type) == 1)
- {
- /* Sometimes univariant enums are encoded without a
- discriminant. In that case, treating it as an encoded enum
- with the first field being the actual type works. */
- const char *field_name = TYPE_NAME (TYPE_FIELD_TYPE (type, 0));
- const char *last = rust_last_path_segment (field_name);
- ret.name = std::string (TYPE_NAME (type)) + "::" + last;
- ret.field_no = RUST_ENCODED_ENUM_REAL;
- ret.is_encoded = 1;
- return ret;
- }
-
- if (strcmp (TYPE_FIELD_NAME (disr_type, 0), "RUST$ENUM$DISR") != 0)
- error (_("Rust debug format has changed"));
-
- string_file temp_file;
- /* The first value of the first field (or any field)
- is the discriminant value. */
- c_val_print (TYPE_FIELD_TYPE (disr_type, 0),
- (embedded_offset + TYPE_FIELD_BITPOS (type, 0) / 8
- + TYPE_FIELD_BITPOS (disr_type, 0) / 8),
- address, &temp_file,
- 0, val, &opts);
-
- ret.name = std::move (temp_file.string ());
- name_segment = rust_last_path_segment (ret.name.c_str ());
- if (name_segment != NULL)
- {
- for (i = 0; i < TYPE_NFIELDS (type); ++i)
- {
- /* Sadly, the discriminant value paths do not match the type
- field name paths ('core::option::Option::Some' vs
- 'core::option::Some'). However, enum variant names are
- unique in the last path segment and the generics are not
- part of this path, so we can just compare those. This is
- hackish and would be better fixed by improving rustc's
- metadata for enums. */
- const char *field_type = TYPE_NAME (TYPE_FIELD_TYPE (type, i));
-
- if (field_type != NULL
- && strcmp (name_segment,
- rust_last_path_segment (field_type)) == 0)
- {
- ret.field_no = i;
- break;
- }
- }
- }
+ /* In Rust the enum always fills the containing structure. */
+ gdb_assert (TYPE_FIELD_BITPOS (type, 0) == 0);
- if (ret.field_no == -1 && !ret.name.empty ())
- {
- /* Somehow the discriminant wasn't found. */
- error (_("Could not find variant of %s with discriminant %s"),
- TYPE_TAG_NAME (type), ret.name.c_str ());
- }
+ struct type *union_type = TYPE_FIELD_TYPE (type, 0);
- return ret;
+ int fieldno = value_union_variant (union_type, contents);
+ return &TYPE_FIELD (union_type, fieldno);
}
/* See rust-lang.h. */
&& TYPE_TAG_NAME (type)[0] == '(');
}
-
/* Return true if all non-static fields of a structlike type are in a
- sequence like __0, __1, __2. OFFSET lets us skip fields. */
+ sequence like __0, __1, __2. */
static bool
-rust_underscore_fields (struct type *type, int offset)
+rust_underscore_fields (struct type *type)
{
int i, field_number;
{
if (!field_is_static (&TYPE_FIELD (type, i)))
{
- if (offset > 0)
- offset--;
- else
- {
- char buf[20];
+ char buf[20];
- xsnprintf (buf, sizeof (buf), "__%d", field_number);
- if (strcmp (buf, TYPE_FIELD_NAME (type, i)) != 0)
- return false;
- field_number++;
- }
+ xsnprintf (buf, sizeof (buf), "__%d", field_number);
+ if (strcmp (buf, TYPE_FIELD_NAME (type, i)) != 0)
+ return false;
+ field_number++;
}
}
return true;
/* This is just an approximation until DWARF can represent Rust more
precisely. We exclude zero-length structs because they may not
be tuple structs, and there's no way to tell. */
- return TYPE_NFIELDS (type) > 0 && rust_underscore_fields (type, 0);
-}
-
-/* Return true if a variant TYPE is a tuple variant, false otherwise. */
-
-static bool
-rust_tuple_variant_type_p (struct type *type)
-{
- /* First field is discriminant */
- return rust_underscore_fields (type, 1);
+ return TYPE_NFIELDS (type) > 0 && rust_underscore_fields (type);
}
/* Return true if TYPE is a slice type, otherwise false. */
options);
}
-/* rust_print_type branch for structs and untagged unions. */
+/* rust_val_print helper for structs and untagged unions. */
static void
val_print_struct (struct type *type, int embedded_offset,
fputs_filtered ("}", stream);
}
+/* rust_val_print helper for discriminated unions (Rust enums). */
+
+static void
+rust_print_enum (struct type *type, int embedded_offset,
+ CORE_ADDR address, struct ui_file *stream,
+ int recurse, struct value *val,
+ const struct value_print_options *options)
+{
+ struct value_print_options opts = *options;
+
+ opts.deref_ref = 0;
+
+ const gdb_byte *valaddr = value_contents_for_printing (val);
+ struct field *variant_field = rust_enum_variant (type, valaddr);
+ embedded_offset += FIELD_BITPOS (*variant_field) / 8;
+ struct type *variant_type = FIELD_TYPE (*variant_field);
+
+ int nfields = TYPE_NFIELDS (variant_type);
+
+ bool is_tuple = rust_tuple_struct_type_p (variant_type);
+
+ fprintf_filtered (stream, "%s", TYPE_NAME (variant_type));
+ if (nfields == 0)
+ {
+ /* In case of a nullary variant like 'None', just output
+ the name. */
+ return;
+ }
+
+ /* In case of a non-nullary variant, we output 'Foo(x,y,z)'. */
+ if (is_tuple)
+ fprintf_filtered (stream, "(");
+ else
+ {
+ /* struct variant. */
+ fprintf_filtered (stream, "{");
+ }
+
+ bool first_field = true;
+ for (int j = 0; j < TYPE_NFIELDS (variant_type); j++)
+ {
+ if (!first_field)
+ fputs_filtered (", ", stream);
+ first_field = false;
+
+ if (!is_tuple)
+ fprintf_filtered (stream, "%s: ",
+ TYPE_FIELD_NAME (variant_type, j));
+
+ val_print (TYPE_FIELD_TYPE (variant_type, j),
+ (embedded_offset
+ + TYPE_FIELD_BITPOS (variant_type, j) / 8),
+ address,
+ stream, recurse + 1, val, &opts,
+ current_language);
+ }
+
+ if (is_tuple)
+ fputs_filtered (")", stream);
+ else
+ fputs_filtered ("}", stream);
+}
+
static const struct generic_val_print_decorations rust_decorations =
{
/* Complex isn't used in Rust, but we provide C-ish values just in
break;
case TYPE_CODE_UNION:
- {
- int j, nfields, first_field, is_tuple, start;
- struct type *variant_type;
- struct disr_info disr;
- struct value_print_options opts;
-
- /* Untagged unions are printed as if they are structs.
- Since the field bit positions overlap in the debuginfo,
- the code for printing a union is same as that for a struct,
- the only difference is that the input type will have overlapping
- fields. */
- if (rust_union_is_untagged (type))
- {
- val_print_struct (type, embedded_offset, address, stream,
- recurse, val, options);
- break;
- }
-
- opts = *options;
- opts.deref_ref = 0;
-
- disr = rust_get_disr_info (type, valaddr, embedded_offset, address,
- val);
-
- if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
- {
- fprintf_filtered (stream, "%s", disr.name.c_str ());
- break;
- }
-
- first_field = 1;
- variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
- nfields = TYPE_NFIELDS (variant_type);
-
- is_tuple = (disr.is_encoded
- ? rust_tuple_struct_type_p (variant_type)
- : rust_tuple_variant_type_p (variant_type));
- start = disr.is_encoded ? 0 : 1;
-
- if (nfields > start)
- {
- /* In case of a non-nullary variant, we output 'Foo(x,y,z)'. */
- if (is_tuple)
- fprintf_filtered (stream, "%s(", disr.name.c_str ());
- else
- {
- /* struct variant. */
- fprintf_filtered (stream, "%s{", disr.name.c_str ());
- }
- }
- else
- {
- /* In case of a nullary variant like 'None', just output
- the name. */
- fprintf_filtered (stream, "%s", disr.name.c_str ());
- break;
- }
-
- for (j = start; j < TYPE_NFIELDS (variant_type); j++)
- {
- if (!first_field)
- fputs_filtered (", ", stream);
- first_field = 0;
-
- if (!is_tuple)
- fprintf_filtered (stream, "%s: ",
- TYPE_FIELD_NAME (variant_type, j));
-
- val_print (TYPE_FIELD_TYPE (variant_type, j),
- (embedded_offset
- + TYPE_FIELD_BITPOS (type, disr.field_no) / 8
- + TYPE_FIELD_BITPOS (variant_type, j) / 8),
- address,
- stream, recurse + 1, val, &opts,
- current_language);
- }
-
- if (is_tuple)
- fputs_filtered (")", stream);
- else
- fputs_filtered ("}", stream);
- }
+ /* Untagged unions are printed as if they are structs. Since
+ the field bit positions overlap in the debuginfo, the code
+ for printing a union is same as that for a struct, the only
+ difference is that the input type will have overlapping
+ fields. */
+ val_print_struct (type, embedded_offset, address, stream,
+ recurse, val, options);
break;
case TYPE_CODE_STRUCT:
- val_print_struct (type, embedded_offset, address, stream,
- recurse, val, options);
+ if (rust_enum_p (type))
+ rust_print_enum (type, embedded_offset, address, stream,
+ recurse, val, options);
+ else
+ val_print_struct (type, embedded_offset, address, stream,
+ recurse, val, options);
break;
default:
\f
static void
-rust_print_type (struct type *type, const char *varstring,
- struct ui_file *stream, int show, int level,
- const struct type_print_options *flags);
+rust_internal_print_type (struct type *type, const char *varstring,
+ struct ui_file *stream, int show, int level,
+ const struct type_print_options *flags,
+ bool for_rust_enum);
/* Print a struct or union typedef. */
static void
rust_print_struct_def (struct type *type, const char *varstring,
struct ui_file *stream, int show, int level,
- const struct type_print_options *flags)
+ const struct type_print_options *flags,
+ bool for_rust_enum)
{
- bool is_tuple_struct;
- int i;
-
/* Print a tuple type simply. */
if (rust_tuple_type_p (type))
{
if (TYPE_N_BASECLASSES (type) > 0)
c_print_type (type, varstring, stream, show, level, flags);
- /* This code path is also used by unions. */
- if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
- fputs_filtered ("struct ", stream);
- else
- fputs_filtered ("union ", stream);
+ /* Compute properties of TYPE here because, in the enum case, the
+ rest of the code ends up looking only at the variant part. */
+ const char *tagname = TYPE_TAG_NAME (type);
+ bool is_tuple_struct = rust_tuple_struct_type_p (type);
+ bool is_tuple = rust_tuple_type_p (type);
+ bool is_enum = rust_enum_p (type);
+ bool is_univariant = false;
+
+ int enum_discriminant_index = -1;
- if (TYPE_TAG_NAME (type) != NULL)
- fputs_filtered (TYPE_TAG_NAME (type), stream);
+ if (for_rust_enum)
+ {
+ /* Already printing an outer enum, so nothing to print here. */
+ }
+ else
+ {
+ /* This code path is also used by unions and enums. */
+ if (is_enum)
+ {
+ fputs_filtered ("enum ", stream);
+ 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;
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_UNION && TYPE_NFIELDS (type) == 1)
+ {
+ /* Probably a univariant enum. */
+ fputs_filtered ("enum ", stream);
+ is_univariant = true;
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
+ fputs_filtered ("struct ", stream);
+ else
+ fputs_filtered ("union ", stream);
- is_tuple_struct = rust_tuple_struct_type_p (type);
+ if (tagname != NULL)
+ fputs_filtered (tagname, stream);
+ }
- if (TYPE_NFIELDS (type) == 0 && !rust_tuple_type_p (type))
+ if (TYPE_NFIELDS (type) == 0 && !is_tuple)
return;
- fputs_filtered (is_tuple_struct ? " (\n" : " {\n", stream);
+ if (for_rust_enum)
+ fputs_filtered (is_tuple_struct ? "(" : "{", stream);
+ else
+ fputs_filtered (is_tuple_struct ? " (\n" : " {\n", stream);
- for (i = 0; i < TYPE_NFIELDS (type); ++i)
+ for (int i = 0; i < TYPE_NFIELDS (type); ++i)
{
QUIT;
if (field_is_static (&TYPE_FIELD (type, i)))
/* For a tuple struct we print the type but nothing
else. */
- print_spaces_filtered (level + 2, stream);
- if (!is_tuple_struct)
+ if (!for_rust_enum)
+ print_spaces_filtered (level + 2, stream);
+ if (is_enum)
+ {
+ if (i == enum_discriminant_index)
+ continue;
+ fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+ }
+ else if (is_univariant)
+ {
+ const char *name
+ = rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (type, i)));
+ fputs_filtered (name, stream);
+ }
+ else if (!is_tuple_struct)
fprintf_filtered (stream, "%s: ", TYPE_FIELD_NAME (type, i));
- rust_print_type (TYPE_FIELD_TYPE (type, i), NULL,
- stream, show - 1, level + 2,
- flags);
- fputs_filtered (",\n", stream);
+ rust_internal_print_type (TYPE_FIELD_TYPE (type, i), NULL,
+ stream,
+ ((is_enum || is_univariant) ? show : show - 1),
+ level + 2, flags, is_enum || is_univariant);
+ if (!for_rust_enum)
+ fputs_filtered (",\n", stream);
+ else if (i + 1 < TYPE_NFIELDS (type))
+ fputs_filtered (", ", stream);
}
- fprintfi_filtered (level, stream, is_tuple_struct ? ")" : "}");
+ if (!for_rust_enum)
+ print_spaces_filtered (level, stream);
+ fputs_filtered (is_tuple_struct ? ")" : "}", stream);
}
/* la_print_typedef implementation for Rust. */
/* la_print_type implementation for Rust. */
static void
-rust_print_type (struct type *type, const char *varstring,
- struct ui_file *stream, int show, int level,
- const struct type_print_options *flags)
+rust_internal_print_type (struct type *type, const char *varstring,
+ struct ui_file *stream, int show, int level,
+ const struct type_print_options *flags,
+ bool for_rust_enum)
{
int i;
switch (TYPE_CODE (type))
{
case TYPE_CODE_VOID:
- fputs_filtered ("()", stream);
+ /* If we have an enum, we've already printed the type's
+ unqualified name, and there is nothing else to print
+ here. */
+ if (!for_rust_enum)
+ fputs_filtered ("()", stream);
break;
case TYPE_CODE_FUNC:
QUIT;
if (i > 0)
fputs_filtered (", ", stream);
- rust_print_type (TYPE_FIELD_TYPE (type, i), "", stream, -1, 0,
- flags);
+ rust_internal_print_type (TYPE_FIELD_TYPE (type, i), "", stream,
+ -1, 0, flags, false);
}
fputs_filtered (")", stream);
/* If it returns unit, we can omit the return type. */
if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
{
fputs_filtered (" -> ", stream);
- rust_print_type (TYPE_TARGET_TYPE (type), "", stream, -1, 0, flags);
+ rust_internal_print_type (TYPE_TARGET_TYPE (type), "", stream,
+ -1, 0, flags, false);
}
break;
LONGEST low_bound, high_bound;
fputs_filtered ("[", stream);
- rust_print_type (TYPE_TARGET_TYPE (type), NULL,
- stream, show - 1, level, flags);
+ rust_internal_print_type (TYPE_TARGET_TYPE (type), NULL,
+ stream, show - 1, level, flags, false);
if (TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCEXPR
|| TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCLIST)
}
break;
+ case TYPE_CODE_UNION:
case TYPE_CODE_STRUCT:
- rust_print_struct_def (type, varstring, stream, show, level, flags);
+ rust_print_struct_def (type, varstring, stream, show, level, flags,
+ for_rust_enum);
break;
case TYPE_CODE_ENUM:
}
break;
- case TYPE_CODE_UNION:
- {
- /* ADT enums. */
- int i;
- /* Skip the discriminant field. */
- int skip_to = 1;
-
- /* Unions and structs have the same syntax in Rust,
- the only difference is that structs are declared with `struct`
- and union with `union`. This difference is handled in the struct
- printer. */
- if (rust_union_is_untagged (type))
- {
- rust_print_struct_def (type, varstring, stream, show, level, flags);
- break;
- }
-
- fputs_filtered ("enum ", stream);
- if (TYPE_TAG_NAME (type) != NULL)
- {
- fputs_filtered (TYPE_TAG_NAME (type), stream);
- fputs_filtered (" ", stream);
- }
- fputs_filtered ("{\n", stream);
-
- if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
- strlen (RUST_ENUM_PREFIX)) == 0)
- {
- const char *zero_field = strrchr (TYPE_FIELD_NAME (type, 0), '$');
- if (zero_field != NULL && strlen (zero_field) > 1)
- {
- fprintfi_filtered (level + 2, stream, "%s,\n", zero_field + 1);
- /* There is no explicit discriminant field, skip nothing. */
- skip_to = 0;
- }
- }
- else if (TYPE_NFIELDS (type) == 1)
- skip_to = 0;
-
- for (i = 0; i < TYPE_NFIELDS (type); ++i)
- {
- struct type *variant_type = TYPE_FIELD_TYPE (type, i);
- const char *name
- = rust_last_path_segment (TYPE_NAME (variant_type));
-
- fprintfi_filtered (level + 2, stream, "%s", name);
-
- if (TYPE_NFIELDS (variant_type) > skip_to)
- {
- int first = 1;
- bool is_tuple = (TYPE_NFIELDS (type) == 1
- ? rust_tuple_struct_type_p (variant_type)
- : rust_tuple_variant_type_p (variant_type));
- int j;
-
- fputs_filtered (is_tuple ? "(" : "{", stream);
- for (j = skip_to; j < TYPE_NFIELDS (variant_type); j++)
- {
- if (first)
- first = 0;
- else
- fputs_filtered (", ", stream);
-
- if (!is_tuple)
- fprintf_filtered (stream, "%s: ",
- TYPE_FIELD_NAME (variant_type, j));
-
- rust_print_type (TYPE_FIELD_TYPE (variant_type, j), NULL,
- stream, show - 1, level + 2,
- flags);
- }
- fputs_filtered (is_tuple ? ")" : "}", stream);
- }
-
- fputs_filtered (",\n", stream);
- }
-
- fputs_filtered ("}", stream);
- }
- break;
-
default:
c_printer:
c_print_type (type, varstring, stream, show, level, flags);
}
}
+static void
+rust_print_type (struct type *type, const char *varstring,
+ struct ui_file *stream, int show, int level,
+ const struct type_print_options *flags)
+{
+ rust_internal_print_type (type, varstring, stream, show, level,
+ flags, false);
+}
+
\f
/* Compute the alignment of the type T. */
struct value *lhs;
int pc, field_number, nfields;
struct type *type, *variant_type;
- struct disr_info disr;
pc = (*pos)++;
field_number = longest_to_int (exp->elts[pc + 1].longconst);
lhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
type = value_type (lhs);
- /* Untagged unions can't have anonymous field access since
- they can only have named fields. */
- if (TYPE_CODE (type) == TYPE_CODE_UNION
- && !rust_union_is_untagged (type))
+
+ /* Treat a univariant union as if it were an enum. */
+ if (TYPE_CODE (type) == TYPE_CODE_UNION && TYPE_NFIELDS (type) == 1)
{
- disr = rust_get_disr_info (type, value_contents (lhs),
- value_embedded_offset (lhs),
- value_address (lhs), lhs);
+ lhs = value_primitive_field (lhs, 0, 0, type);
+ type = value_type (lhs);
+ }
- if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
- {
- variant_type = NULL;
- nfields = 0;
- }
- else
+ if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
+ {
+ struct type *outer_type = NULL;
+
+ if (rust_enum_p (type))
{
- variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
- nfields = TYPE_NFIELDS (variant_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));
+ outer_type = type;
+ type = value_type (lhs);
}
- if (!disr.is_encoded)
- ++field_number;
-
- if (field_number >= nfields || field_number < 0)
- error(_("Cannot access field %d of variant %s, \
-there are only %d fields"),
- disr.is_encoded ? field_number : field_number - 1,
- disr.name.c_str (),
- disr.is_encoded ? nfields : nfields - 1);
-
- if (!(disr.is_encoded
- ? rust_tuple_struct_type_p (variant_type)
- : rust_tuple_variant_type_p (variant_type)))
- error(_("Variant %s is not a tuple variant"), disr.name.c_str ());
-
- result = value_primitive_field (lhs, 0, field_number,
- variant_type);
- }
- else if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
- {
/* Tuples and tuple structs */
- nfields = TYPE_NFIELDS(type);
+ nfields = TYPE_NFIELDS (type);
if (field_number >= nfields || field_number < 0)
- error(_("Cannot access field %d of %s, there are only %d fields"),
- field_number, TYPE_TAG_NAME (type), nfields);
+ {
+ if (outer_type != NULL)
+ error(_("Cannot access field %d of variant %s::%s, "
+ "there are only %d fields"),
+ field_number, TYPE_TAG_NAME (outer_type),
+ rust_last_path_segment (TYPE_TAG_NAME (type)),
+ nfields);
+ else
+ error(_("Cannot access field %d of %s, "
+ "there are only %d fields"),
+ field_number, TYPE_TAG_NAME (type), nfields);
+ }
/* Tuples are tuple structs too. */
if (!rust_tuple_struct_type_p (type))
- error(_("Attempting to access anonymous field %d of %s, which is \
-not a tuple, tuple struct, or tuple-like variant"),
- field_number, TYPE_TAG_NAME (type));
+ {
+ if (outer_type != NULL)
+ error(_("Variant %s::%s is not a tuple variant"),
+ TYPE_TAG_NAME (outer_type),
+ rust_last_path_segment (TYPE_TAG_NAME (type)));
+ else
+ error(_("Attempting to access anonymous field %d "
+ "of %s, which is not a tuple, tuple struct, or "
+ "tuple-like variant"),
+ field_number, TYPE_TAG_NAME (type));
+ }
result = value_primitive_field (lhs, 0, field_number, type);
}
const char *field_name = &exp->elts[pc + 2].string;
type = value_type (lhs);
- if (TYPE_CODE (type) == TYPE_CODE_UNION
- && !rust_union_is_untagged (type))
+ if (TYPE_CODE (type) == TYPE_CODE_STRUCT && rust_enum_p (type))
{
- int i, start;
- struct disr_info disr;
- struct type *variant_type;
-
- disr = rust_get_disr_info (type, value_contents (lhs),
- value_embedded_offset (lhs),
- value_address (lhs), lhs);
-
- if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
- error(_("Could not find field %s of struct variant %s"),
- field_name, disr.name.c_str ());
-
- variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
-
- if (variant_type == NULL
- || (disr.is_encoded
- ? rust_tuple_struct_type_p (variant_type)
- : rust_tuple_variant_type_p (variant_type)))
- error(_("Attempting to access named field %s of tuple variant %s, \
-which has only anonymous fields"),
- field_name, disr.name.c_str ());
-
- start = disr.is_encoded ? 0 : 1;
- for (i = start; i < TYPE_NFIELDS (variant_type); i++)
+ 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));
+
+ struct type *outer_type = type;
+ type = value_type (lhs);
+ if (rust_tuple_type_p (type) || rust_tuple_struct_type_p (type))
+ error (_("Attempting to access named field foo of tuple "
+ "variant %s::%s, which has only anonymous fields"),
+ TYPE_TAG_NAME (outer_type),
+ rust_last_path_segment (TYPE_NAME (type)));
+
+ TRY
{
- if (strcmp (TYPE_FIELD_NAME (variant_type, i),
- field_name) == 0) {
- result = value_primitive_field (lhs, 0, i, variant_type);
- break;
- }
+ result = value_struct_elt (&lhs, NULL, field_name,
+ NULL, "structure");
}
-
- if (i == TYPE_NFIELDS (variant_type))
- /* We didn't find it. */
- error(_("Could not find field %s of struct variant %s"),
- field_name, disr.name.c_str ());
+ CATCH (except, RETURN_MASK_ERROR)
+ {
+ error (_("Could not find field %s of struct variant %s::%s"),
+ field_name, TYPE_TAG_NAME (outer_type),
+ rust_last_path_segment (TYPE_NAME (type)));
+ }
+ END_CATCH
}
else
- {
- result = value_struct_elt (&lhs, NULL, field_name, NULL,
- "structure");
- if (noside == EVAL_AVOID_SIDE_EFFECTS)
- result = value_zero (value_type (result), VALUE_LVAL (result));
- }
+ result = value_struct_elt (&lhs, NULL, field_name, NULL, "structure");
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ result = value_zero (value_type (result), VALUE_LVAL (result));
}
break;