From c973d0aa4a2c737ab527ae44a617f1c357e07364 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Mon, 21 Aug 2017 11:34:32 +0100 Subject: [PATCH] Fix type casts losing typedefs and reimplement "whatis" typedef stripping (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 * eval.c (evaluate_subexp_standard) : 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 * 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. --- gdb/ChangeLog | 10 + gdb/eval.c | 13 +- gdb/testsuite/ChangeLog | 19 ++ 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.base/whatis-ptype-typedefs.c | 143 +++++++++ .../gdb.base/whatis-ptype-typedefs.exp | 272 ++++++++++++++++++ gdb/testsuite/gdb.python/py-prettyprint.c | 9 + gdb/testsuite/gdb.python/py-prettyprint.exp | 13 + gdb/testsuite/gdb.python/py-prettyprint.py | 34 +++ gdb/typeprint.c | 36 ++- gdb/valops.c | 33 ++- 13 files changed, 654 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/ChangeLog b/gdb/ChangeLog index 7ad3f4c5e04..fae715041cb 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2017-08-21 Pedro Alves + + * eval.c (evaluate_subexp_standard) : 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 Pedro Alves diff --git a/gdb/eval.c b/gdb/eval.c index 2a39774cfd9..80dfb2e38d8 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/ChangeLog b/gdb/testsuite/ChangeLog index d188f83cca4..03e825e0462 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,22 @@ +2017-08-21 Pedro Alves + + * 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 * gdb.server/unittest.exp: New. diff --git a/gdb/testsuite/gdb.base/dfp-test.c b/gdb/testsuite/gdb.base/dfp-test.c index 3af2b4de097..a184acb3949 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 5f7b13d149e..c3a51a4a544 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 44b1405107a..dac1714b449 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 00000000000..5711a962583 --- /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 . */ + +/* 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 00000000000..d333d812384 --- /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 . + +# 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 + # . + # 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/testsuite/gdb.python/py-prettyprint.c b/gdb/testsuite/gdb.python/py-prettyprint.c index fd58358d9a6..82f9fe7676d 100644 --- a/gdb/testsuite/gdb.python/py-prettyprint.c +++ b/gdb/testsuite/gdb.python/py-prettyprint.c @@ -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 () { diff --git a/gdb/testsuite/gdb.python/py-prettyprint.exp b/gdb/testsuite/gdb.python/py-prettyprint.exp index b0a9e32df6a..02300e9ecd4 100644 --- a/gdb/testsuite/gdb.python/py-prettyprint.exp +++ b/gdb/testsuite/gdb.python/py-prettyprint.exp @@ -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 } diff --git a/gdb/testsuite/gdb.python/py-prettyprint.py b/gdb/testsuite/gdb.python/py-prettyprint.py index c56f5643bc7..ec845e46575 100644 --- a/gdb/testsuite/gdb.python/py-prettyprint.py +++ b/gdb/testsuite/gdb.python/py-prettyprint.py @@ -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) diff --git a/gdb/typeprint.c b/gdb/typeprint.c index fc687d3fd50..045271a8074 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 3668f0b4bc2..c1bb93776ad 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -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.")); -- 2.30.2