Support ptype/o in Rust
authorTom Tromey <tom@tromey.com>
Fri, 8 Jun 2018 19:18:25 +0000 (13:18 -0600)
committerTom Tromey <tom@tromey.com>
Tue, 26 Jun 2018 20:53:17 +0000 (14:53 -0600)
This adds support for ptype/o to the Rust language code.

By default, the Rust compiler reorders fields to reduce padding.  So,
the Rust language code sorts the fields by offset before printing.
This may yield somewhat odd-looking results, but it is faithful to
"what really happens", and might be useful when doing lower-level
debugging.

The reordering can be disabled using #[repr(c)]; ptype/o might be more
useful in this case.

gdb/ChangeLog
2018-06-26  Tom Tromey  <tom@tromey.com>

PR rust/22574:
* typeprint.c (whatis_exp): Allow ptype/o for Rust.
* rust-lang.c (rust_print_struct_def): Add podata parameter.
Update.
(rust_internal_print_type): Add podata parameter.
(rust_print_type): Update.

gdb/testsuite/ChangeLog
2018-06-26  Tom Tromey  <tom@tromey.com>

PR rust/22574:
* gdb.rust/simple.exp (test_one_slice): Add ptype/o tests.
* gdb.rust/simple.rs (struct SimpleLayout): New.

gdb/ChangeLog
gdb/rust-lang.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.rust/simple.exp
gdb/testsuite/gdb.rust/simple.rs
gdb/typeprint.c

index 677328b2c51d5e8decd2a446cfbcb49ad12767a0..2baf2525b8c5d401d0270fe8f5fc1a56ab4a4606 100644 (file)
@@ -1,3 +1,12 @@
+2018-06-26  Tom Tromey  <tom@tromey.com>
+
+       PR rust/22574:
+       * typeprint.c (whatis_exp): Allow ptype/o for Rust.
+       * rust-lang.c (rust_print_struct_def): Add podata parameter.
+       Update.
+       (rust_internal_print_type): Add podata parameter.
+       (rust_print_type): Update.
+
 2018-06-26  Tom Tromey  <tom@tromey.com>
 
        * typeprint.h (struct print_offset_data) <update, finish,
index d9807d0ac1a41ee5b9e883cd5b684a318b50f6eb..5f4aceb86ba91949ff2f8ce2293698e2b068956a 100644 (file)
 #include "objfiles.h"
 #include "psymtab.h"
 #include "rust-lang.h"
+#include "typeprint.h"
 #include "valprint.h"
 #include "varobj.h"
+#include <algorithm>
 #include <string>
 #include <vector>
 
@@ -616,14 +618,14 @@ static void
 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);
+                         bool for_rust_enum, print_offset_data *podata);
 
 /* 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,
-                      bool for_rust_enum)
+                      bool for_rust_enum, print_offset_data *podata)
 {
   /* Print a tuple type simply.  */
   if (rust_tuple_type_p (type))
