diff options
author | Pedro Alves <palves@redhat.com> | 2017-09-04 20:21:16 +0100 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2017-09-04 20:21:16 +0100 |
commit | 3693fdb3c8ec14bd8ecb4ebb39e4384b330a2999 (patch) | |
tree | 595184fd6316fdadcc281f8cd5f7e48d1868b052 /gdb/testsuite/gdb.cp/local-static.exp | |
parent | e68cb8e00166d2f8a9e23c60f68bec04ef0a90f0 (diff) | |
download | gdb-3693fdb3c8ec14bd8ecb4ebb39e4384b330a2999.zip gdb-3693fdb3c8ec14bd8ecb4ebb39e4384b330a2999.tar.gz gdb-3693fdb3c8ec14bd8ecb4ebb39e4384b330a2999.tar.bz2 |
Make "p S::method() const::static_var" work too
Trying to print a function local static variable of a const-qualified
method still doesn't work after the previous fixes:
(gdb) p 'S::method() const'::static_var
$1 = {i1 = 1, i2 = 2, i3 = 3}
(gdb) p S::method() const::static_var
No symbol "static_var" in specified context.
The reason is that the expression parser/evaluator loses the "const",
and the above unquoted case is just like trying to print a variable of
the non-const overload, if it exists, even. As if the above unquoted
case had been written as:
(gdb) p S::method()::static_var
No symbol "static_var" in specified context.
We can see the problem without static vars in the picture. With:
struct S
{
void method ();
void method () const;
};
Compare:
(gdb) print 'S::method(void) const'
$1 = {void (const S * const)} 0x400606 <S::method() const>
(gdb) print S::method(void) const
$2 = {void (S * const)} 0x4005d8 <S::method()> # wrong method!
That's what we need to fix. If we fix that, the function local static
case starts working.
The grammar production for function/method types is this one:
exp: exp '(' parameter_typelist ')' const_or_volatile
This results in a TYPE_INSTANCE expression evaluator operator. For
the example above, we get something like this ("set debug expression 1"):
...
0 TYPE_INSTANCE 1 TypeInstance: Type @0x560fda958be0 (void)
5 OP_SCOPE Type @0x560fdaa544d8 (S) Field name: `method'
...
While evaluating TYPE_INSTANCE, we end up in
value_struct_elt_for_reference, trying to find the method named
"method" that has the prototype recorded in TYPE_INSTANCE. In this
case, TYPE_INSTANCE says that we're looking for a method that has
"(void)" as parameters (that's what "1 TypeInstance: Type
@0x560fda958be0 (void)" above means. The trouble is that nowhere in
this mechanism do we communicate to value_struct_elt_for_reference
that we're looking for the _const_ overload.
value_struct_elt_for_reference only compared parameters, and the
non-const "method()" overload has matching parameters, so it's
considered the right match...
Conveniently, the "const_or_volatile" production in the grammar
already records "const" and "volatile" info in the type stack. The
type stack is not used in this code path, but we can borrow the
information. The patch converts the info in the type stack to an
"instance flags" enum, and adds that as another element in
TYPE_INSTANCE operators. This type instance flags is then applied to
the temporary type that is passed to value_struct_elt_for_reference
for matching.
The other side of the problem is that methods in the debug info aren't
marked const/volatile, so with that in place, the matching never finds
const/volatile-qualified methods.
The problem is that in the DWARF, there's no indication at all whether
a method is const/volatile qualified... For example (c++filt applied
to the linkage name for convenience):
<2><d3>: Abbrev Number: 6 (DW_TAG_subprogram)
<d4> DW_AT_external : 1
<d4> DW_AT_name : (indirect string, offset: 0x3df): method
<d8> DW_AT_decl_file : 1
<d9> DW_AT_decl_line : 58
<da> DW_AT_linkage_name: (indirect string, offset: 0x5b2): S::method() const
<de> DW_AT_declaration : 1
<de> DW_AT_object_pointer: <0xe6>
<e2> DW_AT_sibling : <0xec>
I see the same with both GCC and Clang. The patch works around this
by extracting the cv qualification from the "const" and "volatile" in
the demangled name. This will need further tweaking for "&" and
"const &" overloads, but we don't support them in the parser yet,
anyway.
The TYPE_CONST changes were necessary otherwise the comparisons in valops.c:
if (TYPE_CONST (intype) != TYPE_FN_FIELD_CONST (f, j))
continue;
would fail, because when both TYPE_CONST() TYPE_FN_FIELD_CONST() were
true, their values were different.
BTW, I'm recording the const/volatile-ness of methods in the
TYPE_FN_FIELD info because #1 - I'm not sure it's kosher to change the
method's type directly (vs having to call make_cv_type to create a new
type), and #2 it's what stabsread.c does:
...
case 'A': /* Normal functions. */
new_sublist->fn_field.is_const = 0;
new_sublist->fn_field.is_volatile = 0;
(*pp)++;
break;
case 'B': /* `const' member functions. */
new_sublist->fn_field.is_const = 1;
new_sublist->fn_field.is_volatile = 0;
...
After all this, this finally all works:
print S::method(void) const
$1 = {void (const S * const)} 0x400606 <S::method() const>
(gdb) p S::method() const::static_var
$2 = {i1 = 1, i2 = 2, i3 = 3}
gdb/ChangeLog:
2017-09-04 Pedro Alves <palves@redhat.com>
* c-exp.y (function_method, function_method_void): Add current
instance flags to TYPE_INSTANCE.
* dwarf2read.c (check_modifier): New.
(compute_delayed_physnames): Assert that only C++ adds delayed
physnames. Mark fn_fields as const/volatile depending on
physname.
* eval.c (make_params): New type_instance_flags parameter. Use
it as the new type's instance flags.
(evaluate_subexp_standard) <TYPE_INSTANCE>: Extract the instance
flags element and pass it to make_params.
* expprint.c (print_subexp_standard) <TYPE_INSTANCE>: Handle
instance flags element.
(dump_subexp_body_standard) <TYPE_INSTANCE>: Likewise.
* gdbtypes.h: Include "enum-flags.h".
(type_instance_flags): New enum-flags type.
(TYPE_CONST, TYPE_VOLATILE, TYPE_RESTRICT, TYPE_ATOMIC)
(TYPE_CODE_SPACE, TYPE_DATA_SPACE): Return boolean.
* parse.c (operator_length_standard) <TYPE_INSTANCE>: Adjust.
(follow_type_instance_flags): New function.
(operator_check_standard) <TYPE_INSTANCE>: Adjust.
* parser-defs.h (follow_type_instance_flags): Declare.
* valops.c (value_struct_elt_for_reference): const/volatile must
match too.
gdb/testsuite/ChangeLog:
2017-09-04 Pedro Alves <palves@redhat.com>
* gdb.base/func-static.c (S::method const, S::method volatile)
(S::method volatile const): New methods.
(c_s, v_s, cv_s): New instances.
(main): Call method() on them.
* gdb.base/func-static.exp (syntax_re, cannot_resolve_re): New variables.
(cannot_resolve): New procedure.
(cxx_scopes_list): Test cv methods. Add print-scope-quote and
print-quote-unquoted columns.
(do_test): Test printing each scope too.
Diffstat (limited to 'gdb/testsuite/gdb.cp/local-static.exp')
-rw-r--r-- | gdb/testsuite/gdb.cp/local-static.exp | 125 |
1 files changed, 109 insertions, 16 deletions
diff --git a/gdb/testsuite/gdb.cp/local-static.exp b/gdb/testsuite/gdb.cp/local-static.exp index 581efa3..5e8eaaa 100644 --- a/gdb/testsuite/gdb.cp/local-static.exp +++ b/gdb/testsuite/gdb.cp/local-static.exp @@ -19,28 +19,98 @@ standard_testfile .c +# A few expected errors. +set syntax_re "A syntax error in expression, near.*" +set cannot_resolve_re "Cannot resolve method S::method to any overloaded instance" + +# Build an "Cannot resolve method ..." expected error string for +# method METH. +# +proc cannot_resolve {meth} { + return "Cannot resolve method $meth to any overloaded instance" +} + # A list of scopes that have the static variables that we want to # print. Each entry has, in order, the scope/function name, and the -# prefix used by the static variables. (The prefix exists to make it -# easier to debug the test if something goes wrong.) - - #SCOPE #PREFIX +# prefix used by the static variables. The prefix exists both to make +# it easier to debug the test if something goes wrong, and, to make +# sure that printing the static local of one method overload doesn't +# find the variables of the wrong overload. +# +# While at it, we also try printing each scope without the static +# local, to check that the parse copes with cv overloads without +# quoting. That's what the third and forth columns are for. Note +# that printing "func()" is different from "func(void)". The former +# is an inferior function call, while the latter is a reference to the +# function. + + #SCOPE #PREFIX #PRINT-SCOPE-QUOTED + #PRINT-SCOPE-UNQUOTED (opt) set cxx_scopes_list { - {"S::method()" "S_M"} - {"S::static_method()" "S_SM"} - {"S::inline_method()" "S_IM"} - {"S::static_inline_method()" "S_SIM"} - {"S2<int>::method()" "S2_M"} - {"S2<int>::static_method()" "S2_SM"} - {"S2<int>::inline_method()" "S2_IM"} - {"S2<int>::static_inline_method()" "S2_SIM"} - {"free_func()" "FF"} - {"free_inline_func()" "FIF"} + {"S::method()" "S_M" {= \\{void \\(S \\* const\\)\\} $hex <S::method\\(\\)>} + {[cannot_resolve "S::method"]}} + + {"S::method() const" "S_M_C" {= \\{void \\(const S \\* const\\)\\} $hex <S::method\\(\\) const>} + $syntax_re} + + {"S::method() volatile" "S_M_V" {= \\{void \\(volatile S \\* const\\)\\} $hex <S::method\\(\\) volatile>} + $syntax_re} + + {"S::method() const volatile" "S_M_CV" {= \\{void \\(const volatile S \\* const\\)\\} $hex <S::method\\(\\) const volatile>} + $syntax_re} + + {"S::method() volatile const" "S_M_CV" {= \\{void \\(const volatile S \\* const\\)\\} $hex <S::method\\(\\) const volatile>} + $syntax_re} + + {"S::method(void)" "S_M" {= \\{void \\(S \\* const\\)\\} $hex <S::method\\(\\)>}} + {"S::method(void) const" "S_M_C" {= \\{void \\(const S \\* const\\)\\} $hex <S::method\\(\\) const>}} + {"S::method(void) volatile" "S_M_V" {= \\{void \\(volatile S \\* const\\)\\} $hex <S::method\\(\\) volatile>}} + {"S::method(void) const volatile" "S_M_CV" {= \\{void \\(const volatile S \\* const\\)\\} $hex <S::method\\(\\) const volatile>}} + {"S::method(void) volatile const" "S_M_CV" {= \\{void \\(const volatile S \\* const\\)\\} $hex <S::method\\(\\) const volatile>}} + + {"S::static_method()" "S_SM" {= \\{void \\(void\\)\\} $hex <S::static_method\\(\\)>} + "void"} + + {"S::static_method(void)" "S_SM" {= \\{void \\(void\\)\\} $hex <S::static_method\\(\\)>}} + + {"S::inline_method()" "S_IM" {= \\{void \\(S \\* const\\)\\} $hex <S::inline_method\\(\\)>} + {[cannot_resolve "S::inline_method"]}} + + {"S::inline_method(void)" "S_IM" {= \\{void \\(S \\* const\\)\\} $hex <S::inline_method\\(\\)>}} + + {"S::static_inline_method()" "S_SIM" {= \\{void \\(void\\)\\} $hex <S::static_inline_method\\(\\)>} + "void"} + + {"S::static_inline_method(void)" "S_SIM" {= \\{void \\(void\\)\\} $hex <S::static_inline_method\\(\\)>}} + + {"S2<int>::method()" "S2_M" {= \\{void \\(S2<int> \\* const\\)\\} $hex <S2<int>::method\\(\\)>} + {[cannot_resolve "S2<int>::method"]}} + + {"S2<int>::static_method()" "S2_SM" {= \\{void \\(void\\)\\} $hex <S2<int>::static_method\\(\\)>} + "void"} + + {"S2<int>::inline_method()" "S2_IM" {= \\{void \\(S2<int> \\* const\\)\\} $hex <S2<int>::inline_method\\(\\)>} + {[cannot_resolve "S2<int>::inline_method"]}} + + {"S2<int>::static_inline_method()" "S2_SIM" {= \\{void \\(void\\)\\} $hex <S2<int>::static_inline_method\\(\\)>} + "void"} + + {"free_func" "FF" {= \\{void \\(void\\)\\} $hex <free_func\\(\\)>}} + + {"free_func()" "FF" {= \\{void \\(void\\)\\} $hex <free_func\\(\\)>} + "void"} + + {"free_func(void)" "FF" {= \\{void \\(void\\)\\} $hex <free_func\\(\\)>}} + + {"free_inline_func()" "FIF" {= \\{void \\(void\\)\\} $hex <free_inline_func\\(\\)>} + "void"} + + {"free_inline_func(void)" "FIF" {= \\{void \\(void\\)\\} $hex <free_inline_func\\(\\)>}} } set c_scopes_list { - {"free_func" "FF"} - {"free_inline_func" "FIF"} + {"free_func" "FF" {= \\{void \\(void\\)\\} $hex <free_func>}} + {"free_inline_func" "FIF" {= \\{void \\(void\\)\\} $hex <free_inline_func>}} } # A list of all the static varibles defined in each scope. The first @@ -91,6 +161,29 @@ proc do_test {lang} { set scopes_list $cxx_scopes_list } + # Print each scope/function using these syntaxes: + # + # "(gdb) p 'S::method() const'" # quoted + # "(gdb) p S::method() const" # unquoted + # + foreach scope_line $scopes_list { + set scope [lindex $scope_line 0] + + set print_quoted_re [lindex $scope_line 2] + set print_quoted_re [uplevel 1 "subst -nobackslashes -nocommands \"$print_quoted_re\""] + + set print_unquoted_re [lindex $scope_line 3] + set print_unquoted_re [uplevel 1 "subst -nobackslashes -nocommands \"$print_unquoted_re\""] + + gdb_test "print '${scope}'" $print_quoted_re + + if {$print_unquoted_re != ""} { + gdb_test "print ${scope}" $print_unquoted_re + } else { + gdb_test "print ${scope}" $print_quoted_re + } + } + # Print each variable using these syntaxes: # # 'func()'::var |