This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


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

Two bugs in dynamic_cast


Egcs appears to be two bugs in dynamic_cast.  For reference, I will
quote from the C++ standard, section 5.2.7:
  
  1 The result of the expression dynamic_cast<T>(v) is the result of
    converting the expression v to type T. T shall be a pointer or
    reference to a complete class type, or ``pointer to cv void''. Types
    shall not be defined in a dynamic_cast. The dynamic_cast operator
    shall not cast away constness (5.2.11).

[Paragraphs 2-5 deal with casting to the same type or a subtype.]

  6 Otherwise, v shall be a pointer to or an lvalue of a polymorphic
    type (10.3).
  
  7 If T is ``pointer to cv void,'' then the result is a pointer to the
    most derived object pointed to by v.  Otherwise, a run-time check is
    applied to see if the object pointed or referred to by v can be
    converted to the type pointed or referred to by T.
  
  8 The run-time check logically executes as follows: 
  
      If, in the most derived object pointed (referred) to by v, v
      points (refers) to a public base class sub object of a T object,
      and if only one object of type T is derived from the sub-object
      pointed (referred) to by v, the result is a pointer (an lvalue
      referring) to that T object.
  
      Otherwise, if v points (refers) to a public base class sub-object
      of the most derived object, and the type of the most derived
      object has an unambiguous public base class of type T, the result
      is a pointer (an lvalue referring) to the T sub-object of the most
      derived object.
  
      Otherwise, the run-time check fails.

Now here is code that I believe demonstrates two bugs in egcs:

/* -- start here -- */
#include <stdio.h>

struct a {
  virtual ~a () {}
};

struct b : public a {
};

struct c : private b {
  a *geta () { return this; }
  static b *a2b (a *objp) { return dynamic_cast<b *> (objp); }
  static c *a2c (a *objp) { return dynamic_cast<c *> (objp); }
};

int
main ()
{
  c obj;
  a *objp = obj.geta ();
  if (!c::a2b (objp))
    fprintf (stderr, "Could not cast from a to b\n");
  if (c::a2c (objp))
    fprintf (stderr, "Cast from a to c\n");
  return 0;
}
/* -- end here -- */

This code produces the following output:

 Could not cast from a to b
 Cast from a to c

Both these casts are incorrect according to the standard.  First let's
look at the case of casting from a to b.  We have:

struct a { ... }
struct b : public a { ... }
struct c : private b { ... }
dynamic_cast<b *> ((a *) ptr_to_c);

And the dynamic cast is failing.  Now b is neither a nor a subtype of
b, and b * is not NULL, so clearly the run-time checks to apply.
However, the first condition should succeed:

      If, in the most derived object pointed (referred) to by v, v
      points (refers) to a public base class sub object of a T object,

Here v is of type "a *", and T is of type "b *".  The most derived
object of v is of type c.  The phrase "sub object of a T object" is
admittedly not entirely clear, however I think the only reasonable
interpretation is that "T object" means the type b, as opposed to the
type "b *".

[The reason they would use this term "T object" is that they are
talking about both pointers and references here, and "T object" is a
way of saying "whatever the pointer points to or the reference
references".  If you disagree with me on this point, please keep
reading anyway, because the second part of this bug report describes
another bug in dynamic_cast unrelated to this clause.  Also note that
if "T object" meant type "b *" instead of b, then this clause would
basically be meaningless.]

Assuming my reading so far, then, v (i.e. objp) does point to a public
base class of b in c, namely a.  Thus, the first condition of of this
sentence has been met.

      and if only one object of type T is derived from the sub-object
      pointed (referred) to by v, 

There is indeed only one b derived from a in the type c.  Thus, the
second requirement in this sentence also holds.

				  the result is a pointer (an lvalue
      referring) to that T object.

Thus, the dynamic cast should succeed.  Yet in the above code, the
dynamic cast failed.

Now let's turn to the second dynamic cast.  Here we basically have:

struct a { ... }
struct b : public a { ... }
struct c : private b { ... }
dynamic_cast<c *> ((a *) ptr_to_c);

Turning once again to the run-time checks (which are mandated here):

      If, in the most derived object pointed (referred) to by v, v
      points (refers) to a public base class sub object of a T object,
      ...

Here v is a *, and "T object" is c.  However, a is NOT a public base
class of c.  Granted, in egcs this dynamic_cast only succeeds within
methods or friends of c.  However, the standard makes no such
distinction.  Class a may be a base class of c accessible from the
point at which the dynamic_cast was made, but it is not a public base
class.  Unless the word public is sloppily being used interchangibly
with the word accessible, a is not a public base class of c and this
condition does not hold.

The second option for a dynamic cast to succeed is mostly designed for
casting to sibling classes with multiple inheritance, so it shouldn't
apply here.  The same problem applies to it:

      Otherwise, if v points (refers) to a public base class sub-object
      of the most derived object, and ...

The most derived object is of type c, and a is not a public base class
of c.  Thus, this condition fails.  That leaves one option:

      Otherwise, the run-time check fails.

But in egcs the dynamic_cast does not fail.

Thanks,
David


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