diff options
author | Tom Tromey <tromey@redhat.com> | 2010-01-18 20:54:35 +0000 |
---|---|---|
committer | Tom Tromey <tromey@redhat.com> | 2010-01-18 20:54:35 +0000 |
commit | 4e8f195d9dd0be6a5caf9349883db5a531aabd5f (patch) | |
tree | 4135b82af0d720a7a7e9786de58cf7cfc9486885 /gdb | |
parent | d9c57d9ff1bf3533ddb272717afe2b18f61dd017 (diff) | |
download | binutils-4e8f195d9dd0be6a5caf9349883db5a531aabd5f.zip binutils-4e8f195d9dd0be6a5caf9349883db5a531aabd5f.tar.gz binutils-4e8f195d9dd0be6a5caf9349883db5a531aabd5f.tar.bz2 |
gdb
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.
gdb/testsuite
PR c++/9680:
* gdb.cp/casts.cc: Add new classes and variables.
* gdb.cp/casts.exp: Test new operators.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 31 | ||||
-rw-r--r-- | gdb/c-exp.y | 65 | ||||
-rw-r--r-- | gdb/eval.c | 16 | ||||
-rw-r--r-- | gdb/expprint.c | 18 | ||||
-rw-r--r-- | gdb/expression.h | 6 | ||||
-rw-r--r-- | gdb/gdbtypes.c | 109 | ||||
-rw-r--r-- | gdb/gdbtypes.h | 6 | ||||
-rw-r--r-- | gdb/parse.c | 2 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/casts.cc | 30 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/casts.exp | 72 | ||||
-rw-r--r-- | gdb/valops.c | 252 | ||||
-rw-r--r-- | gdb/value.h | 5 |
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]; } @@ -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); |