if (sym_a.block != sym_b.block)
return sym_a.block - sym_b.block;
- return strcmp (sym_a.symbol->print_name (), sym_b.symbol->print_name ());
+ c = strcmp (sym_a.symbol->print_name (), sym_b.symbol->print_name ());
+
+ if (c != 0)
+ return c;
+
+ /* These two symbols have the same name. It is possible, with types,
+ that we can see two symbols with the same name, but different types,
+ consider in C: 'typedef struct foo { ... } foo;' which creates a
+ 'struct foo' type and a 'foo' typedef type. For now this is the only
+ case we handle. In all other cases, we treat symbols with the same
+ name as being the same.
+
+
+ First, check the types, if they are the same, then consider these
+ symbols as the same. */
+ if (sym_a.symbol->type ()->code () == sym_b.symbol->type ()->code ())
+ return 0;
+
+ /* The types are different, but if neither is a typedef then we still
+ consider these symbols as the same. */
+ if (sym_a.symbol->type ()->code () != TYPE_CODE_TYPEDEF
+ && sym_b.symbol->type ()->code () != TYPE_CODE_TYPEDEF)
+ return 0;
+
+ /* The symbols have different types, and one is a typedef. They cannot
+ both be typedefs or we'd have taken the "types are the same" exit path
+ above. If the two types are defined on different lines then order by
+ line number. As line numbers are unsigned, don't subtract one from
+ the other in order to avoid underflow. */
+ if (sym_a.symbol->line () != sym_b.symbol->line ())
+ return (sym_a.symbol->line () > sym_b.symbol->line () ? 1 : -1);
+
+ /* The symbols have different types, and one is a typedef, but both
+ symbols are defined on the same line. For example:
+
+ typedef struct foo { int a; } foo;
+
+ In this case we sort the typedef after the non-typedef. This is an
+ arbitrary decision, but I think looks slightly nicer in the 'info
+ types' output; first we get the type, then the typedef. */
+ if (sym_a.symbol->type ()->code () == TYPE_CODE_TYPEDEF)
+ return 1;
+ else
+ return -1;
}
/* Returns true if the type_name of symbol_type of SYM matches TREG.
"28:\[\t \]+typedef struct baz_t baz;" \
"31:\[\t \]+typedef struct baz_t \\* baz_ptr;" \
"21:\[\t \]+struct baz_t;" \
+ "27:\[\t \]+typedef struct baz_t baz_t;" \
"\[\t \]+double" \
"33:\[\t \]+enum enum_t;" \
"\[\t \]+float" \
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2025 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/>. */
+
+typedef struct foo /* struct foo defined here. */
+{
+ int a;
+ int b;
+} foo; /* typedef foo defined here. */
+
+struct bar /* struct bar defined here. */
+{
+ int a;
+ int b;
+};
+
+/* Yes, this really is typedef-ing 'bar' to something other than 'struct
+ bar'. Just testing that GDB handles this. This is not good code. */
+typedef struct foo bar; /* typedef bar defined here. */
+
+/* The following must be a single line. This tests the 'struct baz' and
+ the 'typedef ... baz;' being on the same line. */
+typedef struct baz { int a; int b; } baz; /* baz defined here. */
+
+volatile struct foo obj1 = { 1, 2 };
+volatile foo obj2 = { 1, 2 };
+volatile struct bar obj3 = { 1, 2 };
+volatile bar obj4 = { 1, 2 };
+volatile baz obj5 = { 1, 2 };
+
+int
+main ()
+{
+ return obj1.b + obj2.b + obj3.b + obj4.b + obj5.b;
+}
--- /dev/null
+# Copyright 2025 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/>.
+
+# Compile a single CU containing a type and a typedef with the same
+# name (think C's "typedef struct foo { ... } foo;"). Check that
+# 'info types' shows both types.
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
+ return
+}
+
+# Line numbers needed by the test.
+set struct_foo_lineno [gdb_get_line_number "struct foo defined here"]
+set typedef_foo_lineno [gdb_get_line_number "typedef foo defined here"]
+set struct_bar_lineno [gdb_get_line_number "struct bar defined here"]
+set typedef_bar_lineno [gdb_get_line_number "typedef bar defined here"]
+set baz_lineno [gdb_get_line_number "baz defined here"]
+
+# Check that the expected two types show up in global SRCFILE.
+proc check_types { testname } {
+ gdb_test "info types foo" \
+ [multi_line \
+ "File (?:\[^\r\n\]+/)?[string_to_regexp $::srcfile]:" \
+ "${::struct_foo_lineno}:\\s+struct foo;" \
+ "${::typedef_foo_lineno}:\\s+typedef struct foo foo;"] \
+ "$testname, check foo"
+
+ gdb_test "info types bar" \
+ [multi_line \
+ "File (?:\[^\r\n\]+/)?[string_to_regexp $::srcfile]:" \
+ "${::struct_bar_lineno}:\\s+struct bar;" \
+ "${::typedef_bar_lineno}:\\s+typedef struct foo bar;"] \
+ "$testname, check bar"
+
+ gdb_test "info types baz" \
+ [multi_line \
+ "File (?:\[^\r\n\]+/)?[string_to_regexp $::srcfile]:" \
+ "${::baz_lineno}:\\s+struct baz;" \
+ "${::baz_lineno}:\\s+typedef struct baz baz;"] \
+ "$testname, check baz"
+}
+
+check_types "before inferior is started"
+
+clean_restart $testfile
+
+if {![runto_main]} {
+ return
+}
+
+check_types "after starting the inferior"