aboutsummaryrefslogtreecommitdiff
path: root/gdb/valops.c
diff options
context:
space:
mode:
authorTom Tromey <tromey@redhat.com>2010-01-18 20:54:35 +0000
committerTom Tromey <tromey@redhat.com>2010-01-18 20:54:35 +0000
commit4e8f195d9dd0be6a5caf9349883db5a531aabd5f (patch)
tree4135b82af0d720a7a7e9786de58cf7cfc9486885 /gdb/valops.c
parentd9c57d9ff1bf3533ddb272717afe2b18f61dd017 (diff)
downloadgdb-4e8f195d9dd0be6a5caf9349883db5a531aabd5f.zip
gdb-4e8f195d9dd0be6a5caf9349883db5a531aabd5f.tar.gz
gdb-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/valops.c')
-rw-r--r--gdb/valops.c252
1 files changed, 252 insertions, 0 deletions
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 *