@@ -636,6 +638,13 @@ rust_print_struct_def (struct type *type, const char *varstring,
   if (TYPE_N_BASECLASSES (type) > 0)
     c_print_type (type, varstring, stream, show, level, flags);
 
+  if (flags->print_offsets)
+    {
+      /* Temporarily bump the level so that the output lines up
+        correctly.  */
+      level += 2;
+    }
+
   /* 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_NAME (type);
@@ -674,16 +683,41 @@ rust_print_struct_def (struct type *type, const char *varstring,
 
   if (TYPE_NFIELDS (type) == 0 && !is_tuple)
     return;
-  if (for_rust_enum)
+  if (for_rust_enum && !flags->print_offsets)
     fputs_filtered (is_tuple_struct ? "(" : "{", stream);
   else
     fputs_filtered (is_tuple_struct ? " (\n" : " {\n", stream);
 
+  /* When printing offsets, we rearrange the fields into storage
+     order.  This lets us show holes more clearly.  We work using
+     field indices here because it simplifies calls to
+     print_offset_data::update below.  */
+  std::vector<int> fields;
   for (int i = 0; i < TYPE_NFIELDS (type); ++i)
     {
-      QUIT;
       if (field_is_static (&TYPE_FIELD (type, i)))
        continue;
+      if (is_enum && i == enum_discriminant_index)
+       continue;
+      fields.push_back (i);
+    }
+  if (flags->print_offsets)
+    std::sort (fields.begin (), fields.end (),
+              [&] (int a, int b)
+              {
+                return (TYPE_FIELD_BITPOS (type, a)
+                        < TYPE_FIELD_BITPOS (type, b));
+              });
+
+  for (int i : fields)
+    {
+      QUIT;
+
+      gdb_assert (!field_is_static (&TYPE_FIELD (type, i)));
+      gdb_assert (! (is_enum && i == enum_discriminant_index));
+
+      if (flags->print_offsets)
+       podata->update (type, i, stream);
 
       /* We'd like to print "pub" here as needed, but rustc
         doesn't emit the debuginfo, and our types don't have
@@ -691,27 +725,35 @@ rust_print_struct_def (struct type *type, const char *varstring,
 
       /* For a tuple struct we print the type but nothing
         else.  */
-      if (!for_rust_enum)
+      if (!for_rust_enum || flags->print_offsets)
        print_spaces_filtered (level + 2, stream);
       if (is_enum)
-       {
-         if (i == enum_discriminant_index)
-           continue;
-         fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
-       }
+       fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
       else if (!is_tuple_struct)
        fprintf_filtered (stream, "%s: ", TYPE_FIELD_NAME (type, i));
 
       rust_internal_print_type (TYPE_FIELD_TYPE (type, i), NULL,
                                stream, (is_enum ? show : show - 1),
-                               level + 2, flags, is_enum);
-      if (!for_rust_enum)
+                               level + 2, flags, is_enum, podata);
+      if (!for_rust_enum || flags->print_offsets)
        fputs_filtered (",\n", stream);
+      /* Note that this check of "I" is ok because we only sorted the
+        fields by offset when print_offsets was set, so we won't take
+        this branch in that case.  */
       else if (i + 1 < TYPE_NFIELDS (type))
        fputs_filtered (", ", stream);
     }
 
-  if (!for_rust_enum)
+  if (flags->print_offsets)
+    {
+      /* Undo the temporary level increase we did above.  */
+      level -= 2;
+      podata->finish (type, level, stream);
+      print_spaces_filtered (print_offset_data::indentation, stream);
+      if (level == 0)
+       print_spaces_filtered (2, stream);
+    }
+  if (!for_rust_enum || flags->print_offsets)
     print_spaces_filtered (level, stream);
   fputs_filtered (is_tuple_struct ? ")" : "}", stream);
 }
@@ -735,7 +777,7 @@ static void
 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)
+                         bool for_rust_enum, print_offset_data *podata)
 {
   int i;
 
@@ -778,7 +820,7 @@ rust_internal_print_type (struct type *type, const char *varstring,
          if (i > 0)
            fputs_filtered (", ", stream);
          rust_internal_print_type (TYPE_FIELD_TYPE (type, i), "", stream,
-                                   -1, 0, flags, false);
+                                   -1, 0, flags, false, podata);
        }
       fputs_filtered (")", stream);
       /* If it returns unit, we can omit the return type.  */
@@ -786,7 +828,7 @@ rust_internal_print_type (struct type *type, const char *varstring,
         {
           fputs_filtered (" -> ", stream);
           rust_internal_print_type (TYPE_TARGET_TYPE (type), "", stream,
-                                   -1, 0, flags, false);
+                                   -1, 0, flags, false, podata);
         }
       break;
 
@@ -796,7 +838,8 @@ rust_internal_print_type (struct type *type, const char *varstring,
 
        fputs_filtered ("[", stream);
        rust_internal_print_type (TYPE_TARGET_TYPE (type), NULL,
-                                 stream, show - 1, level, flags, false);
+                                 stream, show - 1, level, flags, false,
+                                 podata);
 
        if (TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCEXPR
            || TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCLIST)
@@ -811,7 +854,7 @@ rust_internal_print_type (struct type *type, const char *varstring,
     case TYPE_CODE_UNION:
     case TYPE_CODE_STRUCT:
       rust_print_struct_def (type, varstring, stream, show, level, flags,
-                            for_rust_enum);
+                            for_rust_enum, podata);
       break;
 
     case TYPE_CODE_ENUM:
@@ -856,8 +899,9 @@ rust_print_type (struct type *type, const char *varstring,
                 struct ui_file *stream, int show, int level,
                 const struct type_print_options *flags)
 {
+  print_offset_data podata;
   rust_internal_print_type (type, varstring, stream, show, level,
-                           flags, false);
+                           flags, false, &podata);
 }
 
 \f
index 73f554561aae88964693d04e0061f79cc3a90e01..b2901dbcc5e935a50793cbfcfde43be700d56226 100644 (file)
@@ -1,3 +1,9 @@
+2018-06-26  Tom Tromey  <tom@tromey.com>
+
+       PR rust/22574:
+       * gdb.rust/simple.exp (test_one_slice): Add ptype/o tests.
+       * gdb.rust/simple.rs (struct SimpleLayout): New.
+
 2018-06-22  Simon Marchi  <simon.marchi@ericsson.com>
 
        * gdb.base/jit-reader.exp (jit_reader_test): Expect spaces in
index ba90e061ce326c929f7d2a70a09f842eeb077da0..20fe8dd0a882ee798010ffbf466f67672b93731f 100644 (file)
@@ -285,6 +285,24 @@ gdb_test "print parametrized" \
 
 gdb_test "print u" " = simple::Union {f1: -1, f2: 255}"
 
+gdb_test_sequence "ptype/o Union" "" {
+    "/\\* offset    |  size \\*/  type = union simple::Union {"
+    "/\\*                 1 \\*/    f1: i8,"
+    "/\\*                 1 \\*/    f2: u8,"
+    ""
+    "                           /\\* total size \\(bytes\\):    1 \\*/"
+    "                         }"
+}
+
+gdb_test_sequence "ptype/o SimpleLayout" "" {
+    "/\\* offset    |  size \\*/  type = struct simple::SimpleLayout {"
+    "/\\*    0      |     2 \\*/    f1: u16,"
+    "/\\*    2      |     2 \\*/    f2: u16,"
+    ""
+    "                           /\\* total size \\(bytes\\):    4 \\*/"
+    "                         }"
+}
+
 load_lib gdb-python.exp
 if {[skip_python_tests]} {
     continue
index e5bbe521226cc4869a7d4cf525cf02abf5b1030e..9d89361b75391b5da07b8526f7e7f67c8f652be7 100644 (file)
@@ -85,6 +85,13 @@ union Union {
     f2: u8,
 }
 
+// A simple structure whose layout won't be changed by the compiler,
+// so that ptype/o testing will work on any platform.
+struct SimpleLayout {
+    f1: u16,
+    f2: u16
+}
+
 fn main () {
     let a = ();
     let b : [i32; 0] = [];
@@ -159,6 +166,7 @@ fn main () {
     };
 
     let u = Union { f2: 255 };
+    let v = SimpleLayout { f1: 8, f2: 9 };
 
     println!("{}, {}", x.0, x.1);        // set breakpoint here
     println!("{}", diff2(92, 45));
index b29ad5f54b93d4f325f3ebacebea8aa1e6d13078..7a0b7627ed4519e211731a12e5428f8837427642 100644 (file)
@@ -490,7 +490,8 @@ whatis_exp (const char *exp, int show)
                       feature.  */
                    if (show > 0
                        && (current_language->la_language == language_c
-                           || current_language->la_language == language_cplus))
+                           || current_language->la_language == language_cplus
+                           || current_language->la_language == language_rust))
                      {
                        flags.print_offsets = 1;
                        flags.print_typedefs = 0;