mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
87a37e5e07
The gdb.cp/ambiguous.exp testcase had been disabled for many years, but recently it was re-enabled. However, it is failing everywhere. That is because it is testing an old feature that is gone from GDB. The testcase is expecting to see an ambiguous field warning, like: # X is derived from A1 and A2; both A1 and A2 have a member 'x' send_gdb "print x.x\n" gdb_expect { -re "warning: x ambiguous; using X::A2::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" { pass "print x.x" } -re "warning: x ambiguous; using X::A1::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" { pass "print x.x" } -re ".*$gdb_prompt $" { fail "print x.x" } timeout { fail "(timeout) print x.x" } } However, GDB just accesses one of the candidates without warning or error: print x.x $1 = 1431655296 (gdb) FAIL: gdb.cp/ambiguous.exp: print x.x (The weird number is because the testcase does not initialize the variables.) The testcase come in originally with the big HP merge: +Sun Jan 10 23:44:11 1999 David Taylor <taylor@texas.cygnus.com> + + + The following files are part of the HP merge; some had longer + names at HP, but have been renamed to be no more than 14 + characters in length. Looking at the tree back then, we find that warning: /* Helper function used by value_struct_elt to recurse through baseclasses. Look for a field NAME in ARG1. Adjust the address of ARG1 by OFFSET bytes, and search in it assuming it has (class) type TYPE. If found, return value, else return NULL. If LOOKING_FOR_BASECLASS, then instead of looking for struct fields, look for a baseclass named NAME. */ static value_ptr search_struct_field (name, arg1, offset, type, looking_for_baseclass) char *name; register value_ptr arg1; int offset; register struct type *type; int looking_for_baseclass; { int found = 0; char found_class[1024]; value_ptr v; struct type *vbase = NULL; found_class[0] = '\000'; v = search_struct_field_aux (name, arg1, offset, type, looking_for_baseclass, &found, found_class, &vbase); if (found > 1) warning ("%s ambiguous; using %s::%s. Use a cast to disambiguate.", name, found_class, name); return v; } However, in current GDB, search_struct_field does not handle the ambiguous field case, nor is that warning found anywhere. Somehow it got lost over the years. That seems like a regression, because the compiler (as per language rules) rejects the ambiguous accesses as well. E.g.: gdb.cp/ambiguous.cc:98:5: error: request for member 'x' is ambiguous 98 | x.x = 1; | ^ gdb.cp/ambiguous.cc:10:7: note: candidates are: 'int A2::x' 10 | int x; | ^ gdb.cp/ambiguous.cc:4:7: note: 'int A1::x' 4 | int x; | ^ This patch restores the feature, though implemented differently and with better user experience, IMHO. An ambiguous access is now an error instead of a warning, and also GDB shows you all the candidates, like: (gdb) print x.x Request for member 'x' is ambiguous in type 'X'. Candidates are: 'int A1::x' (X -> A1) 'int A2::x' (X -> A2) (gdb) print j.x Request for member 'x' is ambiguous in type 'J'. Candidates are: 'int A1::x' (J -> K -> A1) 'int A1::x' (J -> L -> A1) Users can then fix their commands by casting or by specifying the baseclass explicitly, like: (gdb) p x.A1::x $1 = 1 (gdb) p x.A2::x $2 = 2 (gdb) p ((A1) x).x $3 = 1 (gdb) p ((A2) x).x $4 = 2 (gdb) p j.K::x $12 = 1 (gdb) p j.L::x $13 = 2 (gdb) p j.A1::x base class 'A1' is ambiguous in type 'J' The last error I've not touched; could be improved to also list the baseclass candidates. The showing the class "path" for each candidate was inspired by GCC's output when you try an ambiguous cast: gdb.cp/ambiguous.cc:161:8: error: ambiguous conversion from derived class 'const JVA1' to base class 'const A1': class JVA1 -> class KV -> class A1 class JVA1 -> class A1 (A1) jva1; ^~~~ I did not include the "class" word as it seemed unnecessarily repetitive, but I can include it if people prefer it: (gdb) print j.x Request for member 'x' is ambiguous in type 'J'. Candidates are: 'int A1::x' (class J -> class K -> class A1) 'int A1::x' (class J -> class L -> class A1) The testcase is adjusted accordingly. I also took the chance to modernize it at the same time. Also, as mentioned above, the testcase doesn't currently initialize the tested variables. This patch inializes them all, giving each field a distinct value, so that we can be sure that GDB is accessing the right fields / offsets. The testcase is extended accordingly. Unfortunately, this exposes a bug, not addressed in this patch. The bug is around a class that inherits from A1 directly and also inherits from two other distinct base classes that inherit virtually from A1 in turn: print jva1.KV::x $51 = 1431665544 (gdb) FAIL: gdb.cp/ambiguous.exp: all fields: print jva1.KV::x print jva1.KV::y $52 = 21845 (gdb) FAIL: gdb.cp/ambiguous.exp: all fields: print jva1.KV::y (gdb) print /x (KV)jva1 $4 = {<A1> = <invalid address>, _vptr.KV = 0x555555557b88 <vtable for JVA1+24>, i = 0x457} (gdb) print /x (A1)(KV)jva1 Cannot access memory at address 0x0 Since that's an orthogonal issue, I filed PR c++/26550 and kfailed the tests that fail because of it. gdb/ChangeLog: PR exp/26602 * valops.c (struct struct_field_searcher): New. (update_search_result): Rename to ... (struct_field_searcher::update_result): ... this. Simplify prototype. Record all found fields. (do_search_struct_field): Rename to ... (struct_field_searcher::search): ... this. Simplify prototype. Maintain stack of visited baseclass path. Call update_result for fields too. Keep searching fields in baseclasses instead of stopping at the first found field. (search_struct_field): Use struct_field_searcher. When looking for fields, report ambiguous access attempts. gdb/testsuite/ChangeLog: PR exp/26602 PR c++/26550 * gdb.cp/ambiguous.cc (marker1): Delete. (main): Initialize all the fields of the locals. Replace marker1 call with a "set breakpoint here" marker. * gdb.cp/ambiguous.exp: Modernize. Use gdb_continue_to_breakpoint instead of running to marker1. Add tests printing all the variables and all the fields of the variables. (test_ambiguous): New proc, expecting the new GDB output when a field access is ambiguous. Change all "warning: X ambiguous" tests to use it.
178 lines
1.9 KiB
C++
178 lines
1.9 KiB
C++
|
|
class A1 {
|
|
public:
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
class A2 {
|
|
public:
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
class A3 {
|
|
public:
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
class X : public A1, public A2 {
|
|
public:
|
|
int z;
|
|
};
|
|
|
|
class L : public A1 {
|
|
public:
|
|
int z;
|
|
};
|
|
|
|
class LV : public virtual A1 {
|
|
public:
|
|
int z;
|
|
};
|
|
|
|
class M : public A2 {
|
|
public:
|
|
int w;
|
|
};
|
|
|
|
class N : public L, public M {
|
|
public:
|
|
int r;
|
|
};
|
|
|
|
class K : public A1 {
|
|
public:
|
|
int i;
|
|
};
|
|
|
|
class KV : public virtual A1 {
|
|
public:
|
|
int i;
|
|
};
|
|
|
|
class J : public K, public L {
|
|
public:
|
|
int j;
|
|
};
|
|
|
|
class JV : public KV, public LV {
|
|
public:
|
|
int jv;
|
|
};
|
|
|
|
class JVA1 : public KV, public LV, public A1 {
|
|
public:
|
|
int jva1;
|
|
};
|
|
|
|
class JVA2 : public KV, public LV, public A2 {
|
|
public:
|
|
int jva2;
|
|
};
|
|
|
|
class JVA1V : public KV, public LV, public virtual A1 {
|
|
public:
|
|
int jva1v;
|
|
};
|
|
|
|
int main()
|
|
{
|
|
A1 a1;
|
|
A2 a2;
|
|
A3 a3;
|
|
X x;
|
|
L l;
|
|
M m;
|
|
N n;
|
|
K k;
|
|
J j;
|
|
JV jv;
|
|
JVA1 jva1;
|
|
JVA2 jva2;
|
|
JVA1V jva1v;
|
|
|
|
int i;
|
|
|
|
i += k.i + m.w + a1.x + a2.x + a3.x + x.z + l.z + n.r + j.j;
|
|
|
|
/* Initialize all the fields. Keep the order the same as in the
|
|
.exp file. */
|
|
|
|
a1.x = 1;
|
|
a1.y = 2;
|
|
|
|
a2.x = 1;
|
|
a2.y = 2;
|
|
|
|
a3.x = 1;
|
|
a3.y = 2;
|
|
|
|
x.A1::x = 1;
|
|
x.A1::y = 2;
|
|
x.A2::x = 3;
|
|
x.A2::y = 4;
|
|
x.z = 5;
|
|
|
|
l.x = 1;
|
|
l.y = 2;
|
|
l.z = 3;
|
|
|
|
m.x = 1;
|
|
m.y = 2;
|
|
m.w = 3;
|
|
|
|
n.A1::x = 1;
|
|
n.A1::y = 2;
|
|
n.A2::x = 3;
|
|
n.A2::y = 4;
|
|
n.w = 5;
|
|
n.r = 6;
|
|
n.z = 7;
|
|
|
|
k.x = 1;
|
|
k.y = 2;
|
|
k.i = 3;
|
|
|
|
j.K::x = 1;
|
|
j.K::y = 2;
|
|
j.L::x = 3;
|
|
j.L::y = 4;
|
|
j.i = 5;
|
|
j.z = 6;
|
|
j.j = 7;
|
|
|
|
jv.x = 1;
|
|
jv.y = 2;
|
|
jv.i = 3;
|
|
jv.z = 4;
|
|
jv.jv = 5;
|
|
|
|
jva1.KV::x = 1;
|
|
jva1.KV::y = 2;
|
|
jva1.LV::x = 3;
|
|
jva1.LV::y = 4;
|
|
jva1.z = 5;
|
|
jva1.i = 6;
|
|
jva1.jva1 = 7;
|
|
|
|
jva2.KV::x = 1;
|
|
jva2.KV::y = 2;
|
|
jva2.LV::x = 3;
|
|
jva2.LV::y = 4;
|
|
jva2.A2::x = 5;
|
|
jva2.A2::y = 6;
|
|
jva2.z = 7;
|
|
jva2.i = 8;
|
|
jva2.jva2 = 9;
|
|
|
|
jva1v.x = 1;
|
|
jva1v.y = 2;
|
|
jva1v.z = 3;
|
|
jva1v.i = 4;
|
|
jva1v.jva1v = 5;
|
|
|
|
return 0; /* set breakpoint here */
|
|
}
|