| 1 | # Copyright 2017-2019 Free Software Foundation, Inc. |
| 2 | |
| 3 | # This program is free software; you can redistribute it and/or modify |
| 4 | # it under the terms of the GNU General Public License as published by |
| 5 | # the Free Software Foundation; either version 3 of the License, or |
| 6 | # (at your option) any later version. |
| 7 | # |
| 8 | # This program is distributed in the hope that it will be useful, |
| 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | # GNU General Public License for more details. |
| 12 | # |
| 13 | # You should have received a copy of the GNU General Public License |
| 14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 15 | |
| 16 | # Test "whatis"/"ptype" of different typedef types, and of expressions |
| 17 | # involving casts to/from different typedefs. |
| 18 | # |
| 19 | # Particularly, when "whatis" is given a type name directly, it should |
| 20 | # strip one (and only one) typedef level. Otherwise, it should not |
| 21 | # strip any typedef at all. GDB used to incorrectly strip typedefs of |
| 22 | # expressions involving casts to typedef types. E.g., (gdb) print |
| 23 | # (int_typedef)0" shall result in a value of type "int_typedef", not |
| 24 | # "int". |
| 25 | |
| 26 | standard_testfile |
| 27 | |
| 28 | # Prepare for testing in language LANG. Lang can be "c" or "c++". |
| 29 | |
| 30 | proc prepare {lang} { |
| 31 | global srcfile testfile |
| 32 | |
| 33 | if [target_info exists no_long_long] { |
| 34 | set options [list debug additional_flags=-DNO_LONG_LONG] |
| 35 | } else { |
| 36 | set options [list debug] |
| 37 | } |
| 38 | |
| 39 | if {$lang == "c++"} { |
| 40 | lappend options c++ |
| 41 | set out $testfile-cxx |
| 42 | } else { |
| 43 | set out $testfile-c |
| 44 | } |
| 45 | |
| 46 | if { [prepare_for_testing "failed to prepare" \ |
| 47 | ${out} [list $srcfile] $options] } { |
| 48 | return 0 |
| 49 | } |
| 50 | |
| 51 | if ![runto_main] then { |
| 52 | fail "can't run to main" |
| 53 | return 0 |
| 54 | } |
| 55 | |
| 56 | return 1 |
| 57 | } |
| 58 | |
| 59 | # The following list is layed out as a table. It is composed by |
| 60 | # sub-lists (lines), with each line representing one whatis/ptype |
| 61 | # test. The sub-list (line) elements (columns) are (in order): |
| 62 | # |
| 63 | # EXP - The user expression passed to whatis/ptype. |
| 64 | # |
| 65 | # WHATIS - What "whatis" should print. |
| 66 | # |
| 67 | # If the EXP column is a type name, then this will be the same type, |
| 68 | # with one (and only one) typedef level removed. Otherwise, this is |
| 69 | # the type of the expression on the first column, with all typedefs |
| 70 | # preserved. |
| 71 | # |
| 72 | # PTYPE - What "ptype" should print. |
| 73 | # |
| 74 | # This is always the type of the input type/expression stripped from |
| 75 | # all typedefs. |
| 76 | # |
| 77 | # LANGUAGE - If the line is language-specific, which language. |
| 78 | # |
| 79 | # This can be "c" or "c++". |
| 80 | # |
| 81 | # Columns in the table represent: |
| 82 | # EXP # whatis # ptype # language |
| 83 | set table { |
| 84 | {"void_typedef" "void" "void"} |
| 85 | {"void_typedef2" "void_typedef" "void"} |
| 86 | |
| 87 | {"int_typedef" "int" "int"} |
| 88 | {"int_typedef2" "int_typedef" "int"} |
| 89 | {"v_int_typedef" "int_typedef" "int"} |
| 90 | {"v_int_typedef2" "int_typedef2" "int"} |
| 91 | |
| 92 | {"float_typedef" "float" "float"} |
| 93 | {"float_typedef2" "float_typedef" "float"} |
| 94 | {"v_float_typedef" "float_typedef" "float"} |
| 95 | {"v_float_typedef2" "float_typedef2" "float"} |
| 96 | |
| 97 | {"double_typedef" "double" "double"} |
| 98 | {"double_typedef2" "double_typedef" "double"} |
| 99 | {"v_double_typedef" "double_typedef" "double"} |
| 100 | {"v_double_typedef2" "double_typedef2" "double"} |
| 101 | |
| 102 | {"long_double_typedef" "long double" "long double"} |
| 103 | {"long_double_typedef2" "long_double_typedef" "long double"} |
| 104 | {"v_long_double_typedef" "long_double_typedef" "long double"} |
| 105 | {"v_long_double_typedef2" "long_double_typedef2" "long double"} |
| 106 | |
| 107 | {"colors_typedef" "(enum )?colors" "enum colors( : unsigned int)? {red, green, blue}"} |
| 108 | {"colors_typedef2" "colors_typedef" "enum colors( : unsigned int)? {red, green, blue}"} |
| 109 | {"v_colors_typedef" "colors_typedef" "enum colors( : unsigned int)? {red, green, blue}"} |
| 110 | {"v_colors_typedef2" "colors_typedef2" "enum colors( : unsigned int)? {red, green, blue}"} |
| 111 | |
| 112 | {"func_ftype" "void \\(void\\)" "void \\(void\\)"} |
| 113 | {"func_ftype2" "func_ftype" "void \\(void\\)"} |
| 114 | |
| 115 | {"func_ftype *" "func_ftype \\*" "void \\(\\*\\)\\(void\\)"} |
| 116 | {"func_ftype2 *" "func_ftype2 \\*" "void \\(\\*\\)\\(void\\)"} |
| 117 | {"v_func_ftype" "func_ftype \\*" "void \\(\\*\\)\\(void\\)"} |
| 118 | {"v_func_ftype2" "func_ftype2 \\*" "void \\(\\*\\)\\(void\\)"} |
| 119 | |
| 120 | {"v_t_struct_typedef" "t_struct_typedef" "struct t_struct {.* member;.*}"} |
| 121 | {"v_t_struct_typedef2" "t_struct_typedef2" "struct t_struct {.* member;.*}"} |
| 122 | {"v_t_struct_union_wrapper_typedef" "t_struct_union_wrapper_typedef" "union t_struct_union_wrapper {.*base;.*}"} |
| 123 | {"v_t_struct_union_wrapper_typedef2" "t_struct_union_wrapper_typedef2" "union t_struct_union_wrapper {.*base;.*}"} |
| 124 | {"v_uchar_array_t_struct_typedef" "uchar_array_t_struct_typedef" "unsigned char \\[.*\\]"} |
| 125 | {"v_uchar_array_t_struct_typedef2" "uchar_array_t_struct_typedef2" "unsigned char \\[.*\\]"} |
| 126 | |
| 127 | {"v_ns_Struct_typedef" "ns_Struct_typedef" "struct ns::Struct {.* method.*}" "c++"} |
| 128 | |
| 129 | {"ns_method_ptr_typedef" |
| 130 | "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)" |
| 131 | "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)" |
| 132 | "c++"} |
| 133 | |
| 134 | {"ns::method_ptr_typedef" |
| 135 | "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)" |
| 136 | "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)" |
| 137 | "c++"} |
| 138 | |
| 139 | {"ns_method_ptr_typedef2" |
| 140 | "ns_method_ptr_typedef" |
| 141 | "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)" |
| 142 | "c++"} |
| 143 | |
| 144 | {"ns::method_ptr_typedef2" |
| 145 | "ns::method_ptr_typedef" |
| 146 | "void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)" |
| 147 | "c++"} |
| 148 | |
| 149 | {"ns::Struct::method" |
| 150 | "void \\(ns::Struct \\* const\\)" |
| 151 | "void \\(ns::Struct \\* const\\)" |
| 152 | "c++"} |
| 153 | } |
| 154 | |
| 155 | # The 4th column above is optional. If present, it indicates that the |
| 156 | # line should only be tested in the specified language. This is a |
| 157 | # helper function that checks whether LINE's language matches LANG. |
| 158 | proc line_lang_match {line lang} { |
| 159 | if {[llength $line] <= 3} { |
| 160 | return true |
| 161 | } |
| 162 | |
| 163 | set line_lang [lindex $line 3] |
| 164 | if {$line_lang == "" || $lang == $line_lang} { |
| 165 | return true |
| 166 | } |
| 167 | |
| 168 | return false |
| 169 | } |
| 170 | |
| 171 | # Run tests in language LANG. |
| 172 | |
| 173 | proc run_tests {lang} { |
| 174 | global table |
| 175 | global gdb_prompt |
| 176 | |
| 177 | # Test passing all EXP in the list/table above to whatis/ptype, |
| 178 | # and check what comes out. |
| 179 | with_test_prefix "whatis/ptype" { |
| 180 | foreach line $table { |
| 181 | set type [lindex $line 0] |
| 182 | set whatis [lindex $line 1] |
| 183 | set ptype [lindex $line 2] |
| 184 | |
| 185 | if {![line_lang_match $line $lang]} { |
| 186 | continue |
| 187 | } |
| 188 | |
| 189 | # GCC doesn't record the target type of "typedef of |
| 190 | # typedef of void" types in the DWARF. See |
| 191 | # <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81267>. |
| 192 | # Handle that case manually in order to be able to xfail |
| 193 | # it. |
| 194 | if {$type == "void_typedef2"} { |
| 195 | set test "whatis $type" |
| 196 | gdb_test_multiple $test $test { |
| 197 | -re "type = void\r\n$gdb_prompt $" { |
| 198 | # gcc/81267. |
| 199 | setup_xfail "*-*-*" |
| 200 | fail "$test (void)" |
| 201 | } |
| 202 | -re "type = void_typedef\r\n$gdb_prompt $" { |
| 203 | pass $test |
| 204 | } |
| 205 | } |
| 206 | } else { |
| 207 | gdb_test "whatis $type" "type = $whatis" |
| 208 | } |
| 209 | |
| 210 | gdb_test "ptype $type" "type = $ptype" |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | # If floats and pointers have the same size on this architecture, |
| 215 | # then casting from array/function to float works, because |
| 216 | # arrays/functions first decay to pointers, and then GDB's cast is |
| 217 | # more general than a C cast and accepts any two types of the same |
| 218 | # length. |
| 219 | set float_ptr_same_size \ |
| 220 | [get_integer_valueof "sizeof (float) == sizeof (void *)" -1] |
| 221 | |
| 222 | # Ditto double. |
| 223 | set double_ptr_same_size \ |
| 224 | [get_integer_valueof "sizeof (double) == sizeof (void *)" -1] |
| 225 | |
| 226 | # Ditto long double. |
| 227 | set long_double_ptr_same_size \ |
| 228 | [get_integer_valueof "sizeof (long double) == sizeof (void *)" -1] |
| 229 | |
| 230 | # Test converting/casting all variables in the first column of the |
| 231 | # table to all types (found in the first column of the table). |
| 232 | # The aggregates are all defined to be the same size so that |
| 233 | # casting actually works. (GDB's casting operator is more general |
| 234 | # than a C cast.) |
| 235 | # |
| 236 | # The main idea here is testing all the different paths in the |
| 237 | # value casting code in GDB (value_cast), making sure typedefs are |
| 238 | # preserved. |
| 239 | with_test_prefix "cast" { |
| 240 | foreach line1 $table { |
| 241 | set from [lindex $line1 0] |
| 242 | |
| 243 | if {![line_lang_match $line1 $lang]} { |
| 244 | continue |
| 245 | } |
| 246 | |
| 247 | foreach line2 $table { |
| 248 | set to [lindex $line2 0] |
| 249 | set whatis [lindex $line2 1] |
| 250 | set ptype [lindex $line2 2] |
| 251 | |
| 252 | if {![line_lang_match $line2 $lang]} { |
| 253 | continue |
| 254 | } |
| 255 | |
| 256 | # We try all combinations, even those that don't |
| 257 | # parse, or are invalid, to catch the case of a |
| 258 | # regression making them inadvertently valid. For |
| 259 | # example, these convertions are invalid: |
| 260 | # |
| 261 | # float <-> array [iff sizeof pointer != sizeof float] |
| 262 | # array -> function (not function pointer) |
| 263 | # array -> member_ptr |
| 264 | # |
| 265 | # while these are invalid syntax: |
| 266 | # |
| 267 | # (anything) type |
| 268 | # (var) anything |
| 269 | # (method) anything [not method pointer] |
| 270 | # (float) method |
| 271 | # |
| 272 | if {([string match "v_*" $to] |
| 273 | || (![string match "v_*" $from] && ![string match "*method" $from]) |
| 274 | || [string match "*method" $to])} { |
| 275 | gdb_test "whatis ($to) $from" "syntax error.*" "whatis ($to) $from (syntax)" |
| 276 | gdb_test "ptype ($to) $from" "syntax error.*" "ptype ($to) $from (syntax)" |
| 277 | } elseif {([string match "*float*" $from] && [string match "*array*" $to]) |
| 278 | || (!$float_ptr_same_size |
| 279 | && ([string match "float*" $to] && [string match "*array*" $from] |
| 280 | || [string match "float*" $to] && [string match "*method" $from])) |
| 281 | || (!$double_ptr_same_size |
| 282 | && ([string match "double*" $to] && [string match "*array*" $from] |
| 283 | || [string match "double*" $to] && [string match "*method" $from])) |
| 284 | || (!$long_double_ptr_same_size |
| 285 | && ([string match "long_double*" $to] && [string match "*array*" $from] |
| 286 | || [string match "long_double*" $to] && [string match "*method" $from])) |
| 287 | || ([string match "*ftype" $to] && [string match "*array*" $from]) |
| 288 | || ([string match "*ftype2" $to] && [string match "*array*" $from]) |
| 289 | || ([string match "*ftype" $to] && [string match "*method" $from]) |
| 290 | || ([string match "*ftype2" $to] && [string match "*method" $from]) |
| 291 | || ([string match "*method_ptr*" $to] && [string match "*method" $from]) |
| 292 | || ([string match "*method_ptr*" $to] && [string match "*array*" $from])} { |
| 293 | gdb_test "whatis ($to) $from" "Invalid cast." "whatis ($to) $from (invalid)" |
| 294 | gdb_test "ptype ($to) $from" "Invalid cast." "ptype ($to) $from (invalid)" |
| 295 | } else { |
| 296 | gdb_test "whatis ($to) $from" "type = [string_to_regexp $to]" |
| 297 | gdb_test "ptype ($to) $from" "type = $ptype" |
| 298 | } |
| 299 | } |
| 300 | } |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | foreach_with_prefix lang {"c" "c++"} { |
| 305 | if { [prepare $lang] } then { |
| 306 | run_tests $lang |
| 307 | } |
| 308 | } |