This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
patch:jet another bug in dynamic_cast
- To: egcs-patches at egcs dot cygnus dot com
- Subject: patch:jet another bug in dynamic_cast
- From: Alfred Minarik <a8601248 at unet dot univie dot ac dot at>
- Date: Sun, 13 Jun 1999 08:00:01 +0200
I have found jet another bug with
dynamic_cast in g++ and fixed it.
The bug can be demonstrated with
the following test case.
-------------snip
struct a{virtual ~a(){}};
struct m:virtual a{};
struct b:m{};
struct r:m{};
struct c:m,r{};
struct d:b,c{};
#include <stdio.h>
d D;
int main()
{
m* mp=dynamic_cast<m*>((a*)&D);
printf("___Result m %p expect %p\n",mp,0);
}
-------------snap
While back tracing from the c branch to d
there is no information transmitted that
there was an ambiguous error from virtual base.
So the b branch does not fail.
I used this additional info transfer for an
additional optimization of my previous patch.
Rationale: If I know that there was an successfull
downcast in one branch, there is no point in
testing the other branches for possible casts.
The code has now become a bit complicate.
If you want me to give more explanation on
the algorithm (or add more comments) tell me.
The patch is again against
the original snapshot 19990608
so it contains and therefore replaces
my previous submission
[patch:dynamic_cast and nonprivate inheritance]
ChangeLog in addition
*tinfo.cc (dcast): fix : report fail on ambiguous casts from
virtual base to 2n+1 derived classes. track downcasts for
optimization.
*tinfo.h (dcast): new parameter final_result in dcast for
error tracking and downcast - crosscast tracking
Alfred
diff -r -c3 egcs-19990608.orig/gcc/cp/tinfo.cc egcs-19990608/gcc/cp/tinfo.cc
*** egcs-19990608.orig/gcc/cp/tinfo.cc Sun Jun 13 06:32:10 1999
--- egcs-19990608/gcc/cp/tinfo.cc Sun Jun 13 07:14:54 1999
***************
*** 62,100 ****
__rtti_user (void *addr, const char *name)
{ new (addr) __user_type_info (name); }
! // dynamic_cast helper methods.
// Returns a pointer to the desired sub-object or 0.
void * __user_type_info::
! dcast (const type_info& to, int, void *addr, const type_info *, void *) const
! { return (*this == to) ? addr : 0; }
void * __si_type_info::
! dcast (const type_info& to, int require_public, void *addr,
! const type_info *sub, void *subptr) const
{
! if (*this == to)
! return addr;
! return base.dcast (to, require_public, addr, sub, subptr);
}
void* __class_type_info::
dcast (const type_info& desired, int is_public, void *objptr,
! const type_info *sub, void *subptr) const
{
if (*this == desired)
! return objptr;
void *match_found = 0;
for (size_t i = 0; i < n_bases; i++)
{
- if (is_public && base_list[i].access != PUBLIC)
- continue;
-
void *p = (char *)objptr + base_list[i].offset;
if (base_list[i].is_virtual)
p = *(void **)p;
! p = base_list[i].base->dcast (desired, is_public, p, sub, subptr);
if (p)
{
if (match_found == 0)
--- 62,208 ----
__rtti_user (void *addr, const char *name)
{ new (addr) __user_type_info (name); }
! // dcast: dynamic_cast helper methods.
! // also called from exeption handler matching without sub
// Returns a pointer to the desired sub-object or 0.
+ // argument is_public 1 respect nonpublic inheritance,
+ // 0 treat all baseclasses as public, -1 for internal
+ // use (respect nonpublic inheritance, but do not enter
+ // nonpublic bases to check for downcasts)
+ // a possible *final_result is set 1 if it is a downcast,
+ // to 0 if crosscast , -1 if error to be propagated.
+
+ #define __save_set_final_result(x) \
+ ((final_result) ? *final_result = (x) : x)
+
+ namespace {
+
+ void *
+ __test_if_downcast(const type_info& desired, int is_public, void *objptr,
+ const type_info *sub, void *subptr, int* final_result = 0)
+ {
+ __save_set_final_result(0);
+ if (!sub)
+ return objptr;
+ if (*sub == desired)
+ {
+ if(subptr == objptr)
+ {
+ __save_set_final_result(1);
+ return objptr;
+ }
+ else return 0;
+ }
+ // Try as downcast (test if there are nonpublic bases on way from sub)
+ // Do not enter special privat handling by using -1 as second argument
+ // (if is_public == 1 or -1)
+ int abs_public = (is_public==-1) ? 1 : is_public;
+ int local_final_result;
+ void *pp = static_cast <const __user_type_info &> (desired).dcast
+ (*sub,-abs_public,objptr,sub,subptr,&local_final_result);
+ if (pp == subptr)
+ {
+ __save_set_final_result(local_final_result);
+ return objptr;
+ }
+ // Distinguish between unreachabel downcast and crosscast
+ pp = static_cast <const __user_type_info &> (desired).dcast
+ (*sub,0,objptr,sub,subptr);
+ return (pp == subptr) ? 0 : objptr;
+ }
+ }
void * __user_type_info::
! dcast (const type_info& desired, int, void *objptr,
! const type_info *sub, void *subptr, int* final_result) const
! {
! __save_set_final_result(0);
! if (*this == desired)
! {
! if (!sub)
! return objptr;
! if (*sub == desired)
! {
! if(subptr == objptr)
! {
! __save_set_final_result(1);
! return objptr;
! }
! else return 0;
! }
! return objptr;
! }
! return 0;
! }
void * __si_type_info::
! dcast (const type_info& desired, int is_public, void *objptr,
! const type_info *sub, void *subptr, int* final_result) const
{
! if (*this == desired)
! return __test_if_downcast(desired, is_public, objptr,
! sub, subptr, final_result);
! return base.dcast (desired, is_public, objptr, sub, subptr, final_result);
}
void* __class_type_info::
dcast (const type_info& desired, int is_public, void *objptr,
! const type_info *sub, void *subptr, int* final_result) const
{
if (*this == desired)
! return __test_if_downcast(desired, is_public, objptr,
! sub, subptr, final_result);
+ __save_set_final_result(0);
void *match_found = 0;
for (size_t i = 0; i < n_bases; i++)
{
void *p = (char *)objptr + base_list[i].offset;
if (base_list[i].is_virtual)
p = *(void **)p;
!
! //There can by a valid downcast within a nonpublic baseclass
! //Skippd at internal public downcast tests indicated with is_public== -1
! if ((is_public==-1) && base_list[i].access != PUBLIC)
! continue;
! if (is_public && base_list[i].access != PUBLIC)
! {
! if (sub)
! {
! void *tmp = base_list[i].base->dcast (desired, 0, p, sub, subptr);
! if(tmp)
! {
! //was it a valid downcast ?
! int local_final_result;
! void *pp = static_cast <const __user_type_info &> (desired).dcast
! (*sub, -1, tmp, sub, subptr, &local_final_result);
! if(pp)
! {
! __save_set_final_result(local_final_result);
! return tmp;
! }
! else
! continue;
! }
! else
! continue;
! }
! else
! continue;
! }
! int local_final_result;
! p = base_list[i].base->dcast (desired, is_public, p, sub,
! subptr, &local_final_result);
! if (local_final_result == -1) //Error ambiguous from virtual base
! {
! __save_set_final_result(local_final_result);
! return 0;
! }
! if (p && local_final_result && !(base_list[i].is_virtual))
! {
! __save_set_final_result(local_final_result);
! return p;
! }
if (p)
{
if (match_found == 0)
***************
*** 108,119 ****
const __user_type_info &d =
static_cast <const __user_type_info &> (desired);
!
! void *os = d.dcast (*sub, 1, match_found);
! void *ns = d.dcast (*sub, 1, p);
if (os == ns)
! /* ambiguous -- subptr is a virtual base */;
else if (os == subptr)
continue;
else if (ns == subptr)
--- 216,231 ----
const __user_type_info &d =
static_cast <const __user_type_info &> (desired);
!
! int abs_public = (is_public==-1) ? 1 : is_public;
! void *os = d.dcast (*sub, -abs_public, match_found,
! sub, subptr);
! void *ns = d.dcast (*sub, -abs_public, p, sub, subptr);
if (os == ns)
! /* ambiguous -- subptr is a virtual base */
! __save_set_final_result(-1);
!
else if (os == subptr)
continue;
else if (ns == subptr)
diff -r -c3 egcs-19990608.orig/gcc/cp/tinfo.h egcs-19990608/gcc/cp/tinfo.h
*** egcs-19990608.orig/gcc/cp/tinfo.h Fri Apr 2 17:35:57 1999
--- egcs-19990608/gcc/cp/tinfo.h Sat Jun 12 21:40:42 1999
***************
*** 13,19 ****
// If our type can be converted to the desired type,
// return the pointer, adjusted accordingly; else return 0.
virtual void* dcast (const type_info &, int, void *,
! const type_info * = 0, void * = 0) const;
};
// type_info for a class with one public, nonvirtual base class.
--- 13,19 ----
// If our type can be converted to the desired type,
// return the pointer, adjusted accordingly; else return 0.
virtual void* dcast (const type_info &, int, void *,
! const type_info * = 0, void * = 0, int * = 0) const;
};
// type_info for a class with one public, nonvirtual base class.
***************
*** 26,32 ****
: __user_type_info (n), base (b) { }
virtual void *dcast (const type_info &, int, void *,
! const type_info * = 0, void * = 0) const;
};
// type_info for a general class.
--- 26,32 ----
: __user_type_info (n), base (b) { }
virtual void *dcast (const type_info &, int, void *,
! const type_info * = 0, void * = 0, int * = 0) const;
};
// type_info for a general class.
***************
*** 51,55 ****
// This is a little complex.
virtual void* dcast (const type_info &, int, void *,
! const type_info * = 0, void * = 0) const;
};
--- 51,55 ----
// This is a little complex.
virtual void* dcast (const type_info &, int, void *,
! const type_info * = 0, void * = 0, int * = 0) const;
};