gdb: symbol_search objects of different types are not the same
Consider the C construct:
typedef struct foo
{
int a;
int b;
} foo;
GDB will see two types here, 'struct foo' and the typedef 'foo'.
However, if we use 'info types foo' we will see this:
File test.c:
18: struct foo;
At least that's what I see with current HEAD of master. However, it
is really just luck that we see the 'struct' here. See more below.
When searching for symbols matching 'foo' GDB ends up in the function
global_symbol_searcher::add_matching_symbols, where we consider all
possible matching symbols. This will include the 'struct foo' and the
typedef 'foo'. However, before a new symbols is added to the results,
we attempt to remove duplicates with this code:
/* Match, insert if not already in the results. */
symbol_search ss (block, sym);
if (result_set->find (ss) == result_set->end ())
result_set->insert (ss);
If a symbol is already present in result_set then it will not be added
a second time.
The symbol_search equality check is done using the function
symbol_search::compare_search_syms, this function does a number of
checks, but at the end, any two symbols that are in the same block
within the same file, with the same name, are considered the same,
even if the types of those symbols are different.
This makes sense in most cases, it usually wouldn't make sense to have
two symbols within a single block with different types. But the
'struct foo' and typedef 'foo' case is a bit of a strange one. Within
DWARF and GDB we consider both of these as just types. But in C
types and structure names live in different namespaces, and so we can
have both in the same block. I don't think that GDB should consider
these two as the same, especially if we consider something really
ill-advised like this:
struct foo
{
int a;
int b;
};
typedef int foo;
This is perfectly valid C code, 'struct foo' and the typedef 'foo' are
in different namespaces, and can be used within the same block. But
please, never write C code like this.
Given the above, I think, when asked about 'foo', GDB should, report
both 'struct foo' and the typedef 'foo'.
To do this I propose extending symbol_search::compare_search_syms such
that if two symbol_search objects are in the same block, within the
same file, and they have the same name, then if just one of them is a
typedef, the two objects will not be considered equal. The results
will be sorted by line number if the line numbers are different, or,
if the line numbers are the same, the non-typedef will be sorted
first. This means that for something like this:
I mentioned earlier that it is really just luck that we see 'struct
foo'. I ran into this problem while working on another patch. When
testing with the 'debug-types' board file I was seeing the typedef
being reported rather than the struct. In "normal" DWARF given the
'typedef struct foo { ...} foo;' construct, the compiler will usually
emit the struct definition first, and then the typedef definition. So
when GDB parses the DWARF it sees the struct first. It is the typedef
that becomes the duplicate which is not added to the results list.
But with the 'debug-types' board the compiler moves the struct
definition out to the .debug_types section. And GDB now parses the CU
containing the typedef first, and then expands the structure
definition from the separate section afterwards. As a result, it is
the structure that is now considered the duplicate, and the typedef is
the result that gets reported.
I think this is yet another motivation for this patch. Changes like
this (the use of .debug_types section) shouldn't impact what results
GDB shows to the user.
There is an interesting update to the gdb.base/info-types.exp.tcl test
script. In this case the C results only needed to change to include
the typedef. The C++ results already included both the struct and the
typedef in the expected results. The reason for this is that C places
both the struct baz_t and the typedef for baz_t into the global block,
while C++ places the struct in the global block, and the typedef into
the static block. I have no idea why there's a difference in the
placement, but I'm choosing to believe the difference is correct. But
this explains why only the C results needed to change. If anything
this (I think) is yet another justification for this change; having C
not show the typedef in this case seems weird when the same source
code compiled as C++ does show the typedef.