[PATCH 1/2][PR gdb/20239] Make evaluation and type-printing of all NonZero-optimized enums work

Manish Goregaokar manish@mozilla.com
Tue Jun 21 09:40:00 GMT 2016


Moved over from https://sourceware.org/bugzilla/show_bug.cgi?id=20239.
Regarding the xstrdup, this is done because strsep edits the string
itself.

Built and tested on OS X El Capitan. Methods tests fail, but they are
a preexisting failure due to Rust's debuginfo changing.



gdb/ChangeLog:
2016-06-21  Manish Goregaokar  <manish@mozilla.com>

    PR gdb/20239
    * rust-lang.c (rust_get_disr_info): Correctly interpret
    NonZero-optimized enums of arbitrary depth.
    (rust_print_type): Correctly print NonZero-optimized
    enums.
---
 gdb/rust-lang.c | 72 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 55 insertions(+), 17 deletions(-)

diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 5df99ce..baabf76 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -120,42 +120,69 @@ rust_get_disr_info (struct type *type, const
gdb_byte *valaddr,
   if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
            strlen (RUST_ENUM_PREFIX)) == 0)
     {
-      char *tail;
+      char *name, *tail, *token;
       unsigned long fieldno;
       struct type *member_type;
       LONGEST value;

+         char *type_name;
+
       ret.is_encoded = 1;

       if (TYPE_NFIELDS (type) != 1)
     error (_("Only expected one field in %s type"), RUST_ENUM_PREFIX);

-      fieldno = strtoul (TYPE_FIELD_NAME (type, 0) + strlen (RUST_ENUM_PREFIX),
-             &tail, 10);
-      if (*tail != '$')
+      /* Optimized enums have only one field */
+      member_type = TYPE_FIELD_TYPE (type, 0);
+
+      name = xstrdup (TYPE_FIELD_NAME (type, 0));
+      cleanup = make_cleanup (xfree, name);
+      tail = name + 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 */
+      while ((token = strsep (&tail, "$")) != NULL)
+      {
+         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);
+        type_name = TYPE_NAME (member_type);
+      };
+
+      if (token >= name + strlen (TYPE_FIELD_NAME (type, 0)))
     error (_("Invalid form for %s"), RUST_ENUM_PREFIX);
+        value = unpack_long (member_type,
+           valaddr + embedded_offset);
+

-      member_type = TYPE_FIELD_TYPE (type, 0);
-      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;
-      value = unpack_long (TYPE_FIELD_TYPE (member_type, fieldno),
-               valaddr + embedded_offset);
       if (value == 0)
     {
       ret.field_no = RUST_ENCODED_ENUM_HIDDEN;
-      ret.name = concat (TYPE_NAME (type), "::", tail + 1, (char *) NULL);
+      ret.name = concat (TYPE_NAME (type), "::", token, (char *) NULL);
     }
       else
     {
       ret.field_no = RUST_ENCODED_ENUM_REAL;
       ret.name = concat (TYPE_NAME (type), "::",
-                 rust_last_path_segment (TYPE_NAME (member_type)),
+                 rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE
(type, 0))),
                  (char *) NULL);
     }

+      do_cleanups (cleanup);
       return ret;
     }

@@ -841,6 +868,7 @@ rust_print_type (struct type *type, const char *varstring,
       {
     /* ADT enums */
     int i, len = 0;
+  int skip_to = 1; /* Skip the discriminant field */

     fputs_filtered ("enum ", stream);
     if (TYPE_TAG_NAME (type) != NULL)
@@ -849,7 +877,17 @@ rust_print_type (struct type *type, const char *varstring,
         fputs_filtered (" ", stream);
         len = strlen (TYPE_TAG_NAME (type));
       }
-    fputs_filtered ("{\n", stream);
+    fputs_filtered ("{\n", stream);
+
+  if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
+       strlen (RUST_ENUM_PREFIX)) == 0) {
+    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;
+    }
+  }

     for (i = 0; i < TYPE_NFIELDS (type); ++i)
       {
@@ -859,14 +897,14 @@ rust_print_type (struct type *type, const char *varstring,

         fprintfi_filtered (level + 2, stream, "%s", name);

-        if (TYPE_NFIELDS (variant_type) > 1)
+        if (TYPE_NFIELDS (variant_type) > skip_to)
           {
-        int first = 1;
+    int first = 1;
         int is_tuple = rust_tuple_variant_type_p (variant_type);
         int j;

         fputs_filtered (is_tuple ? "(" : "{", stream);
-        for (j = 1; j < TYPE_NFIELDS (variant_type); j++)
+        for (j = skip_to; j < TYPE_NFIELDS (variant_type); j++)
           {
             if (first)
               first = 0;
-- 
2.8.3



More information about the Gdb-patches mailing list