PR c++/9065:
authorTom Tromey <tromey@redhat.com>
Mon, 15 Apr 2013 17:36:14 +0000 (17:36 +0000)
committerTom Tromey <tromey@redhat.com>
Mon, 15 Apr 2013 17:36:14 +0000 (17:36 +0000)
* 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.

14 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/breakpoint.c
gdb/c-exp.y
gdb/cp-abi.c
gdb/cp-abi.h
gdb/eval.c
gdb/expprint.c
gdb/gnu-v3-abi.c
gdb/parse.c
gdb/std-operator.def
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.cp/typeid.cc [new file with mode: 0644]
gdb/testsuite/gdb.cp/typeid.exp [new file with mode: 0644]

index d96ab8b588def3764546af3ffbc2ea209c047c55..8730b7d7d298d8019944fcfaafee5ac7d9bf43b4 100644 (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
index 55f25d8325b112ea67c559f78fe9817fcb755274..c78a4fb70f52c22bb729b9602faff5b2ecedef08 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -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
index bd1ca5db5397c310966b7772f99d64e8a4868644..b0dd9b4ba236be5bc52323bdc3a32a802c0483f4 100644 (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:
 
index c3c7f1697baba2e32d346dcd7850fa272218729a..dd032d2ead0f86963760506d4226bdd0623072a0 100644 (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
index 8e9d545328e534e53243fd58e6f53404d7870ca6..7ac2a204c6300ae895efcf4b931e28aba63913c6 100644 (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)
 {
index 96e9aeac22dd4df972f9d1de0ae961d5774194e5..d68e2ec8be3106dac24d89b0977b0a4cb4d74afb 100644 (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);
 };
index a91ba2271abd589541a6d5910ff4dfe73ace7d86..f04baeee8e426577368e7169a8509a51fc5a7b9c 100644 (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
index 69055348cf64993e4f3a640ae9132d8968885f62..ea9b5601f52cb5fccb515a0f33e698f967d9dd79 100644 (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:
       {
index b3585a661f2fd676ff11c1a95d44748bcac97ca4..7649a48ea72af5776910222e6ebfb1f26bd8f47b 100644 (file)
 
 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;
 }
index aa8f09caf556e0c421679be190ea8355dde7036d..13699a85e6bc4e71af590ae1402ddf6ee2125a6a 100644 (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;
index 467c1411f5d231429767ccf373700cac8109028f..c4f33d90fd00745db93e1eacb011b8d27e9a720f 100644 (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)
index 65269805839bbc4da10dac48c706c725f488ea4c..7da3ff0e23e22eff7bd068a4ddb95a8b157a32e8 100644 (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.
diff --git a/gdb/testsuite/gdb.cp/typeid.cc b/gdb/testsuite/gdb.cp/typeid.cc
new file mode 100644 (file)
index 0000000..7120031
--- /dev/null
@@ -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;
+}
diff --git a/gdb/testsuite/gdb.cp/typeid.exp b/gdb/testsuite/gdb.cp/typeid.exp
new file mode 100644 (file)
index 0000000..2096ad9
--- /dev/null
@@ -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
+}
This page took 0.053897 seconds and 4 git commands to generate.