PR c++/9065:

* NEWS: Update.
	* breakpoint.c (watchpoint_exp_is_const): Add OP_TYPEID.
	* c-exp.y (TYPEID): New token.
	(exp): Add new TYPEID productions.
	(ident_tokens): Add "typeid".
	* cp-abi.c (cplus_typeid, cplus_typeid_type): New functions.
	* cp-abi.h (cplus_typeid, cplus_typeid_type): Declare.
	(struct cp_abi_ops) <get_typeid, get_typeid_type>: New fields.
	* eval.c (evaluate_subexp_standard) <OP_TYPEID>: New case.
	* expprint.c (dump_subexp_body_standard) <OP_TYPEID>: New
	case.
	* gnu-v3-abi.c (std_type_info_gdbarch_data): New global.
	(build_std_type_info_type, gnuv3_get_typeid_type)
	(gnuv3_get_typeid): New functions.
	(init_gnuv3_ops): Initialize std_type_info_gdbarch_data.  Set
	new fields on ABI object.
	* parse.c (operator_length_standard) <OP_TYPEID>: New case.
	* std-operator.def (OP_TYPEID): New.
gdb/testsuite
	* gdb.cp/typeid.cc: New file.
	* gdb.cp/typeid.exp: New file.
This commit is contained in:
Tom Tromey 2013-04-15 17:36:14 +00:00
parent 715c6909e2
commit 6e72ca205c
14 changed files with 385 additions and 1 deletions

View File

@ -1,3 +1,25 @@
2013-04-15 Tom Tromey <tromey@redhat.com>
PR c++/9065:
* NEWS: Update.
* breakpoint.c (watchpoint_exp_is_const): Add OP_TYPEID.
* c-exp.y (TYPEID): New token.
(exp): Add new TYPEID productions.
(ident_tokens): Add "typeid".
* cp-abi.c (cplus_typeid, cplus_typeid_type): New functions.
* cp-abi.h (cplus_typeid, cplus_typeid_type): Declare.
(struct cp_abi_ops) <get_typeid, get_typeid_type>: New fields.
* eval.c (evaluate_subexp_standard) <OP_TYPEID>: New case.
* expprint.c (dump_subexp_body_standard) <OP_TYPEID>: New
case.
* gnu-v3-abi.c (std_type_info_gdbarch_data): New global.
(build_std_type_info_type, gnuv3_get_typeid_type)
(gnuv3_get_typeid): New functions.
(init_gnuv3_ops): Initialize std_type_info_gdbarch_data. Set
new fields on ABI object.
* parse.c (operator_length_standard) <OP_TYPEID>: New case.
* std-operator.def (OP_TYPEID): New.
2013-04-15 Tom Tromey <tromey@redhat.com>
* elfread.c (elf_symtab_read): Install versioned symbol under

View File

@ -29,6 +29,8 @@ show remote trace-status-packet
* Newly installed $prefix/bin/gcore acts as a shell interface for the
GDB command gcore.
* GDB now implements the the C++ 'typeid' operator.
* MI changes
** The -trace-save MI command can optionally save trace buffer in Common

View File

@ -10317,6 +10317,7 @@ watchpoint_exp_is_const (const struct expression *exp)
case OP_TYPE:
case OP_TYPEOF:
case OP_DECLTYPE:
case OP_TYPEID:
case OP_NAME:
case OP_OBJC_NSSTRING:

View File

