- 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;
- }
- }
- }