aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ChangeLog15
-rw-r--r--gdb/testsuite/ChangeLog14
-rw-r--r--gdb/testsuite/gdb.cp/ambiguous.cc85
-rw-r--r--gdb/testsuite/gdb.cp/ambiguous.exp329
-rw-r--r--gdb/valops.c227
5 files changed, 451 insertions, 219 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 26623c6..63ed7e4 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,18 @@
+2020-10-12 Pedro Alves <pedro@palves.net>
+
+ 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.
+
2020-10-11 Andrew Burgess <andrew.burgess@embecosm.com>
* frame.c (inside_main_func): Check full symbols as well as
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index aefdcba..d508701 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,17 @@
+2020-10-12 Pedro Alves <pedro@palves.net>
+
+ 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.
+
2020-10-12 Gary Benson <gbenson@redhat.com>
* gdb.base/msym-bp.c (foo): Add __attribute__ ((used)).
diff --git a/gdb/testsuite/gdb.cp/ambiguous.cc b/gdb/testsuite/gdb.cp/ambiguous.cc
index 93fba1c..a556865 100644
--- a/gdb/testsuite/gdb.cp/ambiguous.cc
+++ b/gdb/testsuite/gdb.cp/ambiguous.cc
@@ -1,9 +1,4 @@
-void marker1()
-{
- return;
-}
-
class A1 {
public:
int x;
@@ -102,7 +97,81 @@ int main()
i += k.i + m.w + a1.x + a2.x + a3.x + x.z + l.z + n.r + j.j;
- marker1();
-
- return 0;
+ /* 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 */
}
diff --git a/gdb/testsuite/gdb.cp/ambiguous.exp b/gdb/testsuite/gdb.cp/ambiguous.exp
index 4d01c10..b7fec1b 100644
--- a/gdb/testsuite/gdb.cp/ambiguous.exp
+++ b/gdb/testsuite/gdb.cp/ambiguous.exp
@@ -15,15 +15,9 @@
# This file is part of the gdb testsuite
-# tests relating to ambiguous class members
-# Written by Satish Pai <pai@apollo.hp.com> 1997-07-28
-
-# This file is part of the gdb testsuite
-
-#
-# test running programs
-#
-
+# Print out various class objects' members and check that the error
+# about the field or baseclass being ambiguous is emitted at the right
+# times.
if { [skip_cplus_tests] } { continue }
@@ -47,187 +41,226 @@ if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
return -1
}
-#
-# set it up at a breakpoint so we can play with the variable values
-#
if ![runto_main] then {
perror "couldn't run to breakpoint"
continue
}
-send_gdb "break marker1\n" ; gdb_expect -re ".*$gdb_prompt $"
- send_gdb "cont\n"
- gdb_expect {
- -re "Break.* marker1 \\(\\) at .*:$decimal.*$gdb_prompt $" {
- send_gdb "up\n"
- gdb_expect {
- -re ".*$gdb_prompt $" { pass "up from marker1" }
- timeout { fail "up from marker1" }
- }
- }
- -re "$gdb_prompt $" { fail "continue to marker1" }
- timeout { fail "(timeout) continue to marker1" }
+# Run to a breakpoint after the variables have been initialized so we
+# can play with the variable values.
+
+set lineno [gdb_get_line_number "set breakpoint here"]
+
+gdb_breakpoint $lineno
+gdb_continue_to_breakpoint "breakpoint here"
+
+set number -?$decimal
+
+with_test_prefix "all vars" {
+ gdb_test "print a1" \
+ " = \{x = 1, y = 2\}"
+
+ gdb_test "print a2" \
+ " = \{x = 1, y = 2\}"
+
+ gdb_test "print a3" \
+ " = \{x = 1, y = 2\}"
+
+ gdb_test "print x" \
+ " = \{<A1> = \{x = 1, y = 2\}, <A2> = \{x = 3, y = 4\}, z = 5\}"
+
+ gdb_test "print l" \
+ " = \{<A1> = \{x = 1, y = 2\}, z = 3\}"
+
+ gdb_test "print m" \
+ " = \{<A2> = \{x = 1, y = 2\}, w = 3\}"
+
+ gdb_test "print n" \
+ " = \{<L> = \{<A1> = \{x = 1, y = 2\}, z = 7\}, <M> = \{<A2> = \{x = 3, y = 4\}, w = 5\}, r = 6\}"
+
+ gdb_test "print k" \
+ " = \{<A1> = \{x = 1, y = 2\}, i = 3\}"
+
+ gdb_test "print j" \
+ " = {<K> = {<A1> = {x = 1, y = 2}, i = 5}, <L> = {<A1> = {x = 3, y = 4}, z = 6}, j = 7}"
+
+ gdb_test "print jv" \
+ " = \{<KV> = \{<A1> = \{x = 1, y = 2\}, _vptr.KV = $hex <vtable for JV.*>, i = 3\}, <LV> = \{_vptr.LV = $hex <VTT for JV>, z = 4\}, jv = 5\}"
+
+ # No way to initialize one of the A1's, so just take any number there.
+ gdb_test "print jva1" \
+ " = \{<KV> = \{<A1> = \{x = 3, y = 4\}, _vptr.KV = $hex <vtable for JVA1.*>, i = 6\}, <LV> = \{_vptr.LV = $hex <VTT for JVA1>, z = 5\}, <A1> = \{x = $number, y = $number\}, jva1 = 7\}"
+
+ gdb_test "print jva2" \
+ " = \{<KV> = \{<A1> = \{x = 3, y = 4\}, _vptr.KV = $hex <vtable for JVA2.*>, i = 8\}, <LV> = \{_vptr.LV = $hex <VTT for JVA2>, z = 7\}, <A2> = \{x = 5, y = 6\}, jva2 = 9\}"
+
+ gdb_test "print jva1v" \
+ " = \{<KV> = \{<A1> = \{x = 1, y = 2\}, _vptr.KV = $hex <vtable for JVA1V+.*>, i = 4\}, <LV> = \{_vptr.LV = $hex <VTT for JVA1V>, z = 3\}, jva1v = 5\}"
+}
+
+# Check that we can access all the fields correctly, using the same
+# syntax as used in the .cc file. Keep the order here in sync with
+# the .cc file.
+with_test_prefix "all fields" {
+ gdb_test "print a1.x" " = 1"
+ gdb_test "print a1.y" " = 2"
+
+ gdb_test "print a2.x" " = 1"
+ gdb_test "print a2.y" " = 2"
+
+ gdb_test "print a3.x" " = 1"
+ gdb_test "print a3.y" " = 2"
+
+ gdb_test "print x.A1::x" " = 1"
+ gdb_test "print x.A1::y" " = 2"
+ gdb_test "print x.A2::x" " = 3"
+ gdb_test "print x.A2::y" " = 4"
+ gdb_test "print x.z" " = 5"
+
+ gdb_test "print l.x" " = 1"
+ gdb_test "print l.y" " = 2"
+ gdb_test "print l.z" " = 3"
+
+ gdb_test "print m.x" " = 1"
+ gdb_test "print m.y" " = 2"
+ gdb_test "print m.w" " = 3"
+
+ gdb_test "print n.A1::x" " = 1"
+ gdb_test "print n.A1::y" " = 2"
+ gdb_test "print n.A2::x" " = 3"
+ gdb_test "print n.A2::y" " = 4"
+ gdb_test "print n.w" " = 5"
+ gdb_test "print n.r" " = 6"
+ gdb_test "print n.z" " = 7"
+
+ gdb_test "print k.x" " = 1"
+ gdb_test "print k.y" " = 2"
+ gdb_test "print k.i" " = 3"
+
+ gdb_test "print j.K::x" " = 1"
+ gdb_test "print j.K::y" " = 2"
+ gdb_test "print j.L::x" " = 3"
+ gdb_test "print j.L::y" " = 4"
+ gdb_test "print j.i" " = 5"
+ gdb_test "print j.z" " = 6"
+ gdb_test "print j.j" " = 7"
+
+ gdb_test "print jv.x" " = 1"
+ gdb_test "print jv.y" " = 2"
+ gdb_test "print jv.i" " = 3"
+ gdb_test "print jv.z" " = 4"
+ gdb_test "print jv.jv" " = 5"
+
+ setup_kfail "c++/26550" *-*-*
+ gdb_test "print jva1.KV::x" " = 1"
+ setup_kfail "c++/26550" *-*-*
+ gdb_test "print jva1.KV::y" " = 2"
+ setup_kfail "c++/26550" *-*-*
+ gdb_test "print jva1.LV::x" " = 3"
+ setup_kfail "c++/26550" *-*-*
+ gdb_test "print jva1.LV::y" " = 4"
+ gdb_test "print jva1.z" " = 5"
+ gdb_test "print jva1.i" " = 6"
+ gdb_test "print jva1.jva1" "= 7"
+
+ setup_kfail "c++/26550" *-*-*
+ gdb_test "print jva2.KV::x" " = 1"
+ setup_kfail "c++/26550" *-*-*
+ gdb_test "print jva2.KV::y" " = 2"
+ setup_kfail "c++/26550" *-*-*
+ gdb_test "print jva2.LV::x" " = 3"
+ setup_kfail "c++/26550" *-*-*
+ gdb_test "print jva2.LV::y" " = 4"
+ gdb_test "print jva2.A2::x" " = 5"
+ gdb_test "print jva2.A2::y" " = 6"
+ gdb_test "print jva2.z" " = 7"
+ gdb_test "print jva2.i" " = 8"
+ gdb_test "print jva2.jva2" "= 9"
+
+ gdb_test "print jva1v.x" " = 1"
+ gdb_test "print jva1v.y" " = 2"
+ gdb_test "print jva1v.z" " = 3"
+ gdb_test "print jva1v.i" " = 4"
+ gdb_test "print jva1v.jva1v" " = 5"
+}
+
+# Test that printing WHAT reports an error about FIELD being ambiguous
+# in TYPE, and that the candidates are CANDIDATES.
+proc test_ambiguous {what field type candidates} {
+ set msg "Request for member '$field' is ambiguous in type '$type'. Candidates are:"
+
+ foreach c $candidates {
+ set c_re [string_to_regexp $c]
+ append msg "\r\n $c_re"
}
-# print out various class objects' members. The values aren't
-# important, just check that the warning is emitted at the
-# right times.
+ gdb_test "print $what" $msg
+}
# X is derived from A1 and A2; both A1 and A2 have a member 'x'
-setup_kfail gdb/26602 *-*-*
-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" }
+test_ambiguous "x.x" "x" "X" {
+ "'int A1::x' (X -> A1)"
+ "'int A2::x' (X -> A2)"
}
-
# N is derived from A1 and A2, but not immediately -- two steps
# up in the hierarchy. Both A1 and A2 have a member 'x'.
-setup_kfail gdb/26602 *-*-*
-send_gdb "print n.x\n"
-gdb_expect {
- -re "warning: x ambiguous; using N::M::A2::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- pass "print n.x"
- }
- -re "warning: x ambiguous; using N::L::A1::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- pass "print n.x"
- }
- -re ".*$gdb_prompt $" { fail "print n.x" }
- timeout { fail "(timeout) print n.x" }
+test_ambiguous "n.x" "x" "N" {
+ "'int A1::x' (N -> L -> A1)"
+ "'int A2::x' (N -> M -> A2)"
}
-# J is derived from A1 twice. A1 has a member x.
-setup_kfail gdb/26602 *-*-*
-send_gdb "print j.x\n"
-gdb_expect {
- -re "warning: x ambiguous; using J::L::A1::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- pass "print j.x"
- }
- -re "warning: x ambiguous; using J::K::A1::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- pass "print j.x"
- }
- -re ".*$gdb_prompt $" { fail "print j.x" }
- timeout { fail "(timeout) print j.x" }
+# J is derived from A1 twice. A1 has a member x.
+test_ambiguous "j.x" "x" "J" {
+ "'int A1::x' (J -> K -> A1)"
+ "'int A1::x' (J -> L -> A1)"
}
# JV is derived from A1 but A1 is a virtual base. Should not
-# report an ambiguity in this case.
-send_gdb "print jv.x\n"
-gdb_expect {
- -re "warning: x ambiguous.*Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- fail "print jv.x (ambiguity reported)"
- }
- -re "\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" { pass "print jv.x" }
- -re ".*$gdb_prompt $" { fail "print jv.x (??)" }
- timeout { fail "(timeout) print jv.x" }
-}
+# report an ambiguity in this case.
+gdb_test "print jv.x" " = 1"
# JVA1 is derived from A1; A1 occurs as a virtual base in two
# ancestors, and as a non-virtual immediate base. Ambiguity must
-# be reported.
-setup_kfail gdb/26602 *-*-*
-send_gdb "print jva1.x\n"
-gdb_expect {
- -re "warning: x ambiguous; using JVA1::A1::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- pass "print jva1.x"
- }
- -re "warning: x ambiguous; using JVA1::KV::A1::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- pass "print jva1.x"
- }
- -re ".*$gdb_prompt $" { fail "print jva1.x" }
- timeout { fail "(timeout) print jva1.x" }
+# be reported.
+test_ambiguous "jva1.x" "x" "JVA1" {
+ "'int A1::x' (JVA1 -> KV -> A1)"
+ "'int A1::x' (JVA1 -> A1)"
}
# JVA2 is derived from A1 & A2; A1 occurs as a virtual base in two
# ancestors, and A2 is a non-virtual immediate base. Ambiguity must
# be reported as A1 and A2 both have a member 'x'.
-setup_kfail gdb/26602 *-*-*
-send_gdb "print jva2.x\n"
-gdb_expect {
- -re "warning: x ambiguous; using JVA2::A2::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- pass "print jva2.x"
- }
- -re "warning: x ambiguous; using JVA2::KV::A1::x. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- pass "print jva2.x"
- }
- -re ".*$gdb_prompt $" { fail "print jva2.x" }
- timeout { fail "(timeout) print jva2.x" }
+test_ambiguous "jva2.x" "x" "JVA2" {
+ "'int A1::x' (JVA2 -> KV -> A1)"
+ "'int A2::x' (JVA2 -> A2)"
}
# JVA1V is derived from A1; A1 occurs as a virtual base in two
# ancestors, and also as a virtual immediate base. Ambiguity must
# not be reported.
-send_gdb "print jva1v.x\n"
-gdb_expect {
- -re "warning: x ambiguous.*Use a cast to disambiguate.\r\n\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" {
- fail "print jva1v.x (ambiguity reported)"
- }
- -re "\\$\[0-9\]* = \[-\]*\[0-9\]*\r\n$gdb_prompt $" { pass "print jva1v.x" }
- -re ".*$gdb_prompt $" { fail "print jva1v.x (??)" }
- timeout { fail "(timeout) print jva1v.x" }
-}
+gdb_test "print jva1v.x" " = 1"
# Now check for ambiguous bases.
# J is derived from A1 twice; report ambiguity if a J is
# cast to an A1.
-setup_kfail gdb/26602 *-*-*
-send_gdb "print (A1)j\n"
-gdb_expect {
- -re "warning: A1 ambiguous; using J::L::A1. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \{x = \[-\]*\[0-9\]*, y = \[-\]*\[0-9\]*\}\r\n$gdb_prompt $" {
- pass "print (A1)j"
- }
- -re "warning: A1 ambiguous; using J::K::A1. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \{x = \[-\]*\[0-9\]*, y = \[-\]*\[0-9\]*\}\r\n$gdb_prompt $" {
- pass "print (A1)j"
- }
- -re ".*$gdb_prompt $" { fail "print (A1)j" }
- timeout { fail "(timeout) print (A1)j" }
-}
+gdb_test "print (A1)j" "base class 'A1' is ambiguous in type 'J'"
# JV is derived from A1 twice, but A1 is a virtual base; should
# not report ambiguity when a JV is cast to an A1.
-send_gdb "print (A1)jv\n"
-gdb_expect {
- -re "warning: A1 ambiguous.*Use a cast to disambiguate.\r\n\\$\[0-9\]* = \{x = \[-\]*\[0-9\]*, y = \[-\]*\[0-9\]*\}\r\n$gdb_prompt $" {
- fail "print (A1)jv (ambiguity reported)"
- }
- -re "\\$\[0-9\]* = \{x = \[-\]*\[0-9\]*, y = \[-\]*\[0-9\]*\}\r\n$gdb_prompt $" { pass "print (A1)jv" }
- -re ".*$gdb_prompt $" { fail "print (A1)jv (??)" }
- timeout { fail "(timeout) print (A1)jv" }
-}
+gdb_test "print (A1)jv" " = {x = 1, y = 2}"
# JVA1 is derived from A1; A1 is a virtual base and also a
# non-virtual base. Must report ambiguity if a JVA1 is cast to an A1.
-setup_kfail gdb/26602 *-*-*
-send_gdb "print (A1)jva1\n"
-gdb_expect {
- -re "warning: A1 ambiguous; using JVA1::A1. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \{x = \[-\]*\[0-9\]*, y = \[-\]*\[0-9\]*\}\r\n$gdb_prompt $" {
- pass "print (A1)jva1"
- }
- -re "warning: A1 ambiguous; using JVA1::KV::A1. Use a cast to disambiguate.\r\n\\$\[0-9\]* = \{x = \[-\]*\[0-9\]*, y = \[-\]*\[0-9\]*\}\r\n$gdb_prompt $" {
- pass "print (A1)jva1"
- }
- -re ".*$gdb_prompt $" { fail "print (A1)jva1" }
- timeout { fail "(timeout) print (A1)jva1" }
-}
+gdb_test "print (A1)jva1" "base class 'A1' is ambiguous in type 'JVA1'"
+
+# Add an intermediate cast to KV, and it should work.
+setup_kfail "c++/26550" *-*-*
+gdb_test "print (KV)jva1" " = \{<A1> = \{x = 3, y = 4\}, _vptr.KV = $hex <VTT for KV>, i = 6\}"
+setup_kfail "c++/26550" *-*-*
+gdb_test "print (A1)(KV)jva1" " = \{x = 3, y = 4\}"
# JVA1V is derived from A1; A1 is a virtual base indirectly
# and also directly; must not report ambiguity when a JVA1V is cast to an A1.
-send_gdb "print (A1)jva1v\n"
-gdb_expect {
- -re "warning: A1 ambiguous.*Use a cast to disambiguate.\r\n\\$\[0-9\]* = \{x = \[-\]*\[0-9\]*, y = \[-\]*\[0-9\]*\}\r\n$gdb_prompt $" {
- fail "print (A1)jva1v (ambiguity reported)"
- }
- -re "\\$\[0-9\]* = \{x = \[-\]*\[0-9\]*, y = \[-\]*\[0-9\]*\}\r\n$gdb_prompt $" { pass "print (A1)jva1v"
- }
- -re ".*$gdb_prompt $" { fail "print (A1)jva1v (??)" }
- timeout { fail "(timeout) print (A1)jva1v" }
-}
-
+gdb_test "print (A1)jva1v" " = {x = 1, y = 2}"
diff --git a/gdb/valops.c b/gdb/valops.c
index 4906b3b..6549613 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1766,54 +1766,132 @@ typecmp (int staticp, int varargs, int nargs,
return i + 1;
}
-/* Helper class for do_search_struct_field that updates *RESULT_PTR
- and *LAST_BOFFSET, and possibly throws an exception if the field
- search has yielded ambiguous results. */
+/* Helper class for search_struct_field that keeps track of found
+ results and possibly throws an exception if the search yields
+ ambiguous results. See search_struct_field for description of
+ LOOKING_FOR_BASECLASS. */
-static void
-update_search_result (struct value **result_ptr, struct value *v,
- LONGEST *last_boffset, LONGEST boffset,
- const char *name, struct type *type)
+struct struct_field_searcher
+{
+ /* A found field. */
+ struct found_field
+ {
+ /* Path to the structure where the field was found. */
+ std::vector<struct type *> path;
+
+ /* The field found. */
+ struct value *field_value;
+ };
+
+ /* See corresponding fields for description of parameters. */
+ struct_field_searcher (const char *name,
+ struct type *outermost_type,
+ bool looking_for_baseclass)
+ : m_name (name),
+ m_looking_for_baseclass (looking_for_baseclass),
+ m_outermost_type (outermost_type)
+ {
+ }
+
+ /* The search entry point. If LOOKING_FOR_BASECLASS is true and the
+ base class search yields ambiguous results, this throws an
+ exception. If LOOKING_FOR_BASECLASS is false, the found fields
+ are accumulated and the caller (search_struct_field) takes care
+ of throwing an error if the field search yields ambiguous
+ results. The latter is done that way so that the error message
+ can include a list of all the found candidates. */
+ void search (struct value *arg, LONGEST offset, struct type *type);
+
+ const std::vector<found_field> &fields ()
+ {
+ return m_fields;
+ }
+
+ struct value *baseclass ()
+ {
+ return m_baseclass;
+ }
+
+private:
+ /* Update results to include V, a found field/baseclass. */
+ void update_result (struct value *v, LONGEST boffset);
+
+ /* The name of the field/baseclass we're searching for. */
+ const char *m_name;
+
+ /* Whether we're looking for a baseclass, or a field. */
+ const bool m_looking_for_baseclass;
+
+ /* The offset of the baseclass containing the field/baseclass we
+ last recorded. */
+ LONGEST m_last_boffset = 0;
+
+ /* If looking for a baseclass, then the result is stored here. */
+ struct value *m_baseclass = nullptr;
+
+ /* When looking for fields, the found candidates are stored
+ here. */
+ std::vector<found_field> m_fields;
+
+ /* The type of the initial type passed to search_struct_field; this
+ is used for error reporting when the lookup is ambiguous. */
+ struct type *m_outermost_type;
+
+ /* The full path to the struct being inspected. E.g. for field 'x'
+ defined in class B inherited by class A, we have A and B pushed
+ on the path. */
+ std::vector <struct type *> m_struct_path;
+};
+
+void
+struct_field_searcher::update_result (struct value *v, LONGEST boffset)
{
if (v != NULL)
{
- if (*result_ptr != NULL
- /* The result is not ambiguous if all the classes that are
- found occupy the same space. */
- && *last_boffset != boffset)
- error (_("base class '%s' is ambiguous in type '%s'"),
- name, TYPE_SAFE_NAME (type));
- *result_ptr = v;
- *last_boffset = boffset;
+ if (m_looking_for_baseclass)
+ {
+ if (m_baseclass != nullptr
+ /* The result is not ambiguous if all the classes that are
+ found occupy the same space. */
+ && m_last_boffset != boffset)
+ error (_("base class '%s' is ambiguous in type '%s'"),
+ m_name, TYPE_SAFE_NAME (m_outermost_type));
+
+ m_baseclass = v;
+ m_last_boffset = boffset;
+ }
+ else
+ {
+ /* The field is not ambiguous if it occupies the same
+ space. */
+ if (m_fields.empty () || m_last_boffset != boffset)
+ m_fields.push_back ({m_struct_path, v});
+ }
}
}
/* A helper for search_struct_field. This does all the work; most
- arguments are as passed to search_struct_field. The result is
- stored in *RESULT_PTR, which must be initialized to NULL.
- OUTERMOST_TYPE is the type of the initial type passed to
- search_struct_field; this is used for error reporting when the
- lookup is ambiguous. */
+ arguments are as passed to search_struct_field. */
-static void
-do_search_struct_field (const char *name, struct value *arg1, LONGEST offset,
- struct type *type, int looking_for_baseclass,
- struct value **result_ptr,
- LONGEST *last_boffset,
- struct type *outermost_type)
+void
+struct_field_searcher::search (struct value *arg1, LONGEST offset,
+ struct type *type)
{
int i;
int nbases;
+ m_struct_path.push_back (type);
+ SCOPE_EXIT { m_struct_path.pop_back (); };
+
type = check_typedef (type);
nbases = TYPE_N_BASECLASSES (type);
- if (!looking_for_baseclass)
+ if (!m_looking_for_baseclass)
for (i = type->num_fields () - 1; i >= nbases; i--)
{
const char *t_field_name = TYPE_FIELD_NAME (type, i);
- if (t_field_name && (strcmp_iw (t_field_name, name) == 0))
+ if (t_field_name && (strcmp_iw (t_field_name, m_name) == 0))
{
struct value *v;
@@ -1821,7 +1899,8 @@ do_search_struct_field (const char *name, struct value *arg1, LONGEST offset,
v = value_static_field (type, i);
else
v = value_primitive_field (arg1, offset, i, type);
- *result_ptr = v;
+
+ update_result (v, offset);
return;
}
@@ -1845,7 +1924,6 @@ do_search_struct_field (const char *name, struct value *arg1, LONGEST offset,
represented as a struct, with a member for each
<variant field>. */
- struct value *v = NULL;
LONGEST new_offset = offset;
/* This is pretty gross. In G++, the offset in an
@@ -1859,16 +1937,7 @@ do_search_struct_field (const char *name, struct value *arg1, LONGEST offset,
&& TYPE_FIELD_BITPOS (field_type, 0) == 0))
new_offset += TYPE_FIELD_BITPOS (type, i) / 8;
- do_search_struct_field (name, arg1, new_offset,
- field_type,
- looking_for_baseclass, &v,
- last_boffset,
- outermost_type);
- if (v)
- {
- *result_ptr = v;
- return;
- }
+ search (arg1, new_offset, field_type);
}
}
}
@@ -1880,10 +1949,10 @@ do_search_struct_field (const char *name, struct value *arg1, LONGEST offset,
/* If we are looking for baseclasses, this is what we get when
we hit them. But it could happen that the base part's member
name is not yet filled in. */
- int found_baseclass = (looking_for_baseclass
+ int found_baseclass = (m_looking_for_baseclass
&& TYPE_BASECLASS_NAME (type, i) != NULL
- && (strcmp_iw (name,
- TYPE_BASECLASS_NAME (type,
+ && (strcmp_iw (m_name,
+ TYPE_BASECLASS_NAME (type,
i)) == 0));
LONGEST boffset = value_embedded_offset (arg1) + offset;
@@ -1924,28 +1993,17 @@ do_search_struct_field (const char *name, struct value *arg1, LONGEST offset,
if (found_baseclass)
v = v2;
else
- {
- do_search_struct_field (name, v2, 0,
- TYPE_BASECLASS (type, i),
- looking_for_baseclass,
- result_ptr, last_boffset,
- outermost_type);
- }
+ search (v2, 0, TYPE_BASECLASS (type, i));
}
else if (found_baseclass)
v = value_primitive_field (arg1, offset, i, type);
else
{
- do_search_struct_field (name, arg1,
- offset + TYPE_BASECLASS_BITPOS (type,
- i) / 8,
- basetype, looking_for_baseclass,
- result_ptr, last_boffset,
- outermost_type);
+ search (arg1, offset + TYPE_BASECLASS_BITPOS (type, i) / 8,
+ basetype);
}
- update_search_result (result_ptr, v, last_boffset,
- boffset, name, outermost_type);
+ update_result (v, boffset);
}
}
@@ -1960,12 +2018,55 @@ static struct value *
search_struct_field (const char *name, struct value *arg1,
struct type *type, int looking_for_baseclass)
{
- struct value *result = NULL;
- LONGEST boffset = 0;
+ struct_field_searcher searcher (name, type, looking_for_baseclass);
- do_search_struct_field (name, arg1, 0, type, looking_for_baseclass,
- &result, &boffset, type);
- return result;
+ searcher.search (arg1, 0, type);
+
+ if (!looking_for_baseclass)
+ {
+ const auto &fields = searcher.fields ();
+
+ if (fields.empty ())
+ return nullptr;
+ else if (fields.size () == 1)
+ return fields[0].field_value;
+ else
+ {
+ std::string candidates;
+
+ for (auto &&candidate : fields)
+ {
+ gdb_assert (!candidate.path.empty ());
+
+ struct type *field_type = value_type (candidate.field_value);
+ struct type *struct_type = candidate.path.back ();
+
+ std::string path;
+ bool first = true;
+ for (struct type *t : candidate.path)
+ {
+ if (first)
+ first = false;
+ else
+ path += " -> ";
+ path += t->name ();
+ }
+
+ candidates += string_printf ("\n '%s %s::%s' (%s)",
+ TYPE_SAFE_NAME (field_type),
+ TYPE_SAFE_NAME (struct_type),
+ name,
+ path.c_str ());
+ }
+
+ error (_("Request for member '%s' is ambiguous in type '%s'."
+ " Candidates are:%s"),
+ name, TYPE_SAFE_NAME (type),
+ candidates.c_str ());
+ }
+ }
+ else
+ return searcher.baseclass ();
}
/* Helper function used by value_struct_elt to recurse through