+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.
+
2017-09-04 Pedro Alves <palves@redhat.com>
* cp-namespace.c (cp_search_static_and_baseclasses): Handle
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);
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);
}
}
+/* 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<size_t N>
+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
{
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;
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;
+ }
+ }
}
}
}
}
-/* 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)
}
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;
-
- 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;
+ {
+ 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 (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);
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);
/* 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;
}
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)
{
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);
#include "hashtab.h"
#include "common/offset-type.h"
+#include "common/enum-flags.h"
/* Forward declarations for prototypes. */
struct field;
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). */
/* * 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. */
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
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;
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 *
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))
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,
+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.
+
2017-09-04 Pedro Alves <palves@redhat.com>
* gdb.base/local-static.exp: Also test with
}
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 ()
{
#ifdef __cplusplus
s.method ();
+ c_s.method ();
+ v_s.method ();
+ cv_s.method ();
s.inline_method ();
S::static_method ();
S::static_inline_method ();
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
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
{
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))