@ -234,6 +234,7 @@ static void c_print_token (FILE *file, int type, YYSTYPE value);
%token ENTRY
%token TYPEOF
%token DECLTYPE
%token TYPEID
/* Special type cases, put in to allow the parser to distinguish different
legal basetypes. */
@ -346,6 +347,14 @@ exp : exp DECREMENT %prec UNARY
{ write_exp_elt_opcode (UNOP_POSTDECREMENT); }
;
exp : TYPEID '(' exp ')' %prec UNARY
{ write_exp_elt_opcode (OP_TYPEID); }
;
exp : TYPEID '(' type_exp ')' %prec UNARY
{ write_exp_elt_opcode (OP_TYPEID); }
;
exp : SIZEOF exp %prec UNARY
{ write_exp_elt_opcode (UNOP_SIZEOF); }
;
@ -2290,7 +2299,9 @@ static const struct token ident_tokens[] =
{"__typeof", TYPEOF, OP_TYPEOF, 0 },
{"typeof", TYPEOF, OP_TYPEOF, FLAG_SHADOW },
{"__decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX },
{"decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX | FLAG_SHADOW }
{"decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX | FLAG_SHADOW },
{"typeid", TYPEID, OP_TYPEID, FLAG_CXX}
};
/* When we find that lexptr (the global var defined in parse.c) is

View File

@ -179,6 +179,26 @@ cplus_print_vtable (struct value *value)
(*current_cp_abi.print_vtable) (value);
}
/* See cp-abi.h. */
struct value *
cplus_typeid (struct value *value)
{
if (current_cp_abi.get_typeid == NULL)
error (_("GDB cannot find the typeid on this target"));
return (*current_cp_abi.get_typeid) (value);
}
/* See cp-abi.h. */
struct type *
cplus_typeid_type (struct gdbarch *gdbarch)
{
if (current_cp_abi.get_typeid_type == NULL)
error (_("GDB cannot find the type for 'typeid' on this target"));
return (*current_cp_abi.get_typeid_type) (gdbarch);
}
int
cp_pass_by_reference (struct type *type)
{

View File

@ -178,6 +178,16 @@ void cplus_make_method_ptr (struct type *type, gdb_byte *CONTENTS,
void cplus_print_vtable (struct value *value);
/* Implement 'typeid': find the type info for VALUE, if possible. If
the type info cannot be found, throw an exception. */
extern struct value *cplus_typeid (struct value *value);
/* Return the type of 'typeid' for the current C++ ABI on the given
architecture. */
extern struct type *cplus_typeid_type (struct gdbarch *gdbarch);
/* Determine if we are currently in a C++ thunk. If so, get the
address of the routine we are thunking to and continue to there
instead. */
@ -219,6 +229,8 @@ struct cp_abi_ops
struct value * (*method_ptr_to_value) (struct value **,
struct value *);
void (*print_vtable) (struct value *);
struct value *(*get_typeid) (struct value *value);
struct type *(*get_typeid_type) (struct gdbarch *gdbarch);
CORE_ADDR (*skip_trampoline) (struct frame_info *, CORE_ADDR);
int (*pass_by_reference) (struct type *type);
};

View File

@ -2798,6 +2798,23 @@ evaluate_subexp_standard (struct type *expect_type,
else
error (_("Attempt to use a type as an expression"));
case OP_TYPEID:
{
struct value *result;
enum exp_opcode sub_op = exp->elts[*pos].opcode;
if (sub_op == OP_TYPE || sub_op == OP_DECLTYPE || sub_op == OP_TYPEOF)
result = evaluate_subexp (NULL_TYPE, exp, pos,
EVAL_AVOID_SIDE_EFFECTS);
else
result = evaluate_subexp (NULL_TYPE, exp, pos, noside);
if (noside != EVAL_NORMAL)
return allocate_value (cplus_typeid_type (exp->gdbarch));
return cplus_typeid (result);
}
default:
/* Removing this case and compiling with gcc -Wall reveals that
a lot of cases are hitting this case. Some of these should

View File

@ -953,6 +953,11 @@ dump_subexp_body_standard (struct expression *exp,
elt = dump_subexp (exp, stream, elt);
fprintf_filtered (stream, ")");
break;
case OP_TYPEID:
fprintf_filtered (stream, "typeid (");
elt = dump_subexp (exp, stream, elt);
fprintf_filtered (stream, ")");
break;
case STRUCTOP_STRUCT:
case STRUCTOP_PTR:
{

View File

@ -34,6 +34,12 @@
static struct cp_abi_ops gnu_v3_abi_ops;
/* A gdbarch key for std::type_info, in the event that it can't be
found in the debug info. */
static struct gdbarch_data *std_type_info_gdbarch_data;
static int
gnuv3_is_vtable_name (const char *name)
{
@ -984,6 +990,154 @@ gnuv3_print_vtable (struct value *value)
do_cleanups (cleanup);
}
/* Return a GDB type representing `struct std::type_info', laid out
appropriately for ARCH.
We use this function as the gdbarch per-architecture data
initialization function. */
static void *
build_std_type_info_type (struct gdbarch *arch)
{
struct type *t;
struct field *field_list, *field;
int offset;
struct type *void_ptr_type
= builtin_type (arch)->builtin_data_ptr;
struct type *char_type
= builtin_type (arch)->builtin_char;
struct type *char_ptr_type
= make_pointer_type (make_cv_type (1, 0, char_type, NULL), NULL);
field_list = xmalloc (sizeof (struct field [2]));
memset (field_list, 0, sizeof (struct field [2]));
field = &field_list[0];
offset = 0;
/* The vtable. */
FIELD_NAME (*field) = "_vptr.type_info";
FIELD_TYPE (*field) = void_ptr_type;
SET_FIELD_BITPOS (*field, offset * TARGET_CHAR_BIT);
offset += TYPE_LENGTH (FIELD_TYPE (*field));
field++;
/* The name. */
FIELD_NAME (*field) = "__name";
FIELD_TYPE (*field) = char_ptr_type;
SET_FIELD_BITPOS (*field, offset * TARGET_CHAR_BIT);
offset += TYPE_LENGTH (FIELD_TYPE (*field));
field++;
gdb_assert (field == (field_list + 2));
t = arch_type (arch, TYPE_CODE_STRUCT, offset, NULL);
TYPE_NFIELDS (t) = field - field_list;
TYPE_FIELDS (t) = field_list;
TYPE_TAG_NAME (t) = "gdb_gnu_v3_type_info";
INIT_CPLUS_SPECIFIC (t);
return t;
}
/* Implement the 'get_typeid_type' method. */
static struct type *
gnuv3_get_typeid_type (struct gdbarch *gdbarch)
{
struct symbol *typeinfo;
struct type *typeinfo_type;
typeinfo = lookup_symbol ("std::type_info", NULL, STRUCT_DOMAIN, NULL);
if (typeinfo == NULL)
typeinfo_type = gdbarch_data (gdbarch, std_type_info_gdbarch_data);
else
typeinfo_type = SYMBOL_TYPE (typeinfo);
return typeinfo_type;
}
/* Implement the 'get_typeid' method. */
static struct value *
gnuv3_get_typeid (struct value *value)
{
struct type *typeinfo_type;
struct type *type;
struct gdbarch *gdbarch;
struct cleanup *cleanup;
struct value *result;
char *typename, *canonical;
/* We have to handle values a bit trickily here, to allow this code
to work properly with non_lvalue values that are really just
disguised types. */
if (value_lval_const (value) == lval_memory)
value = coerce_ref (value);
type = check_typedef (value_type (value));
/* In the non_lvalue case, a reference might have slipped through
here. */
if (TYPE_CODE (type) == TYPE_CODE_REF)
type = check_typedef (TYPE_TARGET_TYPE (type));
/* Ignore top-level cv-qualifiers. */
type = make_cv_type (0, 0, type, NULL);
gdbarch = get_type_arch (type);
typename = type_to_string (type);
if (typename == NULL)
error (_("cannot find typeinfo for unnamed type"));
cleanup = make_cleanup (xfree, typename);
/* We need to canonicalize the type name here, because we do lookups
using the demangled name, and so we must match the format it
uses. E.g., GDB tends to use "const char *" as a type name, but
the demangler uses "char const *". */
canonical = cp_canonicalize_string (typename);
if (canonical != NULL)
{
make_cleanup (xfree, canonical);
typename = canonical;
}
typeinfo_type = gnuv3_get_typeid_type (gdbarch);
/* We check for lval_memory because in the "typeid (type-id)" case,
the type is passed via a not_lval value object. */
if (TYPE_CODE (type) == TYPE_CODE_CLASS
&& value_lval_const (value) == lval_memory
&& gnuv3_dynamic_class (type))
{
struct value *vtable, *typeinfo_value;
CORE_ADDR address = value_address (value) + value_embedded_offset (value);
vtable = gnuv3_get_vtable (gdbarch, type, address);
if (vtable == NULL)
error (_("cannot find typeinfo for object of type '%s'"), typename);
typeinfo_value = value_field (vtable, vtable_field_type_info);
result = value_ind (value_cast (make_pointer_type (typeinfo_type, NULL),
typeinfo_value));
}
else
{
char *sym_name;
struct minimal_symbol *minsym;
sym_name = concat ("typeinfo for ", typename, (char *) NULL);
make_cleanup (xfree, sym_name);
minsym = lookup_minimal_symbol (sym_name, NULL, NULL);
if (minsym == NULL)
error (_("could not find typeinfo symbol for '%s'"), typename);
result = value_at_lazy (typeinfo_type, SYMBOL_VALUE_ADDRESS (minsym));
}
do_cleanups (cleanup);
return result;
}
/* Determine if we are currently in a C++ thunk. If so, get the address
of the routine we are thunking to and continue to there instead. */
@ -1116,6 +1270,8 @@ init_gnuv3_ops (void)
{
vtable_type_gdbarch_data
= gdbarch_data_register_post_init (build_gdb_vtable_type);
std_type_info_gdbarch_data
= gdbarch_data_register_post_init (build_std_type_info_type);
gnu_v3_abi_ops.shortname = "gnu-v3";
gnu_v3_abi_ops.longname = "GNU G++ Version 3 ABI";
@ -1134,6 +1290,8 @@ init_gnuv3_ops (void)
gnu_v3_abi_ops.make_method_ptr = gnuv3_make_method_ptr;
gnu_v3_abi_ops.method_ptr_to_value = gnuv3_method_ptr_to_value;
gnu_v3_abi_ops.print_vtable = gnuv3_print_vtable;
gnu_v3_abi_ops.get_typeid = gnuv3_get_typeid;
gnu_v3_abi_ops.get_typeid_type = gnuv3_get_typeid_type;
gnu_v3_abi_ops.skip_trampoline = gnuv3_skip_trampoline;
gnu_v3_abi_ops.pass_by_reference = gnuv3_pass_by_reference;
}

View File

@ -972,6 +972,7 @@ operator_length_standard (const struct expression *expr, int endpos,
case UNOP_TRUNC:
case OP_TYPEOF:
case OP_DECLTYPE:
case OP_TYPEID:
oplen = 1;
args = 1;
break;

View File

@ -316,3 +316,6 @@ OP (OP_TYPEOF)
evaluated solely for its type. This is similar to typeof, but has
slight different semantics. */
OP (OP_DECLTYPE)
/* The typeid operator. This has one expression argument. */
OP (OP_TYPEID)

View File

@ -1,3 +1,8 @@
2013-04-15 Tom Tromey <tromey@redhat.com>
* gdb.cp/typeid.cc: New file.
* gdb.cp/typeid.exp: New file.
2013-04-15 Tom Tromey <tromey@redhat.com>
* gdb.cp/exception.exp: Add "catch rethrow" tests.

View File

@ -0,0 +1,60 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2013 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/>. */
#include <typeinfo>
int i;
char *cp;
const char *ccp;
char ca[5];
struct Base
{
virtual ~Base() { }
};
struct VB1 : public virtual Base
{
};
struct VB2 : public virtual Base
{
};
struct Derived : public VB1, VB2
{
};
Derived d;
Base *b = &d;
VB1 *vb1 = &d;
VB1 *vb2 = &d;
const Base *bv = &d;
int main ()
{
const std::type_info &xi = typeid(i);
const std::type_info &xcp = typeid(cp);
const std::type_info &xccp = typeid(ccp);
const std::type_info &xca = typeid(ca);
const std::type_info &xd = typeid(d);
const std::type_info &xb = typeid(b);
return 0;
}

View File

@ -0,0 +1,67 @@
# Copyright 2013 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/>.
standard_testfile .cc
if {[skip_cplus_tests]} {
return -1
}
if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
return -1
}
proc do_typeid_tests {started} {
global hex
# We might see the standard type or gdb's internal type.
set type_re "(std::type_info|struct gdb_gnu_v3_type_info)"
foreach simple_var {i cp ccp ca b} {
gdb_test "print &typeid($simple_var)" \
" = \\($type_re \\*\\) $hex.*"
# Note that we test pointer equality rather than object
# equality here. That is because std::type_info's operator==
# is not present in the libstdc++ .so.
gdb_test "print &typeid($simple_var) == &typeid(typeof($simple_var))" \
" = true"
}
# typeid for these is Derived. Don't try these tests until the
# inferior has started.
if {$started} {
foreach der_var {*b *vb1 *vb2 *bv d {const Derived} {const Derived &}} {
gdb_test "print &typeid($der_var)" \
" = \\($type_re \\*\\) $hex.*"
gdb_test "print &typeid($der_var) == &typeid(d)" \
" = true"
}
}
}
with_test_prefix "before starting" {
do_typeid_tests 0
}
if ![runto_main] {
untested typeid
return -1
}
with_test_prefix "after starting" {
do_typeid_tests 1
}