Fix type casts losing typedefs and reimplement "whatis" typedef stripping
authorPedro Alves <palves@redhat.com>
Mon, 21 Aug 2017 10:34:32 +0000 (11:34 +0100)
committerPedro Alves <palves@redhat.com>
Mon, 21 Aug 2017 10:34:32 +0000 (11:34 +0100)
(Ref: https://sourceware.org/ml/gdb/2017-06/msg00020.html)

Assuming int_t is a typedef to int:

 typedef int int_t;

gdb currently loses this expression's typedef:

 (gdb) p (int_t) 0
 $1 = 0
 (gdb) whatis $1
 type = int

or:

 (gdb) whatis (int_t) 0
 type = int

or, to get "whatis" out of the way:

 (gdb) maint print type (int_t) 0
 ...
 name 'int'
 code 0x8 (TYPE_CODE_INT)
 ...

This prevents a type printer for "int_t" kicking in, with e.g.:

 (gdb) p (int_t) 0

From the manual, we can see that that "whatis (int_t) 0" command
invocation should have printed "type = int_t":

 If @var{arg} is a variable or an expression, @code{whatis} prints its
 literal type as it is used in the source code.  If the type was
 defined using a @code{typedef}, @code{whatis} will @emph{not} print
 the data type underlying the @code{typedef}.
 (...)
 If @var{arg} is a type name that was defined using @code{typedef},
 @code{whatis} @dfn{unrolls} only one level of that @code{typedef}.

That one-level stripping is currently done here, in
gdb/eval.c:evaluate_subexp_standard, handling OP_TYPE:

...
     else if (noside == EVAL_AVOID_SIDE_EFFECTS)
{
  struct type *type = exp->elts[pc + 1].type;

  /* If this is a typedef, then find its immediate target.  We
     use check_typedef to resolve stubs, but we ignore its
     result because we do not want to dig past all
     typedefs.  */
  check_typedef (type);
  if (TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
    type = TYPE_TARGET_TYPE (type);
  return allocate_value (type);
}

However, this stripping is reachable in both:

 #1 - (gdb) whatis (int_t)0     # ARG is an expression with a cast to
                                # typedef type.
 #2 - (gdb) whatis int_t        # ARG is a type name.

while only case #2 should strip the typedef.  Removing that code from
evaluate_subexp_standard is part of the fix.  Instead, we make the
"whatis" command implementation itself strip one level of typedefs
when the command argument is a type name.

We then run into another problem, also fixed by this commit:
value_cast always drops any typedefs of the destination type.

With all that fixed, "whatis (int_t) 0" now works as expected:

 (gdb) whatis int_t
 type = int
 (gdb) whatis (int_t)0
 type = int_t

value_cast has many different exit/convertion paths, for handling many
different kinds of casts/conversions, and most of them had to be
tweaked to construct the value of the right "to" type.  The new tests
try to exercise most of it, by trying castin of many different
combinations of types.  With:

 $ make check TESTS="*/whatis-ptype*.exp */gnu_vector.exp */dfp-test.exp"

... due to combinatorial explosion, the testsuite results for the
tests above alone grow like:

 - # of expected passes            246
 + # of expected passes            3811

You'll note that the tests exposed one GCC buglet, filed here:

  Missing DW_AT_type in DW_TAG_typedef of "typedef of typedef of void"
  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81267

gdb/ChangeLog:
2017-08-21  Pedro Alves  <palves@redhat.com>

* eval.c (evaluate_subexp_standard) <OP_TYPE>: Don't dig past
typedefs.
* typeprint.c (whatis_exp): If handling "whatis", and expression
is OP_TYPE, strip one typedef level.  Otherwise don't strip
typedefs here.
* valops.c (value_cast): Save "to" type before resolving
stubs/typedefs.  Use that type as resulting value's type.

gdb/testsuite/ChangeLog:
2017-08-21  Pedro Alves  <palves@redhat.com>

* gdb.base/dfp-test.c
(d32_t, d64_t, d128_t, d32_t2, d64_t2, d128_t2, v_d32_t, v_d64_t)
(v_d128_t, v_d32_t2, v_d64_t2, v_d128_t2): New.
* gdb.base/dfp-test.exp: Add whatis/ptype/cast tests.
* gdb.base/gnu_vector.exp: Add whatis/ptype/cast tests.
* gdb.base/whatis-ptype-typedefs.c: New.
* gdb.base/whatis-ptype-typedefs.exp: New.
* gdb.python/py-prettyprint.c (int_type, int_type2): New typedefs.
(an_int, an_int_type, an_int_type2): New globals.
* gdb.python/py-prettyprint.exp (run_lang_tests): Add tests
involving typedefs and cast expressions.
* gdb.python/py-prettyprint.py (class pp_int_typedef): New.
(lookup_typedefs_function): New.
(typedefs_pretty_printers_dict): New.
(top level): Register lookup_typedefs_function in
gdb.pretty_printers.

13 files changed:
gdb/ChangeLog
gdb/eval.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/dfp-test.c
gdb/testsuite/gdb.base/dfp-test.exp
gdb/testsuite/gdb.base/gnu_vector.exp
gdb/testsuite/gdb.base/whatis-ptype-typedefs.c [new file with mode: 0644]
gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp [new file with mode: 0644]
gdb/testsuite/gdb.python/py-prettyprint.c
gdb/testsuite/gdb.python/py-prettyprint.exp
gdb/testsuite/gdb.python/py-prettyprint.py
gdb/typeprint.c
gdb/valops.c

index 7ad3f4c5e04246caa05effc01346fd9fa4ff8ee7..fae715041cbcb2136ddd8e2a455023506e683d18 100644 (file)
@@ -1,3 +1,13 @@
+2017-08-21  Pedro Alves  <palves@redhat.com>
+
+       * eval.c (evaluate_subexp_standard) <OP_TYPE>: Don't dig past
+       typedefs.
+       * typeprint.c (whatis_exp): If handling "whatis", and expression
+       is OP_TYPE, strip one typedef level.  Otherwise don't strip
+       typedefs here.
+       * valops.c (value_cast): Save "to" type before resolving
+       stubs/typedefs.  Use that type as resulting value's type.
+
 2017-08-18  Tom Tromey  <tom@tromey.com>
            Pedro Alves  <palves@redhat.com>
 
index 2a39774cfd979e303c6ddaede28e6f1f01a7b328..80dfb2e38d8b2bcd7f345ed18880aa81bcc97199 100644 (file)
@@ -2727,18 +2727,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
         goto nosideret;
       else if (noside == EVAL_AVOID_SIDE_EFFECTS)
-       {
-         struct type *type = exp->elts[pc + 1].type;
-
-         /* If this is a typedef, then find its immediate target.  We
-            use check_typedef to resolve stubs, but we ignore its
-            result because we do not want to dig past all
-            typedefs.  */
-         check_typedef (type);
-         if (TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
-           type = TYPE_TARGET_TYPE (type);
-         return allocate_value (type);
-       }
+       return allocate_value (exp->elts[pc + 1].type);
       else
         error (_("Attempt to use a type name as an expression"));
 
index d188f83cca489193b6145d8a9b41474d0864d13c..03e825e046201f2e0edb11e53c7fbcd5c0c42390 100644 (file)
@@ -1,3 +1,22 @@
+2017-08-21  Pedro Alves  <palves@redhat.com>
+
+       * gdb.base/dfp-test.c
+       (d32_t, d64_t, d128_t, d32_t2, d64_t2, d128_t2, v_d32_t, v_d64_t)
+       (v_d128_t, v_d32_t2, v_d64_t2, v_d128_t2): New.
+       * gdb.base/dfp-test.exp: Add whatis/ptype/cast tests.
+       * gdb.base/gnu_vector.exp: Add whatis/ptype/cast tests.
+       * gdb.base/whatis-ptype-typedefs.c: New.
+       * gdb.base/whatis-ptype-typedefs.exp: New.
+       * gdb.python/py-prettyprint.c (int_type, int_type2): New typedefs.
+       (an_int, an_int_type, an_int_type2): New globals.
+       * gdb.python/py-prettyprint.exp (run_lang_tests): Add tests
+       involving typedefs and cast expressions.
+       * gdb.python/py-prettyprint.py (class pp_int_typedef): New.
+       (lookup_typedefs_function): New.
+       (typedefs_pretty_printers_dict): New.
+       (top level): Register lookup_typedefs_function in
+       gdb.pretty_printers.
+
 2017-08-18  Yao Qi  <yao.qi@linaro.org>
 
        * gdb.server/unittest.exp: New.
index 3af2b4de0972ba5a6b0fe233349b388cb0d369b9..a184acb3949921985bd80401943b836e86157052 100644 (file)
@@ -91,6 +91,23 @@ volatile _Decimal32 d32;
 volatile _Decimal64 d64;
 volatile _Decimal128 d128;
 
+/* Typedefs and typedefs of typedefs, for ptype/whatis testing.  */
+typedef _Decimal32 d32_t;
+typedef _Decimal64 d64_t;
+typedef _Decimal128 d128_t;
+
+typedef d32_t d32_t2;
+typedef d64_t d64_t2;
+typedef d128_t d128_t2;
+
+d32_t v_d32_t;
+d64_t v_d64_t;
+d128_t v_d128_t;
+
+d32_t2 v_d32_t2;
+d64_t2 v_d64_t2;
+d128_t2 v_d128_t2;
+
 struct decstruct
 {
   int int4;
index 5f7b13d149e0dde45e7153a4a6b9fc7195c858ca..c3a51a4a5444b587c16b53b512909d97cd47b48a 100644 (file)
@@ -274,6 +274,10 @@ gdb_test "ptype d64 + ds.dec32" " = volatile _Decimal64"
 gdb_test "ptype d128 + ds.dec32" " = volatile _Decimal128"
 gdb_test "ptype d128 + ds.dec64" " = volatile _Decimal128"
 
+gdb_test "whatis d64 + ds.dec32" " = volatile _Decimal64"
+gdb_test "whatis d128 + ds.dec32" " = volatile _Decimal128"
+gdb_test "whatis d128 + ds.dec64" " = volatile _Decimal128"
+
 # Mixture of Decimal and integral operands
 gdb_test "p d32 + 1" " = 1.1"
 gdb_test "p 2 + d64" " = 2.1"
@@ -331,3 +335,58 @@ gdb_test "print ds.dec128 = -ds.double8" " = 0.(0999.*|1000.*)"
 gdb_test "print ds.dec128 = ds.dec32" " = -0.1"
 gdb_test "print ds.dec32 = ds.int4" " = 1"
 gdb_test "print ds.int4 = 7.3dl" " = 7"
+
+# Test "whatis"/"ptype" of expressions involving casts to/from dfp
+# typedefs.
+
+# This list is composed by sub-lists, and their elements are (in
+# order):
+#
+# - Type to cast to.  This is also what "whatis" should print.
+# - What "ptype" should print.
+
+# Columns in the sublists represent:
+     # to/whatis   # ptype
+foreach elem {
+    {"_Decimal32"  "_Decimal32"}
+    {"_Decimal64"  "_Decimal64"}
+    {"_Decimal128" "_Decimal128"}
+    {"d32_t"       "_Decimal32"}
+    {"d64_t"       "_Decimal64"}
+    {"d128_t"      "_Decimal128"}
+    {"d32_t2"      "_Decimal32"}
+    {"d64_t2"      "_Decimal64"}
+    {"d128_t2"     "_Decimal128"}
+} {
+    set type [lindex $elem 0]
+    set ptype [lindex $elem 1]
+    gdb_test "whatis ($type) 0" " = $type"
+    gdb_test "ptype ($type) 0" " = $ptype"
+}
+
+# Test:
+# - whatis/ptype of variables of typedef type.
+# - whatis/ptype of typedef type names.
+# - whatis/ptype of typedef-of-typedef type names.
+
+# Columns in the sublists represent:
+    # Type name    # whatis      # ptype
+foreach elem {
+    {"v_d32_t"     "d32_t"       "_Decimal32"}
+    {"v_d64_t"     "d64_t"       "_Decimal64"}
+    {"v_d128_t"    "d128_t"      "_Decimal128"}
+
+    {"d32_t"       "_Decimal32"  "_Decimal32"}
+    {"d64_t"       "_Decimal64"  "_Decimal64"}
+    {"d128_t"      "_Decimal128" "_Decimal128"}
+
+    {"d32_t2"      "d32_t"       "_Decimal32"}
+    {"d64_t2"      "d64_t"       "_Decimal64"}
+    {"d128_t2"     "d128_t"      "_Decimal128"}
+} {
+    set type [lindex $elem 0]
+    set whatis [lindex $elem 1]
+    set ptype [lindex $elem 2]
+    gdb_test "whatis $type" " = $whatis"
+    gdb_test "ptype $type" " = $ptype"
+}
index 44b1405107a1d57a4a5255a01d1541b913ca387a..dac1714b44988764c9349308c655a40fa0ea4201 100644 (file)
@@ -95,6 +95,17 @@ gdb_test "print -f4a" "\\\$$decimal = \\{-2, -4, -8, -16\\}"
 gdb_test "print (char4) 0x01010101" "\\\$$decimal = \\{1, 1, 1, 1\\}"
 gdb_test "print (int2) lla" "\\\$$decimal = \\{1, 1\\}"
 
+# Check that "whatis" doesn't peel off the destination type's typedef
+# by mistake, in expressions that involve a cast to typedef type.
+gdb_test "whatis (char4) 0x01010101" "type = char4"
+gdb_test "whatis (int2) lla" "type = int2"
+# Check that OTOH "ptype" does peel off the destination type's
+# typedef.
+gdb_test "ptype (char4) 0x01010101" \
+    "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "ptype (int2) lla" \
+    "type = int __attribute__ \\(\\(vector_size\\(2\\)\\)\\)"
+
 if { ![string compare $endian big] } then {
     gdb_test "print (char4) ia" "\\\$$decimal = \\{0, 0, 0, 2\\}"
 } else {
@@ -167,16 +178,30 @@ gdb_test "print (double2) f2" "Cannot convert between vector values of different
 gdb_test "print (int4) c4" "Cannot convert between vector values of different sizes"
 gdb_test "print (char4) i4a" "Cannot convert between vector values of different sizes"
 
-# Test ptype on vector types.
+# Test ptype/whatis on vector types/vars.
 gdb_test "ptype c4" "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis c4" "type = char4"
+
 gdb_test "ptype char4" "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis char4" "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+
 gdb_test "ptype i4a" "type = int __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis i4a" "type = int4"
+
 gdb_test "ptype int4" "type = int __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis int4" "type = int __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+
 gdb_test "ptype f4b" "type = float __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis f4b" "type = float4"
+
 gdb_test "ptype float4" "type = float __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis float4" "type = float __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
 
 gdb_test "ptype union_with_vector_1" "type = union {\r\n\[\t \]+int i;\r\n\[\t \]+char cv __attribute__ \\(\\(vector_size\\(4\\)\\)\\);\r\n}"
+gdb_test "whatis union_with_vector_1" {type = union {...}}
+
 gdb_test "ptype struct_with_vector_1" "type = struct {\r\n\[\t \]+int i;\r\n\[\t \]+char cv __attribute__ \\(\\(vector_size\\(4\\)\\)\\);\r\n\[\t \]+float4 f4;\r\n}"
+gdb_test "whatis struct_with_vector_1" {type = struct {...}}
 
 # Test inferior function calls with vector arguments and/or vector
 # return values.
diff --git a/gdb/testsuite/gdb.base/whatis-ptype-typedefs.c b/gdb/testsuite/gdb.base/whatis-ptype-typedefs.c
new file mode 100644 (file)
index 0000000..5711a96
--- /dev/null
@@ -0,0 +1,143 @@
+/* This test program is part of GDB, the GNU debugger.
+
+   Copyright 2017 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Define typedefs of different types, for testing the "whatis" and
+   "ptype" commands.  */
+
+/* Helper macro used to consistently define variables/typedefs using
+   the same name scheme.  BASE is the shared part of the name of all
+   typedefs/variables generated.  Defines a variable of the given
+   typedef type, and then a typedef of that typedef and a variable of
+   that new typedef type.  The "double typedef" is useful to checking
+   that whatis only strips one typedef level.  For example, if BASE is
+   "int", we get:
+
+  int_typedef v_int_typedef; // "v_" stands for variable of typedef type
+  typedef int_typedef int_typedef2; // typedef-of-typedef
+  int_typedef2 v_int_typedef2; // var of typedef-of-typedef
+*/
+#define DEF(base)                              \
+  base ## _typedef v_ ## base ## _typedef;     \
+                                               \
+  typedef base ## _typedef base ## _typedef2;  \
+  base ## _typedef2 v_ ## base ## _typedef2
+
+/* Void.  */
+
+/* (Can't have variables of void type.)  */
+
+typedef void void_typedef;
+typedef void_typedef void_typedef2;
+
+void_typedef *v_void_typedef_ptr;
+void_typedef2 *v_void_typedef_ptr2;
+
+/* Integers.  */
+
+typedef int int_typedef;
+DEF (int);
+
+/* Floats.  */
+
+typedef float float_typedef;
+DEF (float);
+
+/* Enums.  */
+
+typedef enum colors {red, green, blue} colors_typedef;
+DEF (colors);
+
+/* Structures.  */
+
+typedef struct t_struct
+{
+  int member;
+} t_struct_typedef;
+DEF (t_struct);
+
+/* Unions.  */
+
+typedef union t_union
+{
+  int member;
+} t_union_typedef;
+DEF (t_union);
+
+/* Arrays.  */
+
+typedef int int_array_typedef[3];
+DEF (int_array);
+
+/* An array the same size of t_struct_typedef, so we can test casting.  */
+typedef unsigned char uchar_array_t_struct_typedef[sizeof (t_struct_typedef)];
+DEF (uchar_array_t_struct);
+
+/* A struct and a eunion the same size as t_struct, so we can test
+   casting.  */
+
+typedef struct t_struct_wrapper
+{
+  struct t_struct base;
+} t_struct_wrapper_typedef;
+DEF (t_struct_wrapper);
+
+typedef union t_struct_union_wrapper
+{
+  struct t_struct base;
+} t_struct_union_wrapper_typedef;
+DEF (t_struct_union_wrapper);
+
+/* Functions / function pointers.  */
+
+typedef void func_ftype (void);
+func_ftype *v_func_ftype;
+
+typedef func_ftype func_ftype2;
+func_ftype2 *v_func_ftype2;
+
+/* C++ methods / method pointers.  */
+
+#ifdef __cplusplus
+
+namespace ns {
+
+struct Struct { void method (); };
+void Struct::method () {}
+
+typedef Struct Struct_typedef;
+DEF (Struct);
+
+/* Typedefs/vars in a namespace.  */
+typedef void (Struct::*method_ptr_typedef) ();
+DEF (method_ptr);
+
+}
+
+/* Similar, but in the global namespace.  */
+typedef ns::Struct ns_Struct_typedef;
+DEF (ns_Struct);
+
+typedef void (ns::Struct::*ns_method_ptr_typedef) ();
+DEF (ns_method_ptr);
+
+#endif
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp b/gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp
new file mode 100644 (file)
index 0000000..d333d81
--- /dev/null
@@ -0,0 +1,272 @@
+# Copyright 2017 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test "whatis"/"ptype" of different typedef types, and of expressions
+# involving casts to/from different typedefs.
+#
+# Particularly, when "whatis" is given a type name directly, it should
+# strip one (and only one) typedef level.  Otherwise, it should not
+# strip any typedef at all.  GDB used to incorrectly strip typedefs of
+# expressions involving casts to typedef types.  E.g., (gdb) print
+# (int_typedef)0" shall result in a value of type "int_typedef", not
+# "int".
+
+standard_testfile
+
+# Prepare for testing in language LANG.  Lang can be "c" or "c++".
+
+proc prepare {lang} {
+    global srcfile testfile
+
+    if [target_info exists no_long_long] {
+       set options [list debug additional_flags=-DNO_LONG_LONG]
+    } else {
+       set options [list debug]
+    }
+
+    if {$lang == "c++"} {
+       lappend options c++
+       set out $testfile-cxx
+    } else {
+       set out $testfile-c
+    }
+
+    if { [prepare_for_testing "failed to prepare" \
+             ${out} [list $srcfile] $options] } {
+       return -1
+    }
+
+    if ![runto_main] then {
+       fail "can't run to main"
+       return 0
+    }
+}
+
+# The following list is layed out as a table.  It is composed by
+# sub-lists (lines), with each line representing one whatis/ptype
+# test.  The sub-list (line) elements (columns) are (in order):
+#
+# EXP - The user expression passed to whatis/ptype.
+#
+# WHATIS - What "whatis" should print.
+#
+# If the EXP column is a type name, then this will be the same type,
+# with one (and only one) typedef level removed.  Otherwise, this is
+# the type of the expression on the first column, with all typedefs
+# preserved.
+#
+# PTYPE - What "ptype" should print.
+#
+# This is always the type of the input type/expression stripped from
+# all typedefs.
+#
+# LANGUAGE - If the line is language-specific, which language.
+#
+# This can be "c" or "c++".
+#
+# Columns in the table represent:
+     # EXP                # whatis           # ptype           # language
+set table {
+    {"void_typedef"       "void"              "void"}
+    {"void_typedef2"      "void_typedef"      "void"}
+
+    {"int_typedef"        "int"              "int"}
+    {"int_typedef2"       "int_typedef"      "int"}
+    {"v_int_typedef"      "int_typedef"      "int"}
+    {"v_int_typedef2"     "int_typedef2"     "int"}
+
+    {"float_typedef"      "float"            "float"}
+    {"float_typedef2"     "float_typedef"    "float"}
+    {"v_float_typedef"    "float_typedef"    "float"}
+    {"v_float_typedef2"   "float_typedef2"   "float"}
+
+    {"colors_typedef"     "(enum )?colors"   "enum colors( : unsigned int)? {red, green, blue}"}
+    {"colors_typedef2"    "colors_typedef"   "enum colors( : unsigned int)? {red, green, blue}"}
+    {"v_colors_typedef"   "colors_typedef"   "enum colors( : unsigned int)? {red, green, blue}"}
+    {"v_colors_typedef2"  "colors_typedef2"  "enum colors( : unsigned int)? {red, green, blue}"}
+
+    {"func_ftype"         "void \\(void\\)"  "void \\(void\\)"}
+    {"func_ftype2"        "func_ftype"       "void \\(void\\)"}
+
+    {"func_ftype *"       "func_ftype \\*"   "void \\(\\*\\)\\(void\\)"}
+    {"func_ftype2 *"      "func_ftype2 \\*"  "void \\(\\*\\)\\(void\\)"}
+    {"v_func_ftype"       "func_ftype \\*"   "void \\(\\*\\)\\(void\\)"}
+    {"v_func_ftype2"      "func_ftype2 \\*"  "void \\(\\*\\)\\(void\\)"}
+
+    {"v_t_struct_typedef"                "t_struct_typedef"                "struct t_struct {.* member;.*}"}
+    {"v_t_struct_typedef2"               "t_struct_typedef2"               "struct t_struct {.* member;.*}"}
+    {"v_t_struct_union_wrapper_typedef"  "t_struct_union_wrapper_typedef"  "union t_struct_union_wrapper {.*base;.*}"}
+    {"v_t_struct_union_wrapper_typedef2" "t_struct_union_wrapper_typedef2" "union t_struct_union_wrapper {.*base;.*}"}
+    {"v_uchar_array_t_struct_typedef"    "uchar_array_t_struct_typedef"    "unsigned char \\[.*\\]"}
+    {"v_uchar_array_t_struct_typedef2"   "uchar_array_t_struct_typedef2"   "unsigned char \\[.*\\]"}
+
+    {"v_ns_Struct_typedef"               "ns_Struct_typedef"                "struct ns::Struct {.* method.*}"   "c++"}
+
+    {"ns_method_ptr_typedef"
+       "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+       "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+       "c++"}
+
+    {"ns::method_ptr_typedef"
+       "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+       "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+       "c++"}
+
+    {"ns_method_ptr_typedef2"
+       "ns_method_ptr_typedef"
+       "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+       "c++"}
+
+    {"ns::method_ptr_typedef2"
+       "ns::method_ptr_typedef"
+       "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+       "c++"}
+
+    {"ns::Struct::method"
+       "void \\(ns::Struct \\* const\\)"
+       "void \\(ns::Struct \\* const\\)"
+       "c++"}
+}
+
+# The 4th column above is optional.  If present, it indicates that the
+# line should only be tested in the specified language.  This is a
+# helper function that checks whether LINE's language matches LANG.
+proc line_lang_match {line lang} {
+    if {[llength $line] <= 3} {
+       return true
+    }
+
+    set line_lang [lindex $line 3]
+    if {$line_lang == "" || $lang == $line_lang} {
+       return true
+    }
+
+    return false
+}
+
+# Run tests in language LANG.
+
+proc run_tests {lang} {
+    global table
+    global gdb_prompt
+
+    # Test passing all EXP in the list/table above to whatis/ptype,
+    # and check what comes out.
+    with_test_prefix "whatis/ptype" {
+       foreach line $table {
+           set type [lindex $line 0]
+           set whatis [lindex $line 1]
+           set ptype [lindex $line 2]
+
+           if {![line_lang_match $line $lang]} {
+               continue
+           }
+
+           # GCC doesn't record the target type of "typedef of
+           # typedef of void" types in the DWARF.  See
+           # <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81267>.
+           # Handle that case manually in order to be able to xfail
+           # it.
+           if {$type == "void_typedef2"} {
+               set test "whatis $type"
+               gdb_test_multiple $test $test {
+                   -re "type = void\r\n$gdb_prompt $" {
+                       # gcc/81267.
+                       setup_xfail "*-*-*"
+                       fail "$test (void)"
+                   }
+                   -re "type = void_typedef\r\n$gdb_prompt $" {
+                       pass $test
+                   }
+               }
+           } else {
+               gdb_test "whatis $type" "type = $whatis"
+           }
+
+           gdb_test "ptype $type" "type = $ptype"
+       }
+    }
+
+    # Test converting/casting all variables in the first column of the
+    # table to all types (found in the first column of the table).
+    # The aggregates are all defined to be the same size so that
+    # casting actually works.  (GDB's casting operator is more general
+    # than a C cast.)
+    #
+    # The main idea here is testing all the different paths in the
+    # value casting code in GDB (value_cast), making sure typedefs are
+    # preserved.
+    with_test_prefix "cast" {
+       foreach line1 $table {
+           set from [lindex $line1 0]
+
+           if {![line_lang_match $line1 $lang]} {
+               continue
+           }
+
+           foreach line2 $table {
+               set to [lindex $line2 0]
+               set whatis [lindex $line2 1]
+               set ptype [lindex $line2 2]
+
+               if {![line_lang_match $line2 $lang]} {
+                   continue
+               }
+
+               # We try all combinations, even those that don't
+               # parse, or are invalid, to catch the case of a
+               # regression making them inadvertently valid.  For
+               # example, these convertions are invalid:
+               #
+               #  float <-> array
+               #  array -> function (not function pointer)
+               #  array -> member_ptr
+               #
+               # while these are invalid syntax:
+               #
+               #  (anything) type
+               #  (var) anything
+               #  (method) anything   [not method pointer]
+               #  (float) method
+               #
+               if {([string match "v_*" $to]
+                    || (![string match "v_*" $from] && ![string match "*method" $from])
+                    || [string match "*method" $to])} {
+                   gdb_test "whatis ($to) $from" "syntax error.*" "whatis ($to) $from (syntax)"
+                   gdb_test "ptype ($to) $from" "syntax error.*" "ptype ($to) $from (syntax)"
+               } elseif {([string match "*float*" $from] && [string match "*array*" $to])
+                         || ([string match "float*" $to] && [string match "*array*" $from])
+                         || ([string match "float*" $to] && [string match "*method" $from])
+                         || ([string match "*ftype" $to] && [string match "*array*" $from])
+                         || ([string match "*ftype2" $to] && [string match "*array*" $from])
+                         || ([string match "*ftype" $to] && [string match "*method" $from])
+                         || ([string match "*ftype2" $to] && [string match "*method" $from])
+                         || ([string match "*method_ptr*" $to] && [string match "*method" $from])
+                         || ([string match "*method_ptr*" $to] && [string match "*array*" $from])} {
+                   gdb_test "whatis ($to) $from" "Invalid cast." "whatis ($to) $from (invalid)"
+                   gdb_test "ptype ($to) $from" "Invalid cast." "ptype ($to) $from (invalid)"
+               } else {
+                   gdb_test "whatis ($to) $from" "type = [string_to_regexp $to]"
+                   gdb_test "ptype ($to) $from" "type = $ptype"
+               }
+           }
+       }
+    }
+}
+
+foreach_with_prefix lang {"c" "c++"} {
+    prepare $lang
+    run_tests $lang
+}
index fd58358d9a69d5426d4e600efa37b0bb99e80e74..82f9fe7676d860d7b23371f8c2ce27f84770ff52 100644 (file)
@@ -257,6 +257,15 @@ bug_14741()
   set_item(&c, 0, 5);
 }
 
+/* Some typedefs/variables for checking that GDB doesn't lose typedefs
+   when looking for a printer.  */
+typedef int int_type;
+typedef int_type int_type2;
+
+int an_int = -1;
+int_type an_int_type = 1;
+int_type2 an_int_type2 = 2;
+
 int
 main ()
 {
index b0a9e32df6af6f2ea0dc79bc8e05a02af646a0bd..02300e9ecd430410fe232366a81a87f6ebad3e7e 100644 (file)
@@ -110,6 +110,19 @@ proc run_lang_tests {exefile lang} {
     gdb_test "print nstype" " = {.0. = 7, .1. = 42}" \
        "print nstype on one line"
 
+    # Check that GDB doesn't lose typedefs when looking for a printer.
+    gdb_test "print an_int" " = -1"
+    gdb_test "print (int) an_int" " = -1"
+    gdb_test "print (int_type) an_int" " = type=int_type, val=-1"
+
+    gdb_test "print an_int_type" " = type=int_type, val=1"
+    gdb_test "print (int_type) an_int_type" " = type=int_type, val=1"
+
+    gdb_test "print an_int_type2" " = type=int_type2, val=2"
+    gdb_test "print (int) an_int_type2" " = 2"
+    gdb_test "print (int_type) an_int_type2" " = type=int_type, val=2"
+    gdb_test "print (int_type2) an_int_type2" " = type=int_type2, val=2"
+
     gdb_continue_to_end
 }
 
index c56f5643bc7152941aeef2bcec02d00438576926..ec845e46575aa31779a5c95a1769cce4d509fdf8 100644 (file)
@@ -227,6 +227,13 @@ class pp_eval_type (object):
         gdb.execute("bt", to_string=True)
         return "eval=<" + str(gdb.parse_and_eval("eval_func (123456789, 2, 3, 4, 5, 6, 7, 8)")) + ">"
 
+class pp_int_typedef (object):
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        return "type=%s, val=%s" % (self.val.type, int(self.val))
+
 def lookup_function (val):
     "Look-up and return a pretty-printer that can print val."
 
@@ -263,6 +270,26 @@ def disable_lookup_function ():
 def enable_lookup_function ():
     lookup_function.enabled = True
 
+# Lookup a printer for VAL in the typedefs dict.
+def lookup_typedefs_function (val):
+    "Look-up and return a pretty-printer that can print val (typedefs)."
+
+    # Get the type.
+    type = val.type
+
+    if type == None or type.name == None or type.code != gdb.TYPE_CODE_TYPEDEF:
+        return None
+
+    # Iterate over local dictionary of typedef types to determine if a
+    # printer is registered for that type.  Return an instantiation of
+    # the printer if found.
+    for function in typedefs_pretty_printers_dict:
+        if function.match (type.name):
+            return typedefs_pretty_printers_dict[function] (val)
+
+    # Cannot find a pretty printer.
+    return None
+
 def register_pretty_printers ():
     pretty_printers_dict[re.compile ('^struct s$')]   = pp_s
     pretty_printers_dict[re.compile ('^s$')]   = pp_s
@@ -309,7 +336,14 @@ def register_pretty_printers ():
 
     pretty_printers_dict[re.compile ('^eval_type_s$')] = pp_eval_type
 
+    typedefs_pretty_printers_dict[re.compile ('^int_type$')] = pp_int_typedef
+    typedefs_pretty_printers_dict[re.compile ('^int_type2$')] = pp_int_typedef
+
+# Dict for struct types with typedefs fully stripped.
 pretty_printers_dict = {}
+# Dict for typedef types.
+typedefs_pretty_printers_dict = {}
 
 register_pretty_printers ()
 gdb.pretty_printers.append (lookup_function)
+gdb.pretty_printers.append (lookup_typedefs_function)
index fc687d3fd50a10008931f6dd9883b38cb6f8597f..045271a8074883d00c86da26b135569b713c3d13 100644 (file)
@@ -443,12 +443,40 @@ whatis_exp (char *exp, int show)
        }
 
       expression_up expr = parse_expression (exp);
-      val = evaluate_type (expr.get ());
+
+      /* The behavior of "whatis" depends on whether the user
+        expression names a type directly, or a language expression
+        (including variable names).  If the former, then "whatis"
+        strips one level of typedefs, only.  If an expression,
+        "whatis" prints the type of the expression without stripping
+        any typedef level.  "ptype" always strips all levels of
+        typedefs.  */
+      if (show == -1 && expr->elts[0].opcode == OP_TYPE)
+       {
+         /* The user expression names a type directly.  */
+         type = expr->elts[1].type;
+
+         /* If this is a typedef, then find its immediate target.
+            Use check_typedef to resolve stubs, but ignore its result
+            because we do not want to dig past all typedefs.  */
+         check_typedef (type);
+         if (TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
+           type = TYPE_TARGET_TYPE (type);
+       }
+      else
+       {
+         /* The user expression names a type indirectly by naming an
+            object or expression of that type.  Find that
+            indirectly-named type.  */
+         val = evaluate_type (expr.get ());
+         type = value_type (val);
+       }
     }
   else
-    val = access_value_history (0);
-
-  type = value_type (val);
+    {
+      val = access_value_history (0);
+      type = value_type (val);
+    }
 
   get_user_print_options (&opts);
   if (opts.objectprint)
index 3668f0b4bc20da91870aaff4f60f09f0e88748f2..c1bb93776ad645b2350e505d8354ce7a2c0d7cac 100644 (file)
@@ -379,6 +379,11 @@ value_cast (struct type *type, struct value *arg2)
     /* We deref the value and then do the cast.  */
     return value_cast (type, coerce_ref (arg2)); 
 
+  /* Strip typedefs / resolve stubs in order to get at the type's
+     code/length, but remember the original type, to use as the
+     resulting type of the cast, in case it was a typedef.  */
+  struct type *to_type = type;
+
   type = check_typedef (type);
   code1 = TYPE_CODE (type);
   arg2 = coerce_ref (arg2);
@@ -434,7 +439,7 @@ value_cast (struct type *type, struct value *arg2)
   code2 = TYPE_CODE (type2);
 
   if (code1 == TYPE_CODE_COMPLEX)
-    return cast_into_complex (type, arg2);
+    return cast_into_complex (to_type, arg2);
   if (code1 == TYPE_CODE_BOOL)
     {
       code1 = TYPE_CODE_INT;
@@ -453,14 +458,14 @@ value_cast (struct type *type, struct value *arg2)
       && (code2 == TYPE_CODE_STRUCT || code2 == TYPE_CODE_UNION)
       && TYPE_NAME (type) != 0)
     {
-      struct value *v = value_cast_structs (type, arg2);
+      struct value *v = value_cast_structs (to_type, arg2);
 
       if (v)
        return v;
     }
 
   if (code1 == TYPE_CODE_FLT && scalar)
-    return value_from_double (type, value_as_double (arg2));
+    return value_from_double (to_type, value_as_double (arg2));
   else if (code1 == TYPE_CODE_DECFLOAT && scalar)
     {
       enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (type));
@@ -476,7 +481,7 @@ value_cast (struct type *type, struct value *arg2)
        /* The only option left is an integral type.  */
        decimal_from_integral (arg2, dec, dec_len, byte_order);
 
-      return value_from_decfloat (type, dec);
+      return value_from_decfloat (to_type, dec);
     }
   else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM
            || code1 == TYPE_CODE_RANGE)
@@ -497,7 +502,7 @@ value_cast (struct type *type, struct value *arg2)
                     gdbarch_byte_order (get_type_arch (type2)));
       else
         longest = value_as_long (arg2);
-      return value_from_longest (type, convert_to_boolean ?
+      return value_from_longest (to_type, convert_to_boolean ?
                                 (LONGEST) (longest ? 1 : 0) : longest);
     }
   else if (code1 == TYPE_CODE_PTR && (code2 == TYPE_CODE_INT  
@@ -523,14 +528,14 @@ value_cast (struct type *type, struct value *arg2)
              || longest <= -((LONGEST) 1 << addr_bit))
            warning (_("value truncated"));
        }
-      return value_from_longest (type, longest);
+      return value_from_longest (to_type, longest);
     }
   else if (code1 == TYPE_CODE_METHODPTR && code2 == TYPE_CODE_INT
           && value_as_long (arg2) == 0)
     {
-      struct value *result = allocate_value (type);
+      struct value *result = allocate_value (to_type);
 
-      cplus_make_method_ptr (type, value_contents_writeable (result), 0, 0);
+      cplus_make_method_ptr (to_type, value_contents_writeable (result), 0, 0);
       return result;
     }
   else if (code1 == TYPE_CODE_MEMBERPTR && code2 == TYPE_CODE_INT
@@ -538,7 +543,7 @@ value_cast (struct type *type, struct value *arg2)
     {
       /* The Itanium C++ ABI represents NULL pointers to members as
         minus one, instead of biasing the normal case.  */
-      return value_from_longest (type, -1);
+      return value_from_longest (to_type, -1);
     }
   else if (code1 == TYPE_CODE_ARRAY && TYPE_VECTOR (type)
           && code2 == TYPE_CODE_ARRAY && TYPE_VECTOR (type2)
@@ -549,21 +554,21 @@ value_cast (struct type *type, struct value *arg2)
     error (_("can only cast scalar to vector of same size"));
   else if (code1 == TYPE_CODE_VOID)
     {
-      return value_zero (type, not_lval);
+      return value_zero (to_type, not_lval);
     }
   else if (TYPE_LENGTH (type) == TYPE_LENGTH (type2))
     {
       if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR)
-       return value_cast_pointers (type, arg2, 0);
+       return value_cast_pointers (to_type, arg2, 0);
 
       arg2 = value_copy (arg2);
-      deprecated_set_value_type (arg2, type);
-      set_value_enclosing_type (arg2, type);
+      deprecated_set_value_type (arg2, to_type);
+      set_value_enclosing_type (arg2, to_type);
       set_value_pointed_to_offset (arg2, 0);   /* pai: chk_val */
       return arg2;
     }
   else if (VALUE_LVAL (arg2) == lval_memory)
-    return value_at_lazy (type, value_address (arg2));
+    return value_at_lazy (to_type, value_address (arg2));
   else
     {
       error (_("Invalid cast."));
This page took 0.047821 seconds and 4 git commands to generate.