This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH] Fix type casts losing typedefs and reimplement "whatis" typedef stripping
- From: Pedro Alves <palves at redhat dot com>
- To: gdb-patches at sourceware dot org
- Date: Fri, 30 Jun 2017 16:48:19 +0100
- Subject: [PATCH] Fix type casts losing typedefs and reimplement "whatis" typedef stripping
- Authentication-results: sourceware.org; auth=none
- Authentication-results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com
- Authentication-results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=palves at redhat dot com
- Dkim-filter: OpenDKIM Filter v2.11.0 mx1.redhat.com B452EC0587F9
- Dmarc-filter: OpenDMARC Filter v1.3.2 mx1.redhat.com B452EC0587F9
(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:
yyyy-mm-dd 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:
yyyy-mm-dd 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.
* whatis-ptype-typedefs.c: New.
* gdb.base/whatis-ptype-typedefs.exp: New.
---
gdb/eval.c | 13 +-
gdb/testsuite/gdb.base/dfp-test.c | 17 ++
gdb/testsuite/gdb.base/dfp-test.exp | 59 +++++
gdb/testsuite/gdb.base/gnu_vector.exp | 27 ++-
gdb/testsuite/gdb.base/whatis-ptype-typedefs.c | 143 ++++++++++++
gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp | 272 +++++++++++++++++++++++
gdb/typeprint.c | 36 ++-
gdb/valops.c | 33 +--
8 files changed, 569 insertions(+), 31 deletions(-)
create mode 100644 gdb/testsuite/gdb.base/whatis-ptype-typedefs.c
create mode 100644 gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp
diff --git a/gdb/eval.c b/gdb/eval.c
index 2a39774..80dfb2e 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -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"));
diff --git a/gdb/testsuite/gdb.base/dfp-test.c b/gdb/testsuite/gdb.base/dfp-test.c
index 3af2b4d..a184acb 100644
--- a/gdb/testsuite/gdb.base/dfp-test.c
+++ b/gdb/testsuite/gdb.base/dfp-test.c
@@ -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;
diff --git a/gdb/testsuite/gdb.base/dfp-test.exp b/gdb/testsuite/gdb.base/dfp-test.exp
index 5f7b13d..c3a51a4 100644
--- a/gdb/testsuite/gdb.base/dfp-test.exp
+++ b/gdb/testsuite/gdb.base/dfp-test.exp
@@ -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"
+}
diff --git a/gdb/testsuite/gdb.base/gnu_vector.exp b/gdb/testsuite/gdb.base/gnu_vector.exp
index 44b1405..dac1714 100644
--- a/gdb/testsuite/gdb.base/gnu_vector.exp
+++ b/gdb/testsuite/gdb.base/gnu_vector.exp
@@ -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
index 0000000..5711a96
--- /dev/null
+++ b/gdb/testsuite/gdb.base/whatis-ptype-typedefs.c
@@ -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
index 0000000..d333d81
--- /dev/null
+++ b/gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp
@@ -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
+}
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index fc687d3..045271a 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -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)
diff --git a/gdb/valops.c b/gdb/valops.c
index 8675e6c..f3f5308 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -378,6 +378,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);
@@ -433,7 +438,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;
@@ -452,14 +457,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));
@@ -475,7 +480,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)
@@ -496,7 +501,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
@@ -522,14 +527,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
@@ -537,7 +542,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)
@@ -548,21 +553,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."));
--
2.5.5