This is the mail archive of the archer@sourceware.org mailing list for the Archer project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[expr-cumulative] RFA: implement new cast operators


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  <tromey@redhat.com>

	* 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) <UNOP_DYNAMIC_CAST,
	UNOP_REINTERPRET_CAST>: 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) <UNOP_DYNAMIC_CAST,
	UNOP_REINTERPRET_CAST>: New constants.
	* expprint.c (print_subexp_standard) <UNOP_DYNAMIC_CAST,
	UNOP_REINTERPRET_CAST>: New cases.
	(op_name_standard): Likewise.
	(dump_subexp_body_standard): Likewise.
	* eval.c (evaluate_subexp_standard) <UNOP_DYNAMIC_CAST,
	UNOP_REINTERPRET_CAST>: 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  <tromey@redhat.com>

	* 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 <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.  */
@@ -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> = {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 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<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))
+	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<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 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);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]