diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 0218bc29c1..2bbccd5816 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,10 @@ +2018-10-08 Weimin Pan + + PR c++/16841 + * valops.c (get_virtual_base_offset): New function. + (value_struct_elt_for_reference): Use it to get virtual base offset + and add it in calculating class member address. + 2018-10-08 John Darrington * dwarf2read.c (dwarf2_cu) : New field. diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 9473646aa0..d35abb5152 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2018-10-08 Weimin Pan + + PR c++/16841 + * gdb.cp/virtbase2.cc: New file. + * gdb.cp/virtbase2.exp: New file. + 2018-10-06 Tom Tromey PR python/19399: diff --git a/gdb/testsuite/gdb.cp/virtbase2.cc b/gdb/testsuite/gdb.cp/virtbase2.cc new file mode 100644 index 0000000000..8b2fb77b46 --- /dev/null +++ b/gdb/testsuite/gdb.cp/virtbase2.cc @@ -0,0 +1,49 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2018 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 . */ + +struct super { + int w; + super () : w(17) {} +}; + +struct superbase { + int x; + superbase () : x(22) {} +}; + +struct base : superbase { + int i; + double d; + base() : i(55), d(6.25) {} +}; + +typedef base tbase; +struct derived: virtual super, virtual tbase +{ + void func_d() { } +}; + +struct foo: virtual derived +{ + void func_f() { } +}; + +int main() +{ + derived().func_d(); + foo().func_f(); +} diff --git a/gdb/testsuite/gdb.cp/virtbase2.exp b/gdb/testsuite/gdb.cp/virtbase2.exp new file mode 100644 index 0000000000..cfb48cf1ca --- /dev/null +++ b/gdb/testsuite/gdb.cp/virtbase2.exp @@ -0,0 +1,111 @@ +# Copyright 2018 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 . + +# Make sure printing virtual base class data member works correctly (PR16841) + +if { [skip_cplus_tests] } { continue } + +standard_testfile .cc + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { + return -1 +} + +if {![runto_main]} then { + perror "couldn't run to main" + continue +} + +# From a list of nested scopes, generate all possible ways of accessing something +# in those scopes. For example, with the argument {foo bar baz}, this proc will +# return: +# - {} (empty string) +# - baz:: +# - bar:: +# - bar::baz:: +# - foo:: +# - foo::baz:: +# - foo::bar:: +# - foo::bar::baz:: + +proc make_scope_list { scopes } { + if { [llength $scopes] == 1 } { + return [list "" "${scopes}::"] + } + + # Pop the first element, save the first scope. + set this_scope [lindex $scopes 0] + set scopes [lreplace $scopes 0 0] + + set child_result [make_scope_list $scopes] + + # Add a copy of the child's result without this scope... + set result $child_result + + # ... and a copy of the child's result with this scope. + foreach r $child_result { + lappend result "${this_scope}::$r" + } + + return $result +} + +proc test_variables_in_base { scopes } { + foreach scope [make_scope_list $scopes] { + gdb_test "print ${scope}i" " = 55" + gdb_test "print ${scope}d" " = 6.25" + gdb_test "print ${scope}x" " = 22" + } +} + +proc test_variables_in_superbase { scopes } { + foreach scope [make_scope_list $scopes] { + gdb_test "print ${scope}x" " = 22" + } +} + +proc test_variables_in_super { scopes } { + foreach scope [make_scope_list $scopes] { + gdb_test "print ${scope}w" " = 17" + } +} + +with_test_prefix "derived::func_d" { + gdb_breakpoint "derived::func_d" + gdb_continue_to_breakpoint "continue to derived::func_d" + test_variables_in_base {derived base} + test_variables_in_superbase {derived base superbase} + test_variables_in_superbase {base superbase} + test_variables_in_superbase {derived superbase} + test_variables_in_superbase {superbase} + test_variables_in_superbase {base} + test_variables_in_super {super} + test_variables_in_super {derived super} +} + +with_test_prefix "foo::func_f" { + gdb_breakpoint "foo::func_f" + gdb_continue_to_breakpoint "continue to foo::func_f" + test_variables_in_base {foo derived base} + test_variables_in_base {foo base} + test_variables_in_base {base} + test_variables_in_superbase {superbase} + test_variables_in_superbase {foo superbase} + test_variables_in_superbase {foo derived superbase} + test_variables_in_superbase {foo derived base superbase} + test_variables_in_super {super} + test_variables_in_super {foo super} + test_variables_in_super {foo derived super} +} diff --git a/gdb/valops.c b/gdb/valops.c index e6e11a66c2..99b12751dc 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -3321,6 +3321,49 @@ compare_parameters (struct type *t1, struct type *t2, int skip_artificial) return 0; } +/* C++: Given an aggregate type VT, and a class type CLS, search + recursively for CLS using value V; If found, store the offset + which is either fetched from the virtual base pointer if CLS + is virtual or accumulated offset of its parent classes if + CLS is non-virtual in *BOFFS, set ISVIRT to indicate if CLS + is virtual, and return true. If not found, return false. */ + +static bool +get_baseclass_offset (struct type *vt, struct type *cls, + struct value *v, int *boffs, bool *isvirt) +{ + for (int i = 0; i < TYPE_N_BASECLASSES (vt); i++) + { + struct type *t = TYPE_FIELD_TYPE (vt, i); + if (types_equal (t, cls)) + { + if (BASETYPE_VIA_VIRTUAL (vt, i)) + { + const gdb_byte *adr = value_contents_for_printing (v); + *boffs = baseclass_offset (vt, i, adr, value_offset (v), + value_as_long (v), v); + *isvirt = true; + } + else + *isvirt = false; + return true; + } + + if (get_baseclass_offset (check_typedef (t), cls, v, boffs, isvirt)) + { + if (*isvirt == false) /* Add non-virtual base offset. */ + { + const gdb_byte *adr = value_contents_for_printing (v); + *boffs += baseclass_offset (vt, i, adr, value_offset (v), + value_as_long (v), v); + } + return true; + } + } + + return false; +} + /* C++: Given an aggregate type CURTYPE, and a member name NAME, return the address of this member as a "pointer to member" type. If INTYPE is non-null, then it will be the type of the member we @@ -3374,7 +3417,7 @@ value_struct_elt_for_reference (struct type *domain, int offset, struct value *v = value_of_this_silent (current_language); if (v != NULL) { - struct value *ptr; + struct value *ptr, *this_v = v; long mem_offset; struct type *type, *tmp; @@ -3385,6 +3428,24 @@ value_struct_elt_for_reference (struct type *domain, int offset, tmp = lookup_pointer_type (TYPE_SELF_TYPE (type)); v = value_cast_pointers (tmp, v, 1); mem_offset = value_as_long (ptr); + if (domain != curtype) + { + /* Find class offset of type CURTYPE from either its + parent type DOMAIN or the type of implied this. */ + int boff = 0; + bool isvirt = false; + if (get_baseclass_offset (domain, curtype, v, &boff, + &isvirt)) + mem_offset += boff; + else + { + struct type *t = check_typedef (value_type (this_v)); + t = check_typedef (TYPE_TARGET_TYPE (t)); + if (get_baseclass_offset (t, curtype, this_v, + &boff, &isvirt)) + mem_offset += boff; + } + } tmp = lookup_pointer_type (TYPE_TARGET_TYPE (type)); result = value_from_pointer (tmp, value_as_long (v) + mem_offset);