diff --git a/gdb/ChangeLog b/gdb/ChangeLog index cdcbfaee93..196fe60950 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,29 @@ +2017-09-04 Pedro Alves + + * 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) : Extract the instance + flags element and pass it to make_params. + * expprint.c (print_subexp_standard) : Handle + instance flags element. + (dump_subexp_body_standard) : 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) : Adjust. + (follow_type_instance_flags): New function. + (operator_check_standard) : Adjust. + * parser-defs.h (follow_type_instance_flags): Declare. + * valops.c (value_struct_elt_for_reference): const/volatile must + match too. + 2017-09-04 Pedro Alves * cp-namespace.c (cp_search_static_and_baseclasses): Handle diff --git a/gdb/c-exp.y b/gdb/c-exp.y index f7f098ba52..bcc3e12dfc 100644 --- a/gdb/c-exp.y +++ b/gdb/c-exp.y @@ -558,6 +558,11 @@ function_method: exp '(' parameter_typelist ')' const_or_volatile LONGEST len = VEC_length (type_ptr, type_list); write_exp_elt_opcode (pstate, TYPE_INSTANCE); + /* Save the const/volatile qualifiers as + recorded by the const_or_volatile + production's actions. */ + write_exp_elt_longcst (pstate, + follow_type_instance_flags ()); write_exp_elt_longcst (pstate, len); for (i = 0; VEC_iterate (type_ptr, type_list, i, type_elt); @@ -571,6 +576,9 @@ function_method: exp '(' parameter_typelist ')' const_or_volatile function_method_void: exp '(' ')' const_or_volatile { write_exp_elt_opcode (pstate, TYPE_INSTANCE); + /* See above. */ + write_exp_elt_longcst (pstate, + follow_type_instance_flags ()); write_exp_elt_longcst (pstate, 0); write_exp_elt_longcst (pstate, 0); write_exp_elt_opcode (pstate, TYPE_INSTANCE); diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index 893e04d83f..6678b33364 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -8042,6 +8042,23 @@ free_delayed_list (void *ptr) } } +/* Check whether [PHYSNAME, PHYSNAME+LEN) ends with a modifier like + "const" / "volatile". If so, decrements LEN by the length of the + modifier and return true. Otherwise return false. */ + +template +static bool +check_modifier (const char *physname, size_t &len, const char (&mod)[N]) +{ + size_t mod_len = sizeof (mod) - 1; + if (len > mod_len && startswith (physname + (len - mod_len), mod)) + { + len -= mod_len; + return true; + } + return false; +} + /* Compute the physnames of any methods on the CU's method list. The computation of method physnames is delayed in order to avoid the @@ -8053,6 +8070,12 @@ compute_delayed_physnames (struct dwarf2_cu *cu) { int i; struct delayed_method_info *mi; + + /* Only C++ delays computing physnames. */ + if (VEC_empty (delayed_method_info, cu->method_list)) + return; + gdb_assert (cu->language == language_cplus); + for (i = 0; VEC_iterate (delayed_method_info, cu->method_list, i, mi) ; ++i) { const char *physname; @@ -8061,6 +8084,26 @@ compute_delayed_physnames (struct dwarf2_cu *cu) physname = dwarf2_physname (mi->name, mi->die, cu); TYPE_FN_FIELD_PHYSNAME (fn_flp->fn_fields, mi->index) = physname ? physname : ""; + + /* Since there's no tag to indicate whether a method is a + const/volatile overload, extract that information out of the + demangled name. */ + if (physname != NULL) + { + size_t len = strlen (physname); + + while (1) + { + if (physname[len] == ')') /* shortcut */ + break; + else if (check_modifier (physname, len, " const")) + TYPE_FN_FIELD_CONST (fn_flp->fn_fields, mi->index) = 1; + else if (check_modifier (physname, len, " volatile")) + TYPE_FN_FIELD_VOLATILE (fn_flp->fn_fields, mi->index) = 1; + else + break; + } + } } } diff --git a/gdb/eval.c b/gdb/eval.c index 7d129f0e77..5a434b9cea 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -642,18 +642,22 @@ ptrmath_type_p (const struct language_defn *lang, struct type *type) } } -/* Constructs a fake method with the given parameter types. - This function is used by the parser to construct an "expected" - type for method overload resolution. */ +/* Constructs a fake method with the given parameter types. This + function is used by the parser to construct an "expected" type for + method overload resolution. FLAGS is used as instance flags of the + new type, in order to be able to make the new type represent a + const/volatile overload. */ static struct type * -make_params (int num_types, struct type **param_types) +make_params (type_instance_flags flags, + int num_types, struct type **param_types) { struct type *type = XCNEW (struct type); TYPE_MAIN_TYPE (type) = XCNEW (struct main_type); TYPE_LENGTH (type) = 1; TYPE_CODE (type) = TYPE_CODE_METHOD; TYPE_CHAIN (type) = type; + TYPE_INSTANCE_FLAGS (type) = flags; if (num_types > 0) { if (param_types[num_types - 1] == NULL) @@ -2039,18 +2043,22 @@ evaluate_subexp_standard (struct type *expect_type, } case TYPE_INSTANCE: - nargs = longest_to_int (exp->elts[pc + 1].longconst); - arg_types = (struct type **) alloca (nargs * sizeof (struct type *)); - for (ix = 0; ix < nargs; ++ix) - arg_types[ix] = exp->elts[pc + 1 + ix + 1].type; + { + type_instance_flags flags + = (type_instance_flag_value) longest_to_int (exp->elts[pc + 1].longconst); + nargs = longest_to_int (exp->elts[pc + 2].longconst); + arg_types = (struct type **) alloca (nargs * sizeof (struct type *)); + for (ix = 0; ix < nargs; ++ix) + arg_types[ix] = exp->elts[pc + 2 + ix + 1].type; - expect_type = make_params (nargs, arg_types); - *(pos) += 3 + nargs; - arg1 = evaluate_subexp_standard (expect_type, exp, pos, noside); - xfree (TYPE_FIELDS (expect_type)); - xfree (TYPE_MAIN_TYPE (expect_type)); - xfree (expect_type); - return arg1; + expect_type = make_params (flags, nargs, arg_types); + *(pos) += 4 + nargs; + arg1 = evaluate_subexp_standard (expect_type, exp, pos, noside); + xfree (TYPE_FIELDS (expect_type)); + xfree (TYPE_MAIN_TYPE (expect_type)); + xfree (expect_type); + return arg1; + } case BINOP_CONCAT: arg1 = evaluate_subexp_with_coercion (exp, pos, noside); diff --git a/gdb/expprint.c b/gdb/expprint.c index fad20e8151..2c16b497fc 100644 --- a/gdb/expprint.c +++ b/gdb/expprint.c @@ -543,11 +543,15 @@ print_subexp_standard (struct expression *exp, int *pos, case TYPE_INSTANCE: { - LONGEST count = exp->elts[pc + 1].longconst; + type_instance_flags flags + = (type_instance_flag_value) longest_to_int (exp->elts[pc + 1].longconst); + LONGEST count = exp->elts[pc + 2].longconst; + /* The FLAGS. */ + (*pos)++; /* The COUNT. */ (*pos)++; - fputs_unfiltered ("TypesInstance(", stream); + fputs_unfiltered ("TypeInstance(", stream); while (count-- > 0) { type_print (exp->elts[(*pos)++].type, "", stream, 0); @@ -558,6 +562,12 @@ print_subexp_standard (struct expression *exp, int *pos, /* Ending COUNT and ending TYPE_INSTANCE. */ (*pos) += 2; print_subexp (exp, pos, stream, PREC_PREFIX); + + if (flags & TYPE_INSTANCE_FLAG_CONST) + fputs_unfiltered (",const", stream); + if (flags & TYPE_INSTANCE_FLAG_VOLATILE) + fputs_unfiltered (",volatile", stream); + fputs_unfiltered (")", stream); return; } @@ -1019,9 +1029,9 @@ dump_subexp_body_standard (struct expression *exp, case TYPE_INSTANCE: { - LONGEST len; - - len = exp->elts[elt++].longconst; + type_instance_flags flags + = (type_instance_flag_value) longest_to_int (exp->elts[elt++].longconst); + LONGEST len = exp->elts[elt++].longconst; fprintf_filtered (stream, "%s TypeInstance: ", plongest (len)); while (len-- > 0) { @@ -1034,6 +1044,22 @@ dump_subexp_body_standard (struct expression *exp, if (len > 0) fputs_filtered (", ", stream); } + + fprintf_filtered (stream, " Flags: %s (", hex_string (flags)); + bool space = false; + auto print_one = [&] (const char *mod) + { + if (space) + fputs_filtered (" ", stream); + space = true; + fprintf_filtered (stream, mod); + }; + if (flags & TYPE_INSTANCE_FLAG_CONST) + print_one ("const"); + if (flags & TYPE_INSTANCE_FLAG_VOLATILE) + print_one ("volatile"); + fprintf_filtered (stream, ")"); + /* Ending LEN and ending TYPE_INSTANCE. */ elt += 2; elt = dump_subexp (exp, stream, elt); diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index d2018a8108..9d9b09ffc9 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -46,6 +46,7 @@ #include "hashtab.h" #include "common/offset-type.h" +#include "common/enum-flags.h" /* Forward declarations for prototypes. */ struct field; @@ -196,6 +197,8 @@ enum type_instance_flag_value TYPE_INSTANCE_FLAG_ATOMIC = (1 << 8) }; +DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags); + /* * Unsigned integer type. If this is not set for a TYPE_CODE_INT, the type is signed (unless TYPE_NOSIGN (below) is set). */ @@ -304,25 +307,25 @@ enum type_instance_flag_value /* * Constant type. If this is set, the corresponding type has a const modifier. */ -#define TYPE_CONST(t) (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_CONST) +#define TYPE_CONST(t) ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_CONST) != 0) /* * Volatile type. If this is set, the corresponding type has a volatile modifier. */ #define TYPE_VOLATILE(t) \ - (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_VOLATILE) + ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_VOLATILE) != 0) /* * Restrict type. If this is set, the corresponding type has a restrict modifier. */ #define TYPE_RESTRICT(t) \ - (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_RESTRICT) + ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_RESTRICT) != 0) /* * Atomic type. If this is set, the corresponding type has an _Atomic modifier. */ #define TYPE_ATOMIC(t) \ - (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_ATOMIC) + ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_ATOMIC) != 0) /* * True if this type represents either an lvalue or lvalue reference type. */ @@ -349,10 +352,10 @@ enum type_instance_flag_value is instruction space, and for data objects is data memory. */ #define TYPE_CODE_SPACE(t) \ - (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_CODE_SPACE) + ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_CODE_SPACE) != 0) #define TYPE_DATA_SPACE(t) \ - (TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_DATA_SPACE) + ((TYPE_INSTANCE_FLAGS (t) & TYPE_INSTANCE_FLAG_DATA_SPACE) != 0) /* * Address class flags. Some environments provide for pointers whose size is different from that of a normal pointer or address diff --git a/gdb/parse.c b/gdb/parse.c index e1fa229230..7971f6c78c 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -927,7 +927,7 @@ operator_length_standard (const struct expression *expr, int endpos, break; case TYPE_INSTANCE: - oplen = 4 + longest_to_int (expr->elts[endpos - 2].longconst); + oplen = 5 + longest_to_int (expr->elts[endpos - 2].longconst); args = 1; break; @@ -1642,6 +1642,33 @@ push_typelist (VEC (type_ptr) *list) push_type (tp_function_with_arguments); } +/* Pop the type stack and return a type_instance_flags that + corresponds the const/volatile qualifiers on the stack. This is + called by the C++ parser when parsing methods types, and as such no + other kind of type in the type stack is expected. */ + +type_instance_flags +follow_type_instance_flags () +{ + type_instance_flags flags = 0; + + for (;;) + switch (pop_type ()) + { + case tp_end: + return flags; + case tp_const: + flags |= TYPE_INSTANCE_FLAG_CONST; + break; + case tp_volatile: + flags |= TYPE_INSTANCE_FLAG_VOLATILE; + break; + default: + gdb_assert_not_reached ("unrecognized tp_ value in follow_types"); + } +} + + /* Pop the type stack and return the type which corresponds to FOLLOW_TYPE as modified by all the stuff on the stack. */ struct type * @@ -1821,11 +1848,11 @@ operator_check_standard (struct expression *exp, int pos, case TYPE_INSTANCE: { - LONGEST arg, nargs = elts[pos + 1].longconst; + LONGEST arg, nargs = elts[pos + 2].longconst; for (arg = 0; arg < nargs; arg++) { - struct type *type = elts[pos + 2 + arg].type; + struct type *type = elts[pos + 3 + arg].type; struct objfile *objfile = TYPE_OBJFILE (type); if (objfile && (*objfile_func) (objfile, data)) diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h index fadc2ef2b1..f7ba7f088c 100644 --- a/gdb/parser-defs.h +++ b/gdb/parser-defs.h @@ -266,6 +266,8 @@ extern const char *op_name_standard (enum exp_opcode); extern struct type *follow_types (struct type *); +extern type_instance_flags follow_type_instance_flags (); + extern void null_post_parser (struct expression **, int); extern int parse_float (const char *p, int len, DOUBLEST *d, diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index ddea68bd53..e53192283e 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2017-09-04 Pedro Alves + + * 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. + 2017-09-04 Pedro Alves * gdb.base/local-static.exp: Also test with diff --git a/gdb/testsuite/gdb.cp/local-static.c b/gdb/testsuite/gdb.cp/local-static.c index 5bfff8d662..0f4c4f0b5f 100644 --- a/gdb/testsuite/gdb.cp/local-static.c +++ b/gdb/testsuite/gdb.cp/local-static.c @@ -53,10 +53,17 @@ struct S } 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 () @@ -64,6 +71,24 @@ 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 () { @@ -127,6 +152,9 @@ main () #ifdef __cplusplus s.method (); + c_s.method (); + v_s.method (); + cv_s.method (); s.inline_method (); S::static_method (); S::static_inline_method (); diff --git a/gdb/testsuite/gdb.cp/local-static.exp b/gdb/testsuite/gdb.cp/local-static.exp index 581efa396a..5e8eaaa974 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.) +# 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 + #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::method()" "S2_M"} - {"S2::static_method()" "S2_SM"} - {"S2::inline_method()" "S2_IM"} - {"S2::static_inline_method()" "S2_SIM"} - {"free_func()" "FF"} - {"free_inline_func()" "FIF"} + {"S::method()" "S_M" {= \\{void \\(S \\* const\\)\\} $hex } + {[cannot_resolve "S::method"]}} + + {"S::method() const" "S_M_C" {= \\{void \\(const S \\* const\\)\\} $hex } + $syntax_re} + + {"S::method() volatile" "S_M_V" {= \\{void \\(volatile S \\* const\\)\\} $hex } + $syntax_re} + + {"S::method() const volatile" "S_M_CV" {= \\{void \\(const volatile S \\* const\\)\\} $hex } + $syntax_re} + + {"S::method() volatile const" "S_M_CV" {= \\{void \\(const volatile S \\* const\\)\\} $hex } + $syntax_re} + + {"S::method(void)" "S_M" {= \\{void \\(S \\* const\\)\\} $hex }} + {"S::method(void) const" "S_M_C" {= \\{void \\(const S \\* const\\)\\} $hex }} + {"S::method(void) volatile" "S_M_V" {= \\{void \\(volatile S \\* const\\)\\} $hex }} + {"S::method(void) const volatile" "S_M_CV" {= \\{void \\(const volatile S \\* const\\)\\} $hex }} + {"S::method(void) volatile const" "S_M_CV" {= \\{void \\(const volatile S \\* const\\)\\} $hex }} + + {"S::static_method()" "S_SM" {= \\{void \\(void\\)\\} $hex } + "void"} + + {"S::static_method(void)" "S_SM" {= \\{void \\(void\\)\\} $hex }} + + {"S::inline_method()" "S_IM" {= \\{void \\(S \\* const\\)\\} $hex } + {[cannot_resolve "S::inline_method"]}} + + {"S::inline_method(void)" "S_IM" {= \\{void \\(S \\* const\\)\\} $hex }} + + {"S::static_inline_method()" "S_SIM" {= \\{void \\(void\\)\\} $hex } + "void"} + + {"S::static_inline_method(void)" "S_SIM" {= \\{void \\(void\\)\\} $hex }} + + {"S2::method()" "S2_M" {= \\{void \\(S2 \\* const\\)\\} $hex ::method\\(\\)>} + {[cannot_resolve "S2::method"]}} + + {"S2::static_method()" "S2_SM" {= \\{void \\(void\\)\\} $hex ::static_method\\(\\)>} + "void"} + + {"S2::inline_method()" "S2_IM" {= \\{void \\(S2 \\* const\\)\\} $hex ::inline_method\\(\\)>} + {[cannot_resolve "S2::inline_method"]}} + + {"S2::static_inline_method()" "S2_SIM" {= \\{void \\(void\\)\\} $hex ::static_inline_method\\(\\)>} + "void"} + + {"free_func" "FF" {= \\{void \\(void\\)\\} $hex }} + + {"free_func()" "FF" {= \\{void \\(void\\)\\} $hex } + "void"} + + {"free_func(void)" "FF" {= \\{void \\(void\\)\\} $hex }} + + {"free_inline_func()" "FIF" {= \\{void \\(void\\)\\} $hex } + "void"} + + {"free_inline_func(void)" "FIF" {= \\{void \\(void\\)\\} $hex }} } set c_scopes_list { - {"free_func" "FF"} - {"free_inline_func" "FIF"} + {"free_func" "FF" {= \\{void \\(void\\)\\} $hex }} + {"free_inline_func" "FIF" {= \\{void \\(void\\)\\} $hex }} } # 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 diff --git a/gdb/valops.c b/gdb/valops.c index f5e3aabbf8..19f96c8ddc 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -3424,6 +3424,11 @@ value_struct_elt_for_reference (struct type *domain, int offset, { for (j = 0; j < len; ++j) { + if (TYPE_CONST (intype) != TYPE_FN_FIELD_CONST (f, j)) + continue; + if (TYPE_VOLATILE (intype) != TYPE_FN_FIELD_VOLATILE (f, j)) + continue; + if (compare_parameters (TYPE_FN_FIELD_TYPE (f, j), intype, 0) || compare_parameters (TYPE_FN_FIELD_TYPE (f, j), intype, 1))