This is the mail archive of the gcc-patches@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]

Re: C++ PATCH: throw (T *)NULL and dynamic_cast


Nathan Sidwell wrote:
[A patch which didn't quite work]
Alfred has pointed out that the version 3 patch failed his tests. It turned out
that even though I'd put them in my testsuite, they were not being executed.
Even though they lack a `Build don't link:' line, they produced (marked)
warnings on compilation (about inaccessible bases), and this caused the
testsuite to not bother to execute them and claim they passed, weird. I've
altered the tests so as to not produce compilation warnings (by introducing
intermediate bases).

Here is an updated patch which does pass all the tests (honest :-). I'd had a
thinko about what a single public inheritance class's base class could look
like. Otherwise the logic is for the same as the previous version.

I attach the patch and all the tests I've been using against it.

nathan

-- 
Dr Nathan Sidwell :: Computer Science Department :: Bristol University
        I have seen the death of PhotoShop -- it is called GIMP
nathan@acm.org  http://www.cs.bris.ac.uk/~nathan/  nathan@cs.bris.ac.uk
// test of dynamic_cast
// runtime detecting of nonpublic
// inheritance within a cast
// and therefor failing with result 0.

extern "C" void abort();
extern "C" void printf(const char*, ...);

static int errors = 0;
void error(int i)
{
  printf("Error %i\n",i);
  errors++;
}

// 1. downcast

// 1.1. single inheritance case

struct A {virtual ~A(){}};
struct AA : A {};
struct B : A {};
struct BB : B {};
class C : B {};
struct D : C {};

struct CC : B {};
class DD : CC {};

class CCC : protected B {};
class DDD : protected CCC {};

void 
test01 ()
{
  D d;
  if(dynamic_cast<D*> ((A*)&d)) error(1);
  if(dynamic_cast<D*> ((B*)&d)) error(2);
  if(&d != dynamic_cast<D*> ((C*)&d)) error(3); //counter example
  if(dynamic_cast<C*> ((B*)&d)) error(4);
	
  DD dd;
  if(dynamic_cast<DD*> ((A*)&dd)) error(5);
  if(dynamic_cast<DD*> ((B*)&dd)) error(6);

  DDD ddd;
  if(dynamic_cast<DDD*> ((A*)&ddd)) error(7);
  if(dynamic_cast<DDD*> ((B*)&ddd)) error(8);
  if(dynamic_cast<CCC*> ((B*)&ddd)) error(9);
}		

// 1.2. multiple inheritance case
// 1.2.1. all bases are public
 
struct E : D, CC {};
struct EE : CC, D {}; //Will search in reverse order.

void 
test02 ()
{
  E e;
  if(dynamic_cast<E*> ((A*)(D*)&e)) error(10);
  if(dynamic_cast<E*> ((B*)(D*)&e)) error(11);
  if(&e != dynamic_cast<E*> ((C*)(D*)&e)) error(12); //counter example
  if(&e != dynamic_cast<E*> ((B*)(CC*)&e)) error(13); //counter example
  if((CC*)&e != dynamic_cast<CC*> ((B*)(CC*)&e)) error(14); //counter example
  
  EE ee;
  if(dynamic_cast<EE*> ((A*)(D*)&ee)) error(15);
  if(dynamic_cast<EE*> ((B*)(D*)&ee)) error(16);
  if(&ee != dynamic_cast<EE*> ((C*)(D*)&ee)) error(17); //counter example
  if(&ee != dynamic_cast<EE*> ((B*)(CC*)&ee)) error(18); //counter example
  if((CC*)&ee != dynamic_cast<CC*> ((B*)(CC*)&ee)) error(19); //counter example
}		

// 1.2.2 one or more branches are nonpublic

struct X : private BB, E {};
struct Y : AA, private B {};

class XX : BB, E {};

void 
test03 ()
{
  X x;
  if(&x != dynamic_cast<X*>((B*)(CC*)(E*)&x)) error(20); //counter example
  XX xx;
  if(dynamic_cast<XX*>((B*)(CC*)(E*)&xx)) error(21);	
  Y y;
  if(dynamic_cast<Y*>((B*)&y)) error (22);
  if(dynamic_cast<Y*>((A*)(B*)&y)) error (23);
}

// 2. crosscast

struct J {virtual ~J(){};};
struct K : CC, private J {}; 
class KK : J, CC{};
		
void 
test04 ()
{
  E e;
  if(dynamic_cast<CC*> ((B*)(D*)&e)) error(24);
  if((CC*)&e != dynamic_cast<CC*> ((C*)(D*)&e)) error(25); //counter example
  K k;
  if(dynamic_cast<J*> ((B*)&k)) error(26);
  KK kk;
  if(dynamic_cast<J*> ((CC*)&kk)) error(27);
}

int 
main ()
{
  test01();
  test02();
  test03();
  test04();
  return errors ? 1 : 0;
}
// test of dynamic_cast
// runtime detecting of valid 
// downcasts within nonpublic 
// baseclasses.

extern "C" void abort();
extern "C" void printf(const char*, ...);

static int errors = 0;

void error(int i)
{
  printf("Error %i\n",i);
  errors++;
}

// 1. downcast
// 1.1 single inheritance case

struct A {virtual ~A(){};int i;};
struct B : A {int i;};
struct C : B {int i;};
struct CC : C {};
class D : C {int i;};

struct E : D {int i;};
class F : E {int i;};

void 
test01 ()
{
  D d;
  if((C*)&d != dynamic_cast<C*> ((A*)&d)) error(1);
  if((C*)&d != dynamic_cast<C*> ((B*)&d)) error(2);
  if((B*)&d != dynamic_cast<B*> ((A*)&d)) error(3);

  E e;
  if((C*)&e != dynamic_cast<C*> ((A*)&e)) error(4);

  F f;
  if((C*)&f != dynamic_cast<C*> ((B*)&f)) error(5);
  if((B*)&f != dynamic_cast<B*> ((A*)&f)) error(6);
  if((E*)&f != dynamic_cast<E*> ((D*)&f)) error(7);
  if(dynamic_cast<E*> ((C*)&f)) error(8); //counter example
}		

// 1.2 multiple inheritance case

struct G : CC, F{};
		
void 
test02 ()
{
  G g;
  if((B*)(F*)&g != dynamic_cast<B*> ((A*)(F*)&g)) error(9);
  if(dynamic_cast<D*> ((A*)(F*)&g)) error(10);
  if(dynamic_cast<G*> ((B*)(F*)&g)) error(11);
}

// 2. crosscast (always fail)

struct I : C{};
struct J : F{};
struct K : I, J{};
class L : K{};
		
void 
test03 ()
{
  L l;
  if(dynamic_cast<J*> ((I*)&l)) error(12);
  if(dynamic_cast<J*> ((E*)&l)) error(13);
  if(dynamic_cast<I*> ((J*)&l)) error(14);
}

int 
main ()
{
  test01();
  test02();
  test03();
  return errors ? 1 : 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// We cannot catch an ambiguous base class. Check with a non-virtual public
// DAG.
// -- public, << private, == virtual

// D--B--A
// +--C--A

struct A { int m; };
struct B : A { int m; };
struct C : A { int m; };
struct D : B, C { int m; };

void fn(D *obj) { throw obj; }

extern "C" void abort();

int main()
{
  D d;
  int caught;
  
  caught = 0;
  try { fn(&d); }
  catch(A *ap) { abort(); } // A is ambiguous
  catch(B *ap) { caught = 1; if (ap != &d) abort();}
  catch(...) { abort(); }
  if (!caught) abort();
  
  caught = 0;
  try { fn(0); }
  catch(A *ap) { abort(); } // A is ambiguous
  catch(B *ap) { caught = 1; if (ap) abort();}
  catch(...) { abort(); }
  if (!caught) abort();
  
  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// We cannot catch an ambiguous base class. Check with a virtual public
// DAG.
// -- public, << private, == virtual

// D--B==A
// +--C==A

struct A { int m; };
struct B : virtual A { int m; };
struct C : virtual A { int m; };
struct D : B, C { int m; };

void fn(D *obj) { throw obj; }

extern "C" void abort();

int main()
{
  D d;
  int caught;
  
  caught = 0;
  try { fn(&d); }
  catch(A *ap) { caught = 1; if (ap != &d) abort(); } // A is not ambiguous
  catch(B *ap) { abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  caught = 0;
  try { fn(0); }
  catch(A *ap) { caught = 1; if (ap) abort(); } // A is not ambiguous
  catch(B *ap) { abort(); }
  catch(...) { abort(); }
  if (!caught) abort();

  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// We cannot catch an ambiguous base class.
// -- public, << private, == virtual

// D--B==A
// +--C==A
// +--AA-A

struct A { int m; };
struct B : virtual A { int m; };
struct C : virtual A { int m; };
struct AA : A { int m; };
struct D : B, C, AA { int m; };

void fn(D *obj) { throw obj; }

extern "C" void abort();

int main()
{
  D d;
  int caught;
  
  caught = 0;
  try { fn(&d); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap != &d) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  caught = 0;
  try { fn(0); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// We cannot catch an ambiguous base class.
// -- public, << private, == virtual

// E==D--B==A
//    +--C==A
//    +--AA-A

struct A { int m; };
struct B : virtual A { int m; };
struct C : virtual A { int m; };
struct AA : A { int m; };
struct D : B, C, AA { int m; };
struct E : virtual D { int m; };

void fn(E *obj) { throw obj; }

extern "C" void abort();

int main()
{
  E e;
  int caught;
  
  caught = 0;
  try { fn(&e); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap != &e) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  caught = 0;
  try { fn(0); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap) abort();}
  catch(...) { abort(); }
  if (!caught) abort();
  
  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// We cannot catch an ambiguous base class.
// -- public, << private, == virtual

// F--D--B--A
//    +--C--A
// +--E--A

struct A { int m; };
struct B : A { int m; };
struct C : A { int m; };
struct D : B, C { int m; };
struct E : A { int m; };
struct F : D, E { int m; };

void fn(F *obj) { throw obj; }

extern "C" void abort();

int main()
{
  F d;
  int caught;
  
  caught = 0;
  try { fn(&d); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap != &d) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  caught = 0;
  try { fn(0); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// We cannot catch an ambiguous base class.
// -- public, << private, == virtual

// D--B--A
// +--C<<A

struct A { int m; };
struct B : A { int m; };
struct C : private A { int m; };
struct D : B, C { int m; };

void fn(D *obj) { throw obj; }

extern "C" void abort();

int main()
{
  D d;
  int caught;
  
  caught = 0;
  try { fn(&d); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap != &d) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  caught = 0;
  try { fn(0); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// We cannot catch an ambiguous base class.
// -- public, << private, == virtual

// D==B--A
// +==C--A

struct A { int m; };
struct B : A { int m; };
struct C : A { int m; };
struct D : virtual B, virtual C { int m; };

void fn(D *obj) { throw obj; }

extern "C" void abort();

int main()
{
  D d;
  int caught;
  
  caught = 0;
  try { fn(&d); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap != &d) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  caught = 0;
  try { fn(0); }
  catch(A *ap) { abort(); }
  catch(B *ap) { caught = 1; if (ap) abort(); }
  catch(...) { abort(); }
  if (!caught) abort();
  
  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// dynamic cast can only cast to public unambiguous bases

struct A {virtual ~A(){} int m; };
struct B {virtual ~B(){} int m; };

struct C1 : A {int m;};
struct C2 : A {int m;};

// A is ambiguous, but private in one branch
struct D1 : B, C1, private C2 {int m;};
// A is ambiguous, and public in both branches
struct D2 : B, C1, C2 {int m;};

void fn(B *bd1, B *bd2)
{
  A *ad1;
  A *ad2;
  
  ad1 = dynamic_cast<A *>(bd1);
  if(ad1) abort();
  ad2 = dynamic_cast<A *>(bd2);
  if(ad2) abort();
}

int main()
{
  D1 d1;
  D2 d2;
  
  fn((B *)&d1, (B *)&d2);
  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 15 Jun 1999 <nathan@acm.org>

// We cannot dynamic_cast via a private link
// -- public, << private, == virtual

// J<<D--A // dcast A -> D is ok, but not A -> J

struct A{int i;virtual ~A(){}};
struct D:A {int i;};
class J:D{int i;};

// E1--B1<<A  // dcast A -> C is not ok
//  +--C
// E2--B1--A  // Dcast A -> C is ok
//  +--C
struct B1 : private A {};
struct B2 : A {};
struct C {};
struct E1 : B1, C {};
struct E2 : B2, C {};

extern "C" void abort();

int main()
{
  J j;
  D *dp = (D *)&j;
  A *ap = dp;
  void *p;
  
  p = dynamic_cast<D *>(ap);
  if (p != dp) abort();
  
  p = dynamic_cast<J *>(dp);
  if (p) abort();
  
  E1 e1;
  ap = (A *)&e1;
  p = dynamic_cast<C *>(ap);
  if (p) abort();
  
  E2 e2;
  ap = (A *)&e2;
  p = dynamic_cast<C *>(ap);
  if (p != (C *)&e2) abort();
  
  return 0;
}
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 6 Jun 1999 <nathan@acm.org>

// dynamic cast should not be fooled by an initially ambiguous base

struct A {int m; virtual ~A(){}};
struct AA : A {};
struct B1 : AA {};
struct B2 : AA {};
struct B3 : AA {};

struct C : B1, B2 {};

struct E1 : C, B3 {};
struct E2 : B3, C {};

extern "C" void abort();

int main()
{
  E1 e1;
  E2 e2;
  A *ap;
  void *p;
  
  ap = (B3 *)&e1;
  p = dynamic_cast<AA *>(ap);
  if (p != (AA *)(B3 *)&e1) abort();
  
  ap = (B3 *)&e2;
  p = dynamic_cast<AA *>(ap);
  if (p != (AA *)(B3 *)&e2) abort();
  
  return 0;
}
1999-06-17  Nathan Sidwell  <nathan@acm.org>

	* tinfo.h (valid_base, ambig_base): New enumeration flags.
	(dcast): Add OK parameter.
	(sub_kind): New enumeration.
	(do_dcast_result): New struct.
	(do_dcast): Move functionality from dcast.  Adjust parameters.
	(find_base): New virtual function.
	* tinfo.cc (dcast): Dispatch to do_dcast.  Check access path from
	source to target.
	(do_dcast): Move old dcast functionality here and adjust.
	Reimplement arbitrary class searching to deal with NULL
	conversions and correctly detect ambiguity. Use find_base for
	sub objects.
	(find_base): New function.
	* tinfo2.cc (__throw_type_match_rtti): Deal with accepting a
	NULL pointer.
	* exception.cc (__cplus_type_matcher): Likewise.

Index: egcs/gcc/cp/tinfo.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/tinfo.h,v
retrieving revision 1.5
diff -c -3 -p -r1.5 tinfo.h
*** tinfo.h	1999/04/02 15:35:57	1.5
--- tinfo.h	1999/06/17 17:29:42
***************
*** 8,19 ****
  // type_info for a class with no base classes (or an enum).
  
  struct __user_type_info : public std::type_info {
    __user_type_info (const char *n) : type_info (n) {}
  
!   // 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.
--- 8,69 ----
  // type_info for a class with no base classes (or an enum).
  
  struct __user_type_info : public std::type_info {
+   public:
    __user_type_info (const char *n) : type_info (n) {}
  
!   protected:
!   // Indicate type of base found -- see do_dcast
!   enum
!   {
!     valid_base = 1,
!     ambig_base = 2
!   };
!   // sub_kind tells us about whether the sub object we started from is
!   // contained within the target object we end up at. It is filled lazily.
!   enum sub_kind
!   {
!     unknown = -1,         // we have no idea
!     not_contained,        // not contained within us
!     contained_ambig,      // contained ambiguously
!     contained_mask = 4,     // contained within us
!     contained_virtual_mask = 1, // via a virtual path
!     contained_public_mask = 2,  // via a public path
!     contained_private = contained_mask,
!     contained_public = contained_mask | contained_public_mask
!   };
!   // do_dcast returns several pieces of information, bundle it together. These
!   // should be initiailized before calling
!   struct do_dcast_result
!   {
!     void *target_obj;   // pointer to target object or NULL (init NULL)
!     bool is_public;     // target is public from whence we started (init true)
!     const type_info *base_type; // where we found the target (init NULL)
!                                 //   an ambiguity is found (ambig_base)
!                                 //   found in a virtual base (__user_type_info
!                                 // of vbase)
!                                 //   found as non-virtual base (valid_base)
!                                 //   not found (NULL)
!     sub_kind sub_obj_kind;    // what we know about any subobject (init unknown)
!   };
!   
!   public:
!   // If our type can be converted to the desired type, return the pointer,
!   // adjusted accordingly, otherwise return NULL. Set OK, if non-NULL, to
!   // indicate that we found a valid convertion.  OBJPTR can be NULL, in which
!   // case NULL will be returned and OK must be used to detect that this is
!   // valid.
!   void* dcast (const type_info &target, int need_public, void *objptr,
! 	       const type_info *sub = 0, void *subptr = 0, bool *ok = 0) const;
!   
!   public:
!   // Worker function for dcast.
!   virtual void do_dcast (const type_info &target, void *objptr,
! 		         const type_info *sub, void *subptr,
! 	                 do_dcast_result *result) const;
!   // Locate a specific base class instance
!   virtual sub_kind find_base (const type_info &base, void *objptr,
!                               void *baseptr) const;
!   	                 
  };
  
  // type_info for a class with one public, nonvirtual base class.
*************** public:
*** 25,32 ****
    __si_type_info (const char *n, const __user_type_info &b)
      : __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.
--- 75,85 ----
    __si_type_info (const char *n, const __user_type_info &b)
      : __user_type_info (n), base (b) { }
  
!   virtual void do_dcast (const type_info &target, void *objptr,
! 		         const type_info *sub, void *subptr,
! 	                 do_dcast_result *result) const;
!   virtual sub_kind find_base (const type_info &base, void *objptr,
!                               void *baseptr) const;
  };
  
  // type_info for a general class.
*************** struct __class_type_info : public __user
*** 49,55 ****
    __class_type_info (const char *name, const base_info *bl, size_t bn)
      : __user_type_info (name), base_list (bl), n_bases (bn) {}
  
!   // This is a little complex.
!   virtual void* dcast (const type_info &, int, void *,
! 		       const type_info * = 0, void * = 0) const;
  };
--- 102,111 ----
    __class_type_info (const char *name, const base_info *bl, size_t bn)
      : __user_type_info (name), base_list (bl), n_bases (bn) {}
  
!   // This is a little complex (to say the least).
!   virtual void do_dcast (const type_info &target, void *objptr,
! 		         const type_info *sub, void *subptr,
! 	                 do_dcast_result *result) const;
!   virtual sub_kind find_base (const type_info &base, void *objptr,
!                               void *baseptr) const;
  };
Index: egcs/gcc/cp/tinfo.cc
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/tinfo.cc,v
retrieving revision 1.10
diff -c -3 -p -r1.10 tinfo.cc
*** tinfo.cc	1999/05/02 22:44:24	1.10
--- tinfo.cc	1999/06/17 17:29:42
*************** __rtti_user (void *addr, const char *nam
*** 63,134 ****
  { 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)
! 	    match_found = p;
! 	  else if (match_found != p)
  	    {
! 	      if (sub)
! 		{
! 		  // Perhaps we're downcasting from *sub to desired; see if
! 		  // subptr is a subobject of exactly one of {match_found,p}.
! 
! 		  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)
! 		    {
! 		      match_found = p;
! 		      continue;
! 		    }
! 		}
! 
! 	      // base found at two different pointers,
! 	      // conversion is not unique
! 	      return 0;
  	    }
  	}
      }
  
!   return match_found;
  }
--- 63,348 ----
  { new (addr) __user_type_info (name); }
  
  // dynamic_cast helper methods.
  
! void *__user_type_info::
! dcast (const type_info &target, int need_public, void *objptr,
!        const type_info *sub = 0, void *subptr = 0, bool *ok = 0) const
  {
+   do_dcast_result result;
+   
+   result.target_obj = NULL;
+   result.is_public = true;
+   result.base_type = NULL;
+   result.sub_obj_kind = unknown;
+   
+   do_dcast(target, objptr, sub, subptr, &result);
+   bool is_ok = result.base_type && result.base_type != (const void *)ambig_base;
+   
+   if (is_ok && need_public)
+     {
+       // We can unambiguously downcast from the most derrived type to the
+       // target type. We need to check access and there are several cases to
+       // consider.
+       //   1) we've no sub -- is_public tells us
+       //   2) sub is a base of target -- subptr must be a public base of target
+       //   3) sub is not a base of target -- (i) target must be a public base
+       // of most derrived. and (ii) subptr must be a public base of most derrived
+       //   If you're confused (you will be) see 5.2.7/8 [expr.dynamic.cast].
+       
+       is_ok = result.is_public; // 1 and 3 part i
+       
+       if (!sub)
+         /* ok */;
+       else if (result.sub_obj_kind >= contained_private)
+         is_ok = result.sub_obj_kind >= contained_public; // 2 cached
+       else
+         {
+           if (result.sub_obj_kind != not_contained)
+             {
+               const __user_type_info &t =
+ 	        static_cast <const __user_type_info &> (target);
+ 	      sub_kind base_kind = t.find_base (*sub, result.target_obj, subptr);
+ 	  
+              if (base_kind >= contained_private)
+                 {
+                   // 2
+                   is_ok = base_kind >= contained_public;
+                   goto found; // break
+                 }
+             }
+           if (is_ok)
+             {
+               // 3 part ii
+ 	      sub_kind base_kind = find_base (*sub, objptr, subptr);
+               
+               if (base_kind < contained_private)
+                 abort(); // this cannot happen ...
+               is_ok = base_kind >= contained_public;
+             }
+         }
+      }
+   found:
+   
+   if (ok)
+     *ok = is_ok;
+   return is_ok ? result.target_obj : NULL;
+ }
+ 
+ void __user_type_info::
+ do_dcast (const type_info& to, void *addr,
+           const type_info *, void *,
+           do_dcast_result *__restrict result) const
+ {
    if (*this == to)
!     {
!       result->target_obj = addr;
!       result->base_type = (const type_info *)(const void *)valid_base;
!       return;
!     }
!   return;
! }
! 
! __user_type_info::sub_kind __user_type_info::
! find_base (const type_info&, void *addr, void *baseptr) const
! {
!   if (baseptr == addr)
!     // if baseptr matches us it must be our type or a base type (which are public).
!     return contained_public;
!   return not_contained;
! }
! 
! void __si_type_info::
! do_dcast (const type_info& to, void *addr,
!           const type_info *sub, void *subptr,
!           do_dcast_result *__restrict result) const
! {
!   if (*this == to)
!     {
!       result->target_obj = addr;
!       result->base_type = (const type_info *)(const void *)valid_base;
!       return;
!     }
!   return base.do_dcast (to, addr, sub, subptr, result);
  }
  
! __user_type_info::sub_kind __si_type_info::
! find_base (const type_info& to, void *addr, void *baseptr) const
  {
+   if (baseptr == addr && *this == to)
+     return contained_public;
+   return base.find_base (to, addr, baseptr);
+ }
+ 
+ void __class_type_info::
+ do_dcast (const type_info& desired, void *objptr,
+           const type_info *sub, void *subptr,
+           do_dcast_result *__restrict result) const
+ {
    if (*this == desired)
!     {
!       result->target_obj = objptr;
!       result->base_type = (const type_info *)(const void *)valid_base;
!       return;
!     }
  
    for (size_t i = 0; i < n_bases; i++)
      {
!       do_dcast_result result2;
!       
!       result2.target_obj = NULL;
!       result2.is_public = true;
!       result2.base_type = NULL;
!       result2.sub_obj_kind = unknown;
  
!       void *p = objptr;
        if (p)
+         {
+           p = (char *)p + base_list[i].offset;
+           if (base_list[i].is_virtual)
+   	    p = *(void **)p;
+         }
+       base_list[i].base->do_dcast (desired, p, sub, subptr, &result2);
+       if (result2.base_type)
  	{
!           if (result2.base_type == (const void *)valid_base
!               && base_list[i].is_virtual)
!             result2.base_type = base_list[i].base;
!           if (base_list[i].access != PUBLIC)
!             result2.is_public = false;
!       
! 	  if (!result->base_type)
!             *result = result2;
! 	  else if (result->target_obj == result2.target_obj)
  	    {
! 	      // found at same address, or both ambiguous
! 	      
! 	      if (result->target_obj)
! 	        {
! 	          // ok, found via virtual
! 	          if (result2.is_public)
! 	            result->is_public = true;
! 	        }
! 	      else if (!objptr)
! 	        {
! 	          // dealing with a null pointer, check vbase containing each
! 	          if (result2.base_type == (const void *)valid_base
! 	              || result2.base_type == (const void *)ambig_base
! 	              || result->base_type == (const void *)valid_base
! 	              || result->base_type == (const void *)ambig_base
! 	              || !(*result2.base_type == *result->base_type))
! 	            {
!     	              result->base_type = (const type_info *)(const void *)ambig_base;
!                       result->target_obj = NULL;
!                       break;
! 	            }
! 	        }
! 	      else
! 	        ;// both ambiguous, no change
  	    }
+ 	  else if (sub)
+             {
+               // See whether subobj is contained within one of the choices.
+               // If it is in only one, then that's the choice.
+               // If it is in both, then we're ambiguous and fail. If it is in
+               // neither, we're ambiguous, but don't yet fail as we might later
+               // find a third base which does contain subptr.
+             
+               sub_kind new_sub_kind = result2.sub_obj_kind;
+               sub_kind old_sub_kind = result->sub_obj_kind;
+               const __user_type_info &d =
+                 static_cast <const __user_type_info &> (desired);
+               
+               if (old_sub_kind >= not_contained)
+                 ;// already calculated
+               else if (result->base_type == (const void *)ambig_base)
+                 abort();// this cannot happen
+               else
+                 old_sub_kind = d.find_base (*sub, result->target_obj, subptr);
+               
+               if (new_sub_kind >= not_contained)
+                 ;// already calculated
+               else if (result2.base_type == (const void *)ambig_base)
+                 abort();// this cannot happen
+               else
+                 new_sub_kind = d.find_base (*sub, result2.target_obj, subptr);
+               
+               if (old_sub_kind >= contained_private && new_sub_kind < contained_ambig)
+                 {
+                   // Only in previous
+                   result->sub_obj_kind = old_sub_kind;
+                   if (!(old_sub_kind & contained_virtual_mask))
+                     break; // there can't be an ambiguous one later
+                 }
+               else if (new_sub_kind >= contained_private && old_sub_kind < contained_ambig)
+                 {
+                   // Only in new
+                   *result = result2;
+                   result->sub_obj_kind = new_sub_kind;
+                   if (!(new_sub_kind & contained_virtual_mask))
+                     break; // there can't be an ambiguous one later
+                 }
+               else if (new_sub_kind == contained_ambig
+                        || old_sub_kind == contained_ambig
+                        || (new_sub_kind >= contained_private
+                            && old_sub_kind >= contained_private))
+                 {
+                   // in both
+                   result->base_type = (const type_info *)(const void *)ambig_base;
+                   result->target_obj = NULL;
+                   result->sub_obj_kind = contained_ambig;
+                   break;
+                 }
+               else
+                 {
+                   // in neither, ambiguous for the moment, but keep looking
+                   result->base_type = (const type_info *)(const void *)ambig_base;
+                   result->target_obj = NULL;
+                   result->sub_obj_kind = not_contained;
+                 }
+             }
+           else
+             {
+ 	      result->base_type = (const type_info *)(const void *)ambig_base;
+ 	      result->target_obj = NULL;
+ 	      break;
+ 	    }
  	}
      }
  
!   return;
! }
! 
! __user_type_info::sub_kind __class_type_info::
! find_base (const type_info& to, void *objptr, void *baseptr) const
! {
!   if (baseptr == objptr && to == *this)
!     return contained_public;
!   
!   sub_kind best_base_kind = not_contained;
!   
!   for (size_t i = 0; i < n_bases; i++)
!     {
!       void *p = objptr;
!       sub_kind base_kind;
!       
!       p = (char *)p + base_list[i].offset;
!       if (base_list[i].is_virtual)
!         p = *(void **)p;
! 
!       base_kind = base_list[i].base->find_base (to, p, baseptr);
!       if (base_kind & contained_mask)
!         {
!           if (base_list[i].is_virtual)
!             base_kind = sub_kind(base_kind | contained_virtual_mask); // mark as virtual
!           if (base_list[i].access != PUBLIC)
!             base_kind = sub_kind(base_kind & ~ contained_public_mask); // mark private
!           if (base_kind & contained_public_mask)
!             return base_kind; // found publicly, can't get better
!           if (!(base_kind & contained_virtual_mask))
!             return base_kind; // found non-virtually, can't get better
!           best_base_kind = base_kind;
!         }
!     }
!   
!   return best_base_kind;
  }
Index: egcs/gcc/cp/tinfo2.cc
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/tinfo2.cc,v
retrieving revision 1.10
diff -c -3 -p -r1.10 tinfo2.cc
*** tinfo2.cc	1998/12/16 21:16:15	1.10
--- tinfo2.cc	1999/06/17 17:29:42
*************** struct __array_type_info : public type_i
*** 91,99 ****
  // Entry points for the compiler.
  
  /* Low level match routine used by compiler to match types of catch
!    variables and thrown objects.  */
  
! extern "C" void*
  __throw_type_match_rtti (const void *catch_type_r, const void *throw_type_r,
  			 void *objptr)
  {
--- 91,103 ----
  // Entry points for the compiler.
  
  /* Low level match routine used by compiler to match types of catch
!    variables and thrown objects.  Return NULL on no match and adjusted object
!    pointer for a match.  Because OBJPTR can be NULL, (when (T *)NULL was
!    thrown), it would be possible for us to return NULL on success.  But
!    that would be taken as not matching.  In that case, we return the special
!    value (void *)1, which should be checked for.  */
  
! extern "C" void *
  __throw_type_match_rtti (const void *catch_type_r, const void *throw_type_r,
  			 void *objptr)
  {
*************** __throw_type_match_rtti (const void *cat
*** 101,107 ****
    const type_info &throw_type = *(const type_info *)throw_type_r;
    
    if (catch_type == throw_type)
!     return objptr;
    
  #if 0
    printf ("We want to match a %s against a %s!\n",
--- 105,115 ----
    const type_info &throw_type = *(const type_info *)throw_type_r;
    
    if (catch_type == throw_type)
!     {
!       if (!objptr)
!         objptr = (void *)1;
!       return objptr;
!     }
    
  #if 0
    printf ("We want to match a %s against a %s!\n",
*************** __throw_type_match_rtti (const void *cat
*** 109,120 ****
  #endif
  
    void *new_objptr = 0;
  
    if (const __user_type_info *p
        = dynamic_cast <const __user_type_info *> (&throw_type))
      {
!       /* The 1 skips conversions to private bases. */
!       new_objptr = p->dcast (catch_type, 1, objptr);
      }
    else if (const __pointer_type_info *fr =
  	   dynamic_cast <const __pointer_type_info *> (&throw_type))
--- 117,129 ----
  #endif
  
    void *new_objptr = 0;
+   bool match = false;
  
    if (const __user_type_info *p
        = dynamic_cast <const __user_type_info *> (&throw_type))
      {
!       /* The 1 inhibits conversions to private bases. */
!       new_objptr = p->dcast (catch_type, 1, objptr, NULL, NULL, &match);
      }
    else if (const __pointer_type_info *fr =
  	   dynamic_cast <const __pointer_type_info *> (&throw_type))
*************** __throw_type_match_rtti (const void *cat
*** 153,167 ****
  	goto fail;
  
        if (*subto == *subfr)
! 	new_objptr = objptr;
        else if (*subto == typeid (void)
  	       && dynamic_cast <const __func_type_info *> (subfr) == 0)
! 	new_objptr = objptr;
        else if (const __user_type_info *p
  	       = dynamic_cast <const __user_type_info *> (subfr))
  	{
! 	  /* The 1 skips conversions to private bases. */
! 	  new_objptr = p->dcast (*subto, 1, objptr);
  	}
        else if (const __pointer_type_info *pfr
  	       = dynamic_cast <const __pointer_type_info *> (subfr))
--- 162,182 ----
  	goto fail;
  
        if (*subto == *subfr)
!         {
! 	  new_objptr = objptr;
! 	  match = true;
! 	}
        else if (*subto == typeid (void)
  	       && dynamic_cast <const __func_type_info *> (subfr) == 0)
!         {
!   	  new_objptr = objptr;
!           match = true;
! 	}
        else if (const __user_type_info *p
  	       = dynamic_cast <const __user_type_info *> (subfr))
  	{
! 	  /* The 1 inhibits conversions to private bases. */
! 	  new_objptr = p->dcast (*subto, 1, objptr, NULL, NULL, &match);
  	}
        else if (const __pointer_type_info *pfr
  	       = dynamic_cast <const __pointer_type_info *> (subfr))
*************** __throw_type_match_rtti (const void *cat
*** 212,217 ****
--- 227,233 ----
  	      if (*subto == *subfr)
  		{
  		  new_objptr = objptr;
+ 	          match = true;
  		  break;
  		}
  
*************** __throw_type_match_rtti (const void *cat
*** 225,236 ****
  	    }
  	}
      }
!  fail:
  
  #if 0
!   if (new_objptr)
      printf ("It converts, delta is %d\n", new_objptr-objptr);
  #endif
    return new_objptr;
  }
  
--- 241,256 ----
  	    }
  	}
      }
!   
  
  #if 0
!   if (match)
      printf ("It converts, delta is %d\n", new_objptr-objptr);
  #endif
+   if (match && !new_objptr)
+       new_objptr = (void *)1;
+ 
+   fail:
    return new_objptr;
  }
  
Index: egcs/gcc/cp/exception.cc
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/exception.cc,v
retrieving revision 1.23
diff -c -3 -p -r1.23 exception.cc
*** exception.cc	1998/12/16 21:15:22	1.23
--- exception.cc	1999/06/17 17:29:42
*************** __eh_free (void *p)
*** 168,173 ****
--- 168,180 ----
  
  typedef void * (* rtimetype) (void);
  
+ /* Check a thrown type against what we're prepared to accept. Return
+    non-NULL if we should catch it. Return NULL if we don't want it.
+    If we do take it, adjust any object pointer to the expected type.
+    Remember C++ allows you to throw (T *)NULL. __throw_type_match_rtti
+    catches that and returns (void *)1, we need to spot that. We also need
+    to make sure we never return NULL, when we want the exception.  */
+ 
  extern "C" void *
  __cplus_type_matcher (cp_eh_info *info, rtimetype match_info, 
                                   exception_descriptor *exception_table)
*************** __cplus_type_matcher (cp_eh_info *info, 
*** 180,186 ****
      return NULL;
  
    if (match_info == CATCH_ALL_TYPE)
!     return info->value;
  
    /* we don't worry about version info yet, there is only one version! */
    
--- 187,193 ----
      return NULL;
  
    if (match_info == CATCH_ALL_TYPE)
!     return info;
  
    /* we don't worry about version info yet, there is only one version! */
    
*************** __cplus_type_matcher (cp_eh_info *info, 
*** 188,195 ****
    ret = __throw_type_match_rtti (match_type, info->type, info->original_value);
    /* change value of exception */
    if (ret)
!     info->value = ret;
!   return ret;
  }
  
  
--- 195,205 ----
    ret = __throw_type_match_rtti (match_type, info->type, info->original_value);
    /* change value of exception */
    if (ret)
!     {
!       info->value = ret == (void *)1 ? NULL : ret;
!       return info;
!     }
!   return NULL;
  }
  
  

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