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]

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


Hi,
this patch supersedes
http://egcs.cygnus.com/ml/egcs-patches/1999-06/msg00226.html (but not the
testcases). All the logic of that patch is included here, so I won't repeat the
description.

Alfred Minarik's testcase in
http://egcs.cygnus.com/ml/egcs-patches/1999-06/msg00307.html showed another
pre-existing flaw in dcast's logic. This amendment applies some post checks in
dcast on the appropriate access path. I see Alfred's submitted a patch
http://egcs.cygnus.com/ml/egcs-patches/1999-06/msg00362.html for this too,
however that appears to do more graph walking than this attached patch. 

This patch has been tested with the current test suite, the new tests I posted
with the previous version of the patch, Alfred's new testcase, and the new
testcase included with this patch.

Some notes,
1) The NEED_PUBLIC parameter to dcast is always non-zero. Perhaps it could be
removed.
2) Making all access public in a correct program can change its behaviour
because previously disallowed dynamic_casts or exception catching can now
succeed
3) 5.2.7/8 is surprisingly complicated :-)

I'm not attaching Alfred's test case, but recommend that it is made suitable
for testsuite inclusion.

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
1999-06-15  Nathan Sidwell  <nathan@acm.org>

	* tinfo.h (valid_base, ambig_base): New enumeration flags.
	(dcast): Add OK parameter. Check target access.
	(do_dcast): Move functionality from dcast. Alter parameters.
	* tinfo.cc (dcast): Dispatch to do_dcast.
	(do_dcast): Move old dcast functionality here and adjust.
	Reimplement arbitrary class searching to deal with NULL
	conversions and correctly detect ambiguity.
	* 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/15 12:01:59
***************
*** 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,44 ----
  // 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
!   };
!     
!   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. BASETYPE is used to indicate,
!   //   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)
!   // IS_PUBLIC is used to indicate we found it with public access.
!   // Returns adjusted pointer.
!   virtual void* do_dcast (const type_info &target, void *objptr,
! 		          const type_info *sub, void *subptr,
! 	                  const type_info **basetype, bool *is_public) 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.
--- 50,58 ----
    __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,
! 	                  const type_info **basetype, bool *is_public) 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;
  };
--- 75,82 ----
    __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,
! 	                  const type_info **basetype, bool *is_public) 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/15 12:02:00
*************** __rtti_user (void *addr, const char *nam
*** 63,105 ****
  { 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)
  		{
--- 63,215 ----
  { 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
+ {
+   const std::type_info *type;
+   bool is_public;
+   
+   void *adj = do_dcast(target, objptr, sub, subptr, &type, &is_public);
+   bool is_ok = type && 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 -- sub 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) sub 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 = is_public; // 1 and 3
+       
+       if (sub)
+         {
+           const __user_type_info &t =
+ 	    static_cast <const __user_type_info &> (target);
+ 	  
+           t.do_dcast(*sub, adj, sub, subptr, &type, &is_public);
+           if (type)
+             {
+               // 2
+               if (type == (const void *)ambig_base)
+                 abort(); // this cannot happen ...
+               is_ok = is_public;
+             }
+           else if (is_ok)
+             {
+               // 3 part ii
+               do_dcast(*sub, subptr, sub, subptr, &type, &is_public);
+               if (!type || type == (const void *)ambig_base)
+                 abort(); // this cannot happen ...
+               is_ok = is_public;
+             }
+         }
+      }
+   if (!is_ok)
+     adj = NULL;
+   if (ok)
+     *ok = is_ok;
+   return adj;
+ }
+ 
  void * __user_type_info::
! do_dcast (const type_info& to, void *addr,
!           const type_info *, void *,
!           const type_info **basetype, bool *is_public) const
! {
!   *is_public = true;
!   if (*this == to)
!     {
!       *basetype = (const type_info *)(const void *)valid_base;
!       return addr;
!     }
!   *basetype = 0;
!   return 0;
! }
  
  void * __si_type_info::
! do_dcast (const type_info& to, void *addr,
!           const type_info *sub, void *subptr,
!           const type_info **basetype, bool *is_public) const
  {
+   *is_public = true;
    if (*this == to)
!     {
!       *basetype = (const type_info *)(const void *)valid_base;
!       return addr;
!     }
!   return base.do_dcast (to, addr, sub, subptr, basetype, is_public);
  }
  
! void * __class_type_info::
! do_dcast (const type_info& desired, void *objptr,
!           const type_info *sub, void *subptr,
!           const type_info **basetype, bool *is_public) const
  {
+   *is_public = true;
    if (*this == desired)
!     {
!       *basetype = (const type_info *)(const void *)valid_base;
!       return objptr;
!     }
  
    void *match_found = 0;
+   const type_info *found_type = 0;
+   bool public_p = false;
+   
    for (size_t i = 0; i < n_bases; i++)
      {
!       void *p = objptr;
        if (p)
+         {
+           p = (char *)p + base_list[i].offset;
+           if (base_list[i].is_virtual)
+   	    p = *(void **)p;
+         }
+       bool base_pub;
+       const type_info *base_flag;
+       p = base_list[i].base->do_dcast (desired, p, sub, subptr,
+                                        &base_flag, &base_pub);
+       if (base_flag)
  	{
!           if (base_flag == (const void *)ambig_base)
!             {
!               found_type = base_flag;
!               match_found = NULL;
!               break;
!             }
!       
!           if (base_flag == (const void *)valid_base && base_list[i].is_virtual)
!             base_flag = base_list[i].base;
!           if (base_list[i].access != PUBLIC)
!             base_pub = false;
!       
! 	  if (!found_type)
! 	    {
! 	      match_found = p;
! 	      found_type = base_flag;
! 	      public_p = base_pub;
! 	    }
! 	  else if (match_found == p)
! 	    {
! 	      // found at same address -- if p is NULL, we need to be carefull
!               // For non-NULL p, this must be ok.
! 	      if (!p && (base_flag == (const void *)valid_base
! 	                 || found_type == (const void *)valid_base
! 	                 || !(*base_flag == *found_type)))
! 	        {
!                   found_type = (const type_info *)(const void *)ambig_base;
!                   match_found = 0;
!                   break;
! 	        }
! 	      public_p |= base_pub;
! 	    }
! 	  else
  	    {
  	      if (sub)
  		{
*************** dcast (const type_info& desired, int is_
*** 108,116 ****
  
  		  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 */;
--- 218,228 ----
  
  		  const __user_type_info &d =
  		    static_cast <const __user_type_info &> (desired);
+ 	          bool pub;
+ 	          const type_info *ot;
  
! 		  void *os = d.do_dcast (*sub, match_found, NULL, NULL, &ot, &pub);
! 		  void *ns = d.do_dcast (*sub, p, NULL, NULL, &ot, &pub);
  
  		  if (os == ns)
  		    /* ambiguous -- subptr is a virtual base */;
*************** dcast (const type_info& desired, int is_
*** 119,134 ****
  		  else if (ns == subptr)
  		    {
  		      match_found = p;
  		      continue;
  		    }
  		}
  
  	      // base found at two different pointers,
  	      // conversion is not unique
! 	      return 0;
  	    }
  	}
      }
  
    return match_found;
  }
--- 231,252 ----
  		  else if (ns == subptr)
  		    {
  		      match_found = p;
+ 	              public_p = base_pub;
+ 	              found_type = base_flag;
  		      continue;
  		    }
  		}
  
  	      // base found at two different pointers,
  	      // conversion is not unique
! 	      found_type = (const type_info *)(const void *)ambig_base;
! 	      match_found = 0;
! 	      break;
  	    }
  	}
      }
  
+   *basetype = found_type;
+   *is_public = public_p;
    return match_found;
  }
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/15 12:02:00
*************** 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/15 12:02:01
*************** __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;
  }
  
  
// 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;
}

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