aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ChangeLog31
-rw-r--r--gdb/c-exp.y65
-rw-r--r--gdb/eval.c16
-rw-r--r--gdb/expprint.c18
-rw-r--r--gdb/expression.h6
-rw-r--r--gdb/gdbtypes.c109
-rw-r--r--gdb/gdbtypes.h6
-rw-r--r--gdb/parse.c2
-rw-r--r--gdb/testsuite/ChangeLog6
-rw-r--r--gdb/testsuite/gdb.cp/casts.cc30
-rw-r--r--gdb/testsuite/gdb.cp/casts.exp72
-rw-r--r--gdb/valops.c252
-rw-r--r--gdb/value.h5
13 files changed, 602 insertions, 16 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index e45be31..9382e67 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,34 @@
+2010-01-18 Tom Tromey <tromey@redhat.com>
+
+ PR c++/9680:
+ * c-exp.y (REINTERPRET_CAST, DYNAMIC_CAST, STATIC_CAST)
+ (CONST_CAST): New tokens.
+ (exp): Add new productions.
+ (ident_tokens): Add const_cast, dynamic_cast, static_cast, and
+ reinterpret_cast.
+ (is_cast_operator): New function.
+ (yylex): Handle cast operators specially.
+ * eval.c (evaluate_subexp_standard) <UNOP_DYNAMIC_CAST,
+ UNOP_REINTERPRET_CAST>: New cases.
+ * expprint.c (print_subexp_standard): Likewise.
+ (op_name_standard): Likewise.
+ (dump_subexp_body_standard): Likewise.
+ * parse.c (operator_length_standard): Likewise.
+ * expression.h (enum exp_opcode): New constants UNOP_DYNAMIC_CAST,
+ UNOP_REINTERPRET_CAST.
+ * gdbtypes.c (class_types_same_p): New function.
+ (is_ancestor): Use it.
+ (is_public_ancestor): New function.
+ (is_unique_ancestor_worker): Likewise.
+ (is_unique_ancestor): Likewise.
+ * gdbtypes.h (class_types_same_p, is_public_ancestor)
+ (is_unique_ancestor): Declare.
+ * valops.c (value_reinterpret_cast): New function.
+ (dynamic_cast_check_1): Likewise.
+ (dynamic_cast_check_2): Likewise.
+ (value_dynamic_cast): Likewise.
+ * value.h (value_reinterpret_cast, value_dynamic_cast): Declare.
+
2010-01-18 Joel Brobecker <brobecker@adacore.com>
Fix build failure when building without Python support.
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 2ea5c6f..8e00979 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -206,6 +206,7 @@ static struct stoken operator_stoken (const char *);
%token ERROR
%token NEW DELETE
%type <sval> operator
+%token REINTERPRET_CAST DYNAMIC_CAST STATIC_CAST CONST_CAST
/* Special type cases, put in to allow the parser to distinguish different
legal basetypes. */
@@ -591,6 +592,32 @@ exp : SIZEOF '(' type ')' %prec UNARY
write_exp_elt_opcode (OP_LONG); }
;
+exp : REINTERPRET_CAST '<' type '>' '(' exp ')' %prec UNARY
+ { write_exp_elt_opcode (UNOP_REINTERPRET_CAST);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (UNOP_REINTERPRET_CAST); }
+ ;
+
+exp : STATIC_CAST '<' type '>' '(' exp ')' %prec UNARY
+ { write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (UNOP_CAST); }
+ ;
+
+exp : DYNAMIC_CAST '<' type '>' '(' exp ')' %prec UNARY
+ { write_exp_elt_opcode (UNOP_DYNAMIC_CAST);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (UNOP_DYNAMIC_CAST); }
+ ;
+
+exp : CONST_CAST '<' type '>' '(' exp ')' %prec UNARY
+ { /* We could do more error checking here, but
+ it doesn't seem worthwhile. */
+ write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (UNOP_CAST); }
+ ;
+
string_exp:
STRING
{
@@ -1894,7 +1921,12 @@ static const struct token ident_tokens[] =
{"or", OROR, BINOP_END, 1},
{"or_eq", ASSIGN_MODIFY, BINOP_BITWISE_IOR, 1},
{"xor", '^', OP_NULL, 1},
- {"xor_eq", ASSIGN_MODIFY, BINOP_BITWISE_XOR, 1}
+ {"xor_eq", ASSIGN_MODIFY, BINOP_BITWISE_XOR, 1},
+
+ {"const_cast", CONST_CAST, OP_NULL, 1 },
+ {"dynamic_cast", DYNAMIC_CAST, OP_NULL, 1 },
+ {"static_cast", STATIC_CAST, OP_NULL, 1 },
+ {"reinterpret_cast", REINTERPRET_CAST, OP_NULL, 1 }
};
/* When we find that lexptr (the global var defined in parse.c) is
@@ -1972,6 +2004,16 @@ scan_macro_cleanup (void *dummy)
obstack_free (&expansion_obstack, NULL);
}
+/* Return true iff the token represents a C++ cast operator. */
+
+static int
+is_cast_operator (const char *token, int len)
+{
+ return (! strncmp (token, "dynamic_cast", len)
+ || ! strncmp (token, "static_cast", len)
+ || ! strncmp (token, "reinterpret_cast", len)
+ || ! strncmp (token, "const_cast", len));
+}
/* The scope used for macro expansion. */
static struct macro_scope *expression_macro_scope;
@@ -2235,16 +2277,19 @@ yylex (void)
FIXME: This mishandles `print $a<4&&$a>3'. */
if (c == '<')
- {
- /* Scan ahead to get rest of the template specification. Note
- that we look ahead only when the '<' adjoins non-whitespace
- characters; for comparison expressions, e.g. "a < b > c",
- there must be spaces before the '<', etc. */
+ {
+ if (! is_cast_operator (tokstart, namelen))
+ {
+ /* Scan ahead to get rest of the template specification. Note
+ that we look ahead only when the '<' adjoins non-whitespace
+ characters; for comparison expressions, e.g. "a < b > c",
+ there must be spaces before the '<', etc. */
- char * p = find_template_name_end (tokstart + namelen);
- if (p)
- namelen = p - tokstart;
- break;
+ char * p = find_template_name_end (tokstart + namelen);
+ if (p)
+ namelen = p - tokstart;
+ }
+ break;
}
c = tokstart[++namelen];
}
diff --git a/gdb/eval.c b/gdb/eval.c
index 3dbbc8b..27b7895 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2413,6 +2413,22 @@ evaluate_subexp_standard (struct type *expect_type,
arg1 = value_cast (type, arg1);
return arg1;
+ case UNOP_DYNAMIC_CAST:
+ (*pos) += 2;
+ type = exp->elts[pc + 1].type;
+ arg1 = evaluate_subexp (type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ return value_dynamic_cast (type, arg1);
+
+ case UNOP_REINTERPRET_CAST:
+ (*pos) += 2;
+ type = exp->elts[pc + 1].type;
+ arg1 = evaluate_subexp (type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ return value_reinterpret_cast (type, arg1);
+
case UNOP_MEMVAL:
(*pos) += 2;
arg1 = evaluate_subexp (expect_type, exp, pos, noside);
diff --git a/gdb/expprint.c b/gdb/expprint.c
index 8c72fc3..e378831 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -410,6 +410,18 @@ print_subexp_standard (struct expression *exp, int *pos,
fputs_filtered (")", stream);
return;
+ case UNOP_DYNAMIC_CAST:
+ case UNOP_REINTERPRET_CAST:
+ fputs_filtered (opcode == UNOP_DYNAMIC_CAST ? "dynamic_cast"
+ : "reinterpret_cast", stream);
+ fputs_filtered ("<", stream);
+ (*pos) += 2;
+ type_print (exp->elts[pc + 1].type, "", stream, 0);
+ fputs_filtered ("> (", stream);
+ print_subexp (exp, pos, stream, PREC_PREFIX);
+ fputs_filtered (")", stream);
+ return;
+
case UNOP_MEMVAL:
(*pos) += 2;
if ((int) prec > (int) PREC_PREFIX)
@@ -730,6 +742,10 @@ op_name_standard (enum exp_opcode opcode)
return "OP_ARRAY";
case UNOP_CAST:
return "UNOP_CAST";
+ case UNOP_DYNAMIC_CAST:
+ return "UNOP_DYNAMIC_CAST";
+ case UNOP_REINTERPRET_CAST:
+ return "UNOP_REINTERPRET_CAST";
case UNOP_MEMVAL:
return "UNOP_MEMVAL";
case UNOP_MEMVAL_TLS:
@@ -1036,6 +1052,8 @@ dump_subexp_body_standard (struct expression *exp,
break;
case UNOP_MEMVAL:
case UNOP_CAST:
+ case UNOP_DYNAMIC_CAST:
+ case UNOP_REINTERPRET_CAST:
fprintf_filtered (stream, "Type @");
gdb_print_host_address (exp->elts[elt].type, stream);
fprintf_filtered (stream, " (");
diff --git a/gdb/expression.h b/gdb/expression.h
index e4e8598..ca216cf 100644
--- a/gdb/expression.h
+++ b/gdb/expression.h
@@ -233,6 +233,12 @@ enum exp_opcode
It casts the value of the following subexpression. */
UNOP_CAST,
+ /* The C++ dynamic_cast operator. */
+ UNOP_DYNAMIC_CAST,
+
+ /* The C++ reinterpret_cast operator. */
+ UNOP_REINTERPRET_CAST,
+
/* UNOP_MEMVAL is followed by a type pointer in the next exp_element
With another UNOP_MEMVAL at the end, this makes three exp_elements.
It casts the contents of the word addressed by the value of the
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 16b34ca..117606a 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1838,6 +1838,18 @@ is_integral_type (struct type *t)
|| (TYPE_CODE (t) == TYPE_CODE_BOOL)));
}
+/* A helper function which returns true if types A and B represent the
+ "same" class type. This is true if the types have the same main
+ type, or the same name. */
+
+int
+class_types_same_p (const struct type *a, const struct type *b)
+{
+ return (TYPE_MAIN_TYPE (a) == TYPE_MAIN_TYPE (b)
+ || (TYPE_NAME (a) && TYPE_NAME (b)
+ && !strcmp (TYPE_NAME (a), TYPE_NAME (b))));
+}
+
/* Check whether BASE is an ancestor or base class or DCLASS
Return 1 if so, and 0 if not.
Note: callers may want to check for identity of the types before
@@ -1852,18 +1864,103 @@ is_ancestor (struct type *base, struct type *dclass)
CHECK_TYPEDEF (base);
CHECK_TYPEDEF (dclass);
- if (base == dclass)
- return 1;
- if (TYPE_NAME (base) && TYPE_NAME (dclass)
- && !strcmp (TYPE_NAME (base), TYPE_NAME (dclass)))
+ if (class_types_same_p (base, dclass))
return 1;
for (i = 0; i < TYPE_N_BASECLASSES (dclass); i++)
- if (is_ancestor (base, TYPE_BASECLASS (dclass, i)))
- return 1;
+ {
+ if (is_ancestor (base, TYPE_BASECLASS (dclass, i)))
+ return 1;
+ }
return 0;
}
+
+/* Like is_ancestor, but only returns true when BASE is a public
+ ancestor of DCLASS. */
+
+int
+is_public_ancestor (struct type *base, struct type *dclass)
+{
+ int i;
+
+ CHECK_TYPEDEF (base);
+ CHECK_TYPEDEF (dclass);
+
+ if (class_types_same_p (base, dclass))
+ return 1;
+
+ for (i = 0; i < TYPE_N_BASECLASSES (dclass); ++i)
+ {
+ if (! BASETYPE_VIA_PUBLIC (dclass, i))
+ continue;
+ if (is_public_ancestor (base, TYPE_BASECLASS (dclass, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* A helper function for is_unique_ancestor. */
+
+static int
+is_unique_ancestor_worker (struct type *base, struct type *dclass,
+ int *offset,
+ const bfd_byte *contents, CORE_ADDR address)
+{
+ int i, count = 0;
+
+ CHECK_TYPEDEF (base);
+ CHECK_TYPEDEF (dclass);
+
+ for (i = 0; i < TYPE_N_BASECLASSES (dclass) && count < 2; ++i)
+ {
+ struct type *iter = check_typedef (TYPE_BASECLASS (dclass, i));
+ int this_offset = baseclass_offset (dclass, i, contents, address);
+
+ if (this_offset == -1)
+ error (_("virtual baseclass botch"));
+
+ if (class_types_same_p (base, iter))
+ {
+ /* If this is the first subclass, set *OFFSET and set count
+ to 1. Otherwise, if this is at the same offset as
+ previous instances, do nothing. Otherwise, increment
+ count. */
+ if (*offset == -1)
+ {
+ *offset = this_offset;
+ count = 1;
+ }
+ else if (this_offset == *offset)
+ {
+ /* Nothing. */
+ }
+ else
+ ++count;
+ }
+ else
+ count += is_unique_ancestor_worker (base, iter, offset,
+ contents + this_offset,
+ address + this_offset);
+ }
+
+ return count;
+}
+
+/* Like is_ancestor, but only returns true if BASE is a unique base
+ class of the type of VAL. */
+
+int
+is_unique_ancestor (struct type *base, struct value *val)
+{
+ int offset = -1;
+
+ return is_unique_ancestor_worker (base, value_type (val), &offset,
+ value_contents (val),
+ value_address (val)) == 1;
+}
+
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 8e73ca0..380f72a 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -1367,8 +1367,14 @@ extern int get_vptr_fieldno (struct type *, struct type **);
extern int get_discrete_bounds (struct type *, LONGEST *, LONGEST *);
+extern int class_types_same_p (const struct type *, const struct type *);
+
extern int is_ancestor (struct type *, struct type *);
+extern int is_public_ancestor (struct type *, struct type *);
+
+extern int is_unique_ancestor (struct type *, struct value *);
+
/* Overload resolution */
#define LENGTH_MATCH(bv) ((bv)->rank[0])
diff --git a/gdb/parse.c b/gdb/parse.c
index 2885506..d5a199b 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -853,6 +853,8 @@ operator_length_standard (struct expression *expr, int endpos,
case BINOP_VAL:
case UNOP_CAST:
+ case UNOP_DYNAMIC_CAST:
+ case UNOP_REINTERPRET_CAST:
case UNOP_MEMVAL:
oplen = 3;
args = 1;
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 3611875..4d6b55a 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,4 +1,10 @@
2010-01-18 Tom Tromey <tromey@redhat.com>
+
+ PR c++/9680:
+ * gdb.cp/casts.cc: Add new classes and variables.
+ * gdb.cp/casts.exp: Test new operators.
+
+2010-01-18 Tom Tromey <tromey@redhat.com>
Thiago Jung Bauermann <bauerman@br.ibm.com>
* gdb.python/source2.py: New file.
diff --git a/gdb/testsuite/gdb.cp/casts.cc b/gdb/testsuite/gdb.cp/casts.cc
index 6ecd340..543db89 100644
--- a/gdb/testsuite/gdb.cp/casts.cc
+++ b/gdb/testsuite/gdb.cp/casts.cc
@@ -10,6 +10,30 @@ struct B: public A
B (int aa, int bb): A (aa), b(bb) {}
};
+
+struct Alpha
+{
+ virtual void x() { }
+};
+
+struct Gamma
+{
+};
+
+struct Derived : public Alpha
+{
+};
+
+struct VirtuallyDerived : public virtual Alpha
+{
+};
+
+struct DoublyDerived : public VirtuallyDerived,
+ public virtual Alpha,
+ public Gamma
+{
+};
+
int
main (int argc, char **argv)
{
@@ -18,5 +42,11 @@ main (int argc, char **argv)
A &ar = *b;
B &br = (B&)ar;
+ Derived derived;
+ DoublyDerived doublyderived;
+
+ Alpha *ad = &derived;
+ Alpha *add = &doublyderived;
+
return 0; /* breakpoint spot: casts.exp: 1 */
}
diff --git a/gdb/testsuite/gdb.cp/casts.exp b/gdb/testsuite/gdb.cp/casts.exp
index c87d604..926f81b 100644
--- a/gdb/testsuite/gdb.cp/casts.exp
+++ b/gdb/testsuite/gdb.cp/casts.exp
@@ -98,3 +98,75 @@ gdb_test "print (B &) ar" ".* = .B.* {<A> = {a = 42}, b = 1729}" \
# Check compiler casting
gdb_test "print br" ".* = .B.* {<A> = {a = 42}, b = 1729}" \
"let compiler cast base class reference to derived class reference"
+
+
+# A few basic tests of "new" casts.
+
+gdb_test "print const_cast<const B *> (b)" " = \\(const B \\*\\) $hex" \
+ "basic test of const_cast"
+
+gdb_test "print const_cast<void *> (0)" " = \\(void \\*\\) 0x0" \
+ "const_cast of 0"
+
+gdb_test "print static_cast<A *> (b)" " = \\(A \\*\\) $hex" \
+ "basic test of static_cast"
+
+gdb_test "print static_cast<A &> (*b)" " = \\(A \\&\\) @$hex: {a = 42}" \
+ "static_cast to reference type"
+
+gdb_test "print reinterpret_cast<A *> (b)" " = \\(A \\*\\) $hex" \
+ "basic test of reinterpret_cast"
+
+gdb_test "print reinterpret_cast<void> (b)" "Invalid reinterpret_cast" \
+ "test invalid reinterpret_cast"
+
+gdb_test "print reinterpret_cast<A &> (*b)" " = \\(A \\&\\) @$hex: {a = 42}" \
+ "reinterpret_cast to reference type"
+
+# Tests of dynamic_cast.
+
+set nonzero_hex "0x\[0-9A-Fa-f\]\[0-9A-Fa-f\]+"
+
+gdb_test "print dynamic_cast<void> (a)" \
+ ".*must be a pointer or reference type" \
+ "invalid dynamic_cast"
+
+gdb_test "print dynamic_cast<void *> (0)" \
+ " = \\(void \\*\\) 0x0" \
+ "dynamic_cast of 0 to void*"
+
+gdb_test "print dynamic_cast<Alpha *> (&derived)" \
+ " = \\(Alpha \\*\\) $nonzero_hex" \
+ "dynamic_cast simple upcast"
+
+gdb_test "print dynamic_cast<Alpha *> (&doublyderived)" \
+ " = \\(Alpha \\*\\) $nonzero_hex" \
+ "dynamic_cast upcast to unique base"
+
+gdb_test "print dynamic_cast<Alpha &> (derived)" \
+ " = \\(Alpha \\&\\) @$nonzero_hex: {.* = $nonzero_hex}" \
+ "dynamic_cast simple upcast to reference"
+
+gdb_test "print dynamic_cast<Derived *> (ad)" \
+ " = \\(Derived \\*\\) $nonzero_hex" \
+ "dynamic_cast simple downcast"
+
+gdb_test "print dynamic_cast<VirtuallyDerived *> (add)" \
+ " = \\(VirtuallyDerived \\*\\) $nonzero_hex" \
+ "dynamic_cast simple downcast to intermediate class"
+
+gdb_test "print dynamic_cast<VirtuallyDerived *> (ad)" \
+ " = \\(VirtuallyDerived \\*\\) 0x0" \
+ "dynamic_cast to non-existing base"
+
+gdb_test "print dynamic_cast<VirtuallyDerived &> (*ad)" \
+ "dynamic_cast failed" \
+ "dynamic_cast to reference to non-existing base"
+
+gdb_test "print dynamic_cast<DoublyDerived *> (add)" \
+ " = \\(DoublyDerived \\*\\) $nonzero_hex" \
+ "dynamic_cast unique downcast"
+
+gdb_test "print dynamic_cast<Gamma *> (add)" \
+ " = \\(Gamma \\*\\) $nonzero_hex" \
+ "dynamic_cast to sibling"
diff --git a/gdb/valops.c b/gdb/valops.c
index 1667368..ca34083 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -527,6 +527,258 @@ value_cast (struct type *type, struct value *arg2)
}
}
+/* The C++ reinterpret_cast operator. */
+
+struct value *
+value_reinterpret_cast (struct type *type, struct value *arg)
+{
+ struct value *result;
+ struct type *real_type = check_typedef (type);
+ struct type *arg_type, *dest_type;
+ int is_ref = 0;
+ enum type_code dest_code, arg_code;
+
+ /* Do reference, function, and array conversion. */
+ arg = coerce_array (arg);
+
+ /* Attempt to preserve the type the user asked for. */
+ dest_type = type;
+
+ /* If we are casting to a reference type, transform
+ reinterpret_cast<T&>(V) to *reinterpret_cast<T*>(&V). */
+ if (TYPE_CODE (real_type) == TYPE_CODE_REF)
+ {
+ is_ref = 1;
+ arg = value_addr (arg);
+ dest_type = lookup_pointer_type (TYPE_TARGET_TYPE (dest_type));
+ real_type = lookup_pointer_type (real_type);
+ }
+
+ arg_type = value_type (arg);
+
+ dest_code = TYPE_CODE (real_type);
+ arg_code = TYPE_CODE (arg_type);
+
+ /* We can convert pointer types, or any pointer type to int, or int
+ type to pointer. */
+ if ((dest_code == TYPE_CODE_PTR && arg_code == TYPE_CODE_INT)
+ || (dest_code == TYPE_CODE_INT && arg_code == TYPE_CODE_PTR)
+ || (dest_code == TYPE_CODE_METHODPTR && arg_code == TYPE_CODE_INT)
+ || (dest_code == TYPE_CODE_INT && arg_code == TYPE_CODE_METHODPTR)
+ || (dest_code == TYPE_CODE_MEMBERPTR && arg_code == TYPE_CODE_INT)
+ || (dest_code == TYPE_CODE_INT && arg_code == TYPE_CODE_MEMBERPTR)
+ || (dest_code == arg_code
+ && (dest_code == TYPE_CODE_PTR
+ || dest_code == TYPE_CODE_METHODPTR
+ || dest_code == TYPE_CODE_MEMBERPTR)))
+ result = value_cast (dest_type, arg);
+ else
+ error (_("Invalid reinterpret_cast"));
+
+ if (is_ref)
+ result = value_cast (type, value_ref (value_ind (result)));
+
+ return result;
+}
+
+/* A helper for value_dynamic_cast. This implements the first of two
+ runtime checks: we iterate over all the base classes of the value's
+ class which are equal to the desired class; if only one of these
+ holds the value, then it is the answer. */
+
+static int
+dynamic_cast_check_1 (struct type *desired_type,
+ const bfd_byte *contents,
+ CORE_ADDR address,
+ struct type *search_type,
+ CORE_ADDR arg_addr,
+ struct type *arg_type,
+ struct value **result)
+{
+ int i, result_count = 0;
+
+ for (i = 0; i < TYPE_N_BASECLASSES (search_type) && result_count < 2; ++i)
+ {
+ int offset = baseclass_offset (search_type, i, contents, address);
+ if (offset == -1)
+ error (_("virtual baseclass botch"));
+ if (class_types_same_p (desired_type, TYPE_BASECLASS (search_type, i)))
+ {
+ if (address + offset >= arg_addr
+ && address + offset < arg_addr + TYPE_LENGTH (arg_type))
+ {
+ ++result_count;
+ if (!*result)
+ *result = value_at_lazy (TYPE_BASECLASS (search_type, i),
+ address + offset);
+ }
+ }
+ else
+ result_count += dynamic_cast_check_1 (desired_type,
+ contents + offset,
+ address + offset,
+ TYPE_BASECLASS (search_type, i),
+ arg_addr,
+ arg_type,
+ result);
+ }
+
+ return result_count;
+}
+
+/* A helper for value_dynamic_cast. This implements the second of two
+ runtime checks: we look for a unique public sibling class of the
+ argument's declared class. */
+
+static int
+dynamic_cast_check_2 (struct type *desired_type,
+ const bfd_byte *contents,
+ CORE_ADDR address,
+ struct type *search_type,
+ struct value **result)
+{
+ int i, result_count = 0;
+
+ for (i = 0; i < TYPE_N_BASECLASSES (search_type) && result_count < 2; ++i)
+ {
+ int offset;
+
+ if (! BASETYPE_VIA_PUBLIC (search_type, i))
+ continue;
+
+ offset = baseclass_offset (search_type, i, contents, address);
+ if (offset == -1)
+ error (_("virtual baseclass botch"));
+ if (class_types_same_p (desired_type, TYPE_BASECLASS (search_type, i)))
+ {
+ ++result_count;
+ if (*result == NULL)
+ *result = value_at_lazy (TYPE_BASECLASS (search_type, i),
+ address + offset);
+ }
+ else
+ result_count += dynamic_cast_check_2 (desired_type,
+ contents + offset,
+ address + offset,
+ TYPE_BASECLASS (search_type, i),
+ result);
+ }
+
+ return result_count;
+}
+
+/* The C++ dynamic_cast operator. */
+
+struct value *
+value_dynamic_cast (struct type *type, struct value *arg)
+{
+ int unambiguous = 0, full, top, using_enc;
+ struct type *resolved_type = check_typedef (type);
+ struct type *arg_type = check_typedef (value_type (arg));
+ struct type *class_type, *rtti_type;
+ struct value *result, *tem, *original_arg = arg;
+ CORE_ADDR addr;
+ int is_ref = TYPE_CODE (resolved_type) == TYPE_CODE_REF;
+
+ if (TYPE_CODE (resolved_type) != TYPE_CODE_PTR
+ && TYPE_CODE (resolved_type) != TYPE_CODE_REF)
+ error (_("Argument to dynamic_cast must be a pointer or reference type"));
+ if (TYPE_CODE (TYPE_TARGET_TYPE (resolved_type)) != TYPE_CODE_VOID
+ && TYPE_CODE (TYPE_TARGET_TYPE (resolved_type)) != TYPE_CODE_CLASS)
+ error (_("Argument to dynamic_cast must be pointer to class or `void *'"));
+
+ class_type = check_typedef (TYPE_TARGET_TYPE (resolved_type));
+ if (TYPE_CODE (resolved_type) == TYPE_CODE_PTR)
+ {
+ if (TYPE_CODE (arg_type) != TYPE_CODE_PTR
+ && ! (TYPE_CODE (arg_type) == TYPE_CODE_INT
+ && value_as_long (arg) == 0))
+ error (_("Argument to dynamic_cast does not have pointer type"));
+ if (TYPE_CODE (arg_type) == TYPE_CODE_PTR)
+ {
+ arg_type = check_typedef (TYPE_TARGET_TYPE (arg_type));
+ if (TYPE_CODE (arg_type) != TYPE_CODE_CLASS)
+ error (_("Argument to dynamic_cast does not have pointer to class type"));
+ }
+
+ /* Handle NULL pointers. */
+ if (value_as_long (arg) == 0)
+ return value_zero (type, not_lval);
+
+ arg = value_ind (arg);
+ }
+ else
+ {
+ if (TYPE_CODE (arg_type) != TYPE_CODE_CLASS)
+ error (_("Argument to dynamic_cast does not have class type"));
+ }
+
+ /* If the classes are the same, just return the argument. */
+ if (class_types_same_p (class_type, arg_type))
+ return value_cast (type, arg);
+
+ /* If the target type is a unique base class of the argument's
+ declared type, just cast it. */
+ if (is_ancestor (class_type, arg_type))
+ {
+ if (is_unique_ancestor (class_type, arg))
+ return value_cast (type, original_arg);
+ error (_("Ambiguous dynamic_cast"));
+ }
+
+ rtti_type = value_rtti_type (arg, &full, &top, &using_enc);
+ if (! rtti_type)
+ error (_("Couldn't determine value's most derived type for dynamic_cast"));
+
+ /* Compute the most derived object's address. */
+ addr = value_address (arg);
+ if (full)
+ {
+ /* Done. */
+ }
+ else if (using_enc)
+ addr += top;
+ else
+ addr += top + value_embedded_offset (arg);
+
+ /* dynamic_cast<void *> means to return a pointer to the
+ most-derived object. */
+ if (TYPE_CODE (resolved_type) == TYPE_CODE_PTR
+ && TYPE_CODE (TYPE_TARGET_TYPE (resolved_type)) == TYPE_CODE_VOID)
+ return value_at_lazy (type, addr);
+
+ tem = value_at (type, addr);
+
+ /* The first dynamic check specified in 5.2.7. */
+ if (is_public_ancestor (arg_type, TYPE_TARGET_TYPE (resolved_type)))
+ {
+ if (class_types_same_p (rtti_type, TYPE_TARGET_TYPE (resolved_type)))
+ return tem;
+ result = NULL;
+ if (dynamic_cast_check_1 (TYPE_TARGET_TYPE (resolved_type),
+ value_contents (tem), value_address (tem),
+ rtti_type, addr,
+ arg_type,
+ &result) == 1)
+ return value_cast (type,
+ is_ref ? value_ref (result) : value_addr (result));
+ }
+
+ /* The second dynamic check specified in 5.2.7. */
+ result = NULL;
+ if (is_public_ancestor (arg_type, rtti_type)
+ && dynamic_cast_check_2 (TYPE_TARGET_TYPE (resolved_type),
+ value_contents (tem), value_address (tem),
+ rtti_type, &result) == 1)
+ return value_cast (type,
+ is_ref ? value_ref (result) : value_addr (result));
+
+ if (TYPE_CODE (resolved_type) == TYPE_CODE_PTR)
+ return value_zero (type, not_lval);
+
+ error (_("dynamic_cast failed"));
+}
+
/* Create a value of type TYPE that is zero, and return it. */
struct value *
diff --git a/gdb/value.h b/gdb/value.h
index 8ac62b8..42b4497 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -469,6 +469,11 @@ extern struct value *value_cast_pointers (struct type *, struct value *);
extern struct value *value_cast (struct type *type, struct value *arg2);
+extern struct value *value_reinterpret_cast (struct type *type,
+ struct value *arg);
+
+extern struct value *value_dynamic_cast (struct type *type, struct value *arg);
+
extern struct value *value_zero (struct type *type, enum lval_type lv);
extern struct value *value_one (struct type *type, enum lval_type lv);