From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 6217 invoked by alias); 9 Oct 2009 17:16:37 -0000 Mailing-List: contact archer-help@sourceware.org; run by ezmlm Sender: Precedence: bulk List-Post: List-Help: List-Subscribe: List-Id: Received: (qmail 5985 invoked by uid 22791); 9 Oct 2009 17:16:23 -0000 X-SWARE-Spam-Status: No, hits=-2.4 required=5.0 tests=AWL,BAYES_00,SPF_HELO_PASS,SPF_PASS X-Spam-Check-By: sourceware.org From: Tom Tromey To: Project Archer Subject: [expr-cumulative] RFA: implement new cast operators Reply-To: Tom Tromey Date: Fri, 09 Oct 2009 17:16:00 -0000 Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-SW-Source: 2009-q4/txt/msg00005.txt.bz2 This patch implements the "new" C++ cast operators dynamic_cast, reinterpret_cast, static_cast, and const_cast. This doesn't check for all possible errors. It didn't seem worthwhile to do that, so I basically only did it where it affects correctness. I'd like to push this to expr-cumulative. Keith, could you review it? Thanks. Tom 2009-10-09 Tom Tromey * value.h (value_reinterpret_cast, value_dynamic_cast): Declare. * valops.c (value_reinterpret_cast): New function. (dynamic_cast_check_1): Likewise. (dynamic_cast_check_2): Likewise. (value_dynamic_cast): Likewise. * parse.c (operator_length_standard) : New cases. * gdbtypes.h (struct vbase): Remove. (class_types_same_p, is_public_ancestor, is_unique_ancestor): Declare. * gdbtypes.c (class_types_same_p): New function. (is_public_ancestor): Likewise. (is_unique_ancestor_worker): Likewise. (is_unique_ancestor): Likewise. (is_ancestor): Use class_types_same_p. * expression.h (enum exp_opcode) : New constants. * expprint.c (print_subexp_standard) : New cases. (op_name_standard): Likewise. (dump_subexp_body_standard): Likewise. * eval.c (evaluate_subexp_standard) : New cases. * c-exp.y (REINTERPRET_CAST, DYNAMIC_CAST, STATIC_CAST) (CONST_CAST): New tokens. (exp): New productions for cast operators. (ident_tokens): Add new casts. (is_cast_operator): New function. (yylex): Handle cast operators. 2009-10-09 Tom Tromey * gdb.cp/casts.exp: Add tests of new operators. * gdb.cp/casts.cc (Alpha, Gamma, Derived, VirtuallyDerived) (DoublyDerived): New classes. (main): Declare new variables. diff --git a/gdb/c-exp.y b/gdb/c-exp.y index 5b47e2e..5796de2 100644 --- a/gdb/c-exp.y +++ b/gdb/c-exp.y @@ -208,6 +208,7 @@ static struct stoken operator_stoken (const char *); %token ERROR %token NEW DELETE %type operator +%token REINTERPRET_CAST DYNAMIC_CAST STATIC_CAST CONST_CAST /* Special type cases, put in to allow the parser to distinguish different legal basetypes. */ @@ -611,6 +612,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 { @@ -1909,7 +1936,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 @@ -1987,6 +2019,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; @@ -2280,16 +2322,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 5a0d317..82b3cb6 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -2433,6 +2433,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 e83d101..18596b6 100644 --- a/gdb/expprint.c +++ b/gdb/expprint.c @@ -409,6 +409,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) @@ -729,6 +741,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: @@ -1035,6 +1051,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 4ec9a35..471cb33 100644 --- a/gdb/expression.h +++ b/gdb/expression.h @@ -236,6 +236,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 299d0c5..0b28260 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -1789,6 +1789,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 @@ -1803,18 +1815,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 be6ed55..a3630c5 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -777,13 +777,6 @@ struct cplus_struct_type *localtype_ptr; }; -/* Struct used in computing virtual base list */ -struct vbase - { - struct type *vbasetype; /* pointer to virtual base */ - struct vbase *next; /* next in chain */ - }; - /* Struct used for ranking a function for overload resolution */ struct badness_vector { @@ -1260,8 +1253,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 6ca5842..57572f8 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -858,6 +858,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/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 0e4b023..09170ff 100644 --- a/gdb/testsuite/gdb.cp/casts.exp +++ b/gdb/testsuite/gdb.cp/casts.exp @@ -97,3 +97,75 @@ gdb_test "print (B &) ar" ".* = .B.* { = {a = 42}, b = 1729}" \ # Check compiler casting gdb_test "print br" ".* = .B.* { = {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 (b)" " = \\(const B \\*\\) $hex" \ + "basic test of const_cast" + +gdb_test "print const_cast (0)" " = \\(void \\*\\) 0x0" \ + "const_cast of 0" + +gdb_test "print static_cast (b)" " = \\(A \\*\\) $hex" \ + "basic test of static_cast" + +gdb_test "print static_cast (*b)" " = \\(A \\&\\) @$hex: {a = 42}" \ + "static_cast to reference type" + +gdb_test "print reinterpret_cast (b)" " = \\(A \\*\\) $hex" \ + "basic test of reinterpret_cast" + +gdb_test "print reinterpret_cast (b)" "Invalid reinterpret_cast" \ + "test invalid reinterpret_cast" + +gdb_test "print reinterpret_cast (*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 (a)" \ + ".*must be a pointer or reference type" \ + "invalid dynamic_cast" + +gdb_test "print dynamic_cast (0)" \ + " = \\(void \\*\\) 0x0" \ + "dynamic_cast of 0 to void*" + +gdb_test "print dynamic_cast (&derived)" \ + " = \\(Alpha \\*\\) $nonzero_hex" \ + "dynamic_cast simple upcast" + +gdb_test "print dynamic_cast (&doublyderived)" \ + " = \\(Alpha \\*\\) $nonzero_hex" \ + "dynamic_cast upcast to unique base" + +gdb_test "print dynamic_cast (derived)" \ + " = \\(Alpha \\&\\) @$nonzero_hex: {.* = $nonzero_hex}" \ + "dynamic_cast simple upcast to reference" + +gdb_test "print dynamic_cast (ad)" \ + " = \\(Derived \\*\\) $nonzero_hex" \ + "dynamic_cast simple downcast" + +gdb_test "print dynamic_cast (add)" \ + " = \\(VirtuallyDerived \\*\\) $nonzero_hex" \ + "dynamic_cast simple downcast to intermediate class" + +gdb_test "print dynamic_cast (ad)" \ + " = \\(VirtuallyDerived \\*\\) 0x0" \ + "dynamic_cast to non-existing base" + +gdb_test "print dynamic_cast (*ad)" \ + "dynamic_cast failed" \ + "dynamic_cast to reference to non-existing base" + +gdb_test "print dynamic_cast (add)" \ + " = \\(DoublyDerived \\*\\) $nonzero_hex" \ + "dynamic_cast unique downcast" + +gdb_test "print dynamic_cast (add)" \ + " = \\(Gamma \\*\\) $nonzero_hex" \ + "dynamic_cast to sibling" diff --git a/gdb/valops.c b/gdb/valops.c index 5cfd3a4..8cd9474 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -522,6 +522,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(V) to *reinterpret_cast(&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)) + contents; + + 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 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 993f05b..6f58c16 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);