mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-30 12:44:10 +08:00
3693fdb3c8
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.
171 lines
2.9 KiB
C
171 lines
2.9 KiB
C
/* This testcase is part of GDB, the GNU debugger.
|
|
|
|
Copyright 2002-2017 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 <http://www.gnu.org/licenses/>. */
|
|
|
|
struct aggregate
|
|
{
|
|
int i1;
|
|
int i2;
|
|
int i3;
|
|
};
|
|
|
|
void keepalive_float (double *var) { }
|
|
void keepalive_int (int *var) { }
|
|
void keepalive_aggregate (struct aggregate *var) { }
|
|
|
|
#define PREFIXIFY(PREFIX, VAR) \
|
|
PREFIX ## _ ## VAR
|
|
|
|
#define DEF_STATICS(PREFIX) \
|
|
static int PREFIXIFY(PREFIX, s_var_int) = 4; \
|
|
static double PREFIXIFY(PREFIX, s_var_float) = 3.14f; \
|
|
static struct aggregate PREFIXIFY(PREFIX, s_var_aggregate) \
|
|
= { 1, 2, 3 }; \
|
|
\
|
|
keepalive_int (&PREFIXIFY(PREFIX, s_var_int)); \
|
|
keepalive_float (&PREFIXIFY(PREFIX, s_var_float)); \
|
|
keepalive_aggregate (&PREFIXIFY(PREFIX, s_var_aggregate));
|
|
|
|
#ifdef __cplusplus
|
|
|
|
struct S
|
|
{
|
|
void inline_method ()
|
|
{
|
|
DEF_STATICS (S_IM);
|
|
}
|
|
static void static_inline_method ()
|
|
{
|
|
DEF_STATICS (S_SIM);
|
|
}
|
|
|
|
void method ();
|
|
void method () const;
|
|
void method () volatile;
|
|
void method () volatile const;
|
|
|
|
static void static_method ();
|
|
};
|
|
|
|
S s;
|
|
const S c_s = {};
|
|
volatile S v_s = {};
|
|
const volatile S cv_s = {};
|
|
|
|
void
|
|
S::method ()
|
|
{
|
|
DEF_STATICS (S_M);
|
|
}
|
|
|
|
void
|
|
S::method () const
|
|
{
|
|
DEF_STATICS (S_M_C);
|
|
}
|
|
|
|
void
|
|
S::method () volatile
|
|
{
|
|
DEF_STATICS (S_M_V);
|
|
}
|
|
|
|
void
|
|
S::method () const volatile
|
|
{
|
|
DEF_STATICS (S_M_CV);
|
|
}
|
|
|
|
void
|
|
S::static_method ()
|
|
{
|
|
DEF_STATICS (S_SM);
|
|
}
|
|
|
|
template <typename T>
|
|
struct S2
|
|
{
|
|
void method ();
|
|
static void static_method ();
|
|
|
|
void inline_method ()
|
|
{
|
|
DEF_STATICS (S2_IM);
|
|
}
|
|
|
|
static void static_inline_method ()
|
|
{
|
|
DEF_STATICS (S2_SIM);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
void
|
|
S2<T>::method ()
|
|
{
|
|
DEF_STATICS (S2_M);
|
|
}
|
|
|
|
template<typename T>
|
|
void
|
|
S2<T>::static_method ()
|
|
{
|
|
DEF_STATICS (S2_SM);
|
|
}
|
|
|
|
S2<int> s2;
|
|
|
|
#endif
|
|
|
|
void
|
|
free_func (void)
|
|
{
|
|
DEF_STATICS (FF);
|
|
}
|
|
|
|
static inline void
|
|
free_inline_func (void)
|
|
{
|
|
DEF_STATICS (FIF);
|
|
}
|
|
|
|
int
|
|
main ()
|
|
{
|
|
for (int i = 0; i < 1000; i++)
|
|
{
|
|
free_func ();
|
|
free_inline_func ();
|
|
|
|
#ifdef __cplusplus
|
|
s.method ();
|
|
c_s.method ();
|
|
v_s.method ();
|
|
cv_s.method ();
|
|
s.inline_method ();
|
|
S::static_method ();
|
|
S::static_inline_method ();
|
|
|
|
s2.method ();
|
|
s2.inline_method ();
|
|
S2<int>::static_method ();
|
|
S2<int>::static_inline_method ();
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|