+1999-09-21 Nathan Sidwell <nathan@acm.org>
+
+ Reimplement dynamic cast and catch matching.
+ * cp-tree.h (get_dynamic_cast_base_type): Prototype new function
+ * search.c (dynamic_cast_base_recurse): New function.
+ (get_dynamic_cast_base_type): New function for dynamic cast.
+ * rtti.c (build_dynamic_cast_1): Determine source and target
+ class relationship. Call __dynamic_cast_2.
+ * tinfo.h (__user_type_info::upcast): New catch dispatcher.
+ (__user_type_info::dyncast): New dynamic cast dispatcher.
+ (__user_type_info::sub_kind): New nested enumeration.
+ (__user_type_info::contained_p): sub_kind predicate.
+ (__user_type_info::contained_public_p): Likewise.
+ (__user_type_info::contained_nonpublic_p): Likewise.
+ (__user_type_info::contained_nonvirtual_p: Likewise.
+ (__user_type_info::upcast_result): New nested struct.
+ (__user_type_info::dyncast_result): New nested struct.
+ (*::do_upcast): New catch function.
+ (*::do_dyncast): New dynamic cast function.
+ (__user_type_info::find_public_subobj): New dynamic cast
+ helper dispatcher.
+ (*::do_find_public_subobj): New dynamic cast helper function.
+ * tinfo.cc (__user_type_info::upcast): Define catch dispatcher.
+ (__user_type_info::dyncast): Define dynamic cast dispatcher.
+ (*::do_upcast): Define catch function.
+ (*::do_dyncast): Define dynamic cast function.
+ (*::do_find_public_subobj): Define dynamic cast helper function.
+ * tinfo2.cc (__throw_type_match_rtti_2): Use upcast.
+ (__dynamic_cast): Backwards compatibility wrapper. Use dyncast.
+ (__dynamic_cast_2): New dynamic cast runtime.
+
1999-09-20 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (finish_stmt_expr): Change prototype.
extern tree get_vbase PROTO((tree, tree));
extern tree get_binfo PROTO((tree, tree, int));
extern int get_base_distance PROTO((tree, tree, int, tree *));
+extern tree get_dynamic_cast_base_type PROTO((tree, tree));
extern int accessible_p PROTO((tree, tree));
extern tree lookup_field PROTO((tree, tree, int, int));
extern int lookup_fnfields_1 PROTO((tree, tree));
{
tree retval;
tree result, td1, td2, td3, elems, expr2;
+ tree static_type, target_type, boff;
/* If we got here, we can't convert statically. Therefore,
dynamic_cast<D&>(b) (b an object) cannot succeed. */
td1 = get_tinfo_fn_dynamic (expr);
td1 = decay_conversion (td1);
- td2 = decay_conversion
- (get_tinfo_fn (TYPE_MAIN_VARIANT (TREE_TYPE (type))));
- td3 = decay_conversion
- (get_tinfo_fn (TYPE_MAIN_VARIANT (TREE_TYPE (exprtype))));
+ target_type = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+ static_type = TYPE_MAIN_VARIANT (TREE_TYPE (exprtype));
+ td2 = decay_conversion (get_tinfo_fn (target_type));
+ td3 = decay_conversion (get_tinfo_fn (static_type));
+
+ /* Determine how T and V are related. */
+ boff = get_dynamic_cast_base_type (static_type, target_type);
elems = tree_cons
(NULL_TREE, td1, tree_cons
(NULL_TREE, td2, tree_cons
- (NULL_TREE, build_int_2 (1, 0), tree_cons
+ (NULL_TREE, boff, tree_cons
(NULL_TREE, expr2, tree_cons
- (NULL_TREE, td3, tree_cons
+ (NULL_TREE, td3, tree_cons
(NULL_TREE, expr1, NULL_TREE))))));
- dcast_fn = get_identifier ("__dynamic_cast");
+ dcast_fn = get_identifier ("__dynamic_cast_2");
if (IDENTIFIER_GLOBAL_VALUE (dcast_fn))
dcast_fn = IDENTIFIER_GLOBAL_VALUE (dcast_fn);
else
tmp = tree_cons
(NULL_TREE, TREE_TYPE (td1), tree_cons
(NULL_TREE, TREE_TYPE (td1), tree_cons
- (NULL_TREE, integer_type_node, tree_cons
+ (NULL_TREE, integer_type_node, tree_cons
(NULL_TREE, ptr_type_node, tree_cons
(NULL_TREE, TREE_TYPE (td1), tree_cons
(NULL_TREE, ptr_type_node, void_list_node))))));
static int get_base_distance_recursive
PROTO((tree, int, int, int, int *, tree *, tree,
int, int *, int, int));
+static int dynamic_cast_base_recurse PROTO((tree, tree, int, tree *));
static void expand_upcast_fixups
PROTO((tree, tree, tree, tree, tree, tree, tree *));
static void fixup_virtual_upcast_offsets
return rval;
}
+/* Worker function for get_dynamic_cast_base_type. */
+
+static int
+dynamic_cast_base_recurse (subtype, binfo, via_virtual, offset_ptr)
+ tree subtype;
+ tree binfo;
+ int via_virtual;
+ tree *offset_ptr;
+{
+ tree binfos;
+ int i, n_baselinks;
+ int worst = -3;
+
+ if (BINFO_TYPE (binfo) == subtype)
+ {
+ if (via_virtual)
+ return -2;
+ else
+ {
+ *offset_ptr = BINFO_OFFSET (binfo);
+ return 0;
+ }
+ }
+
+ binfos = BINFO_BASETYPES (binfo);
+ n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ int rval;
+
+ if (!TREE_VIA_PUBLIC (base_binfo))
+ continue;
+ rval = dynamic_cast_base_recurse
+ (subtype, base_binfo,
+ via_virtual || TREE_VIA_VIRTUAL (base_binfo), offset_ptr);
+ if (worst == -3)
+ worst = rval;
+ else if (rval >= 0)
+ worst = worst >= 0 ? -1 : worst;
+ else if (rval > -3)
+ worst = worst < rval ? worst : rval;
+ }
+ return worst;
+}
+
+/* The dynamic cast runtime needs a hint about how the static SUBTYPE type started
+ from is related to the required TARGET type, in order to optimize the
+ inheritance graph search. This information is independant of the
+ current context, and ignores private paths, hence get_base_distance is
+ inappropriate. Return a TREE specifying the base offset, BOFF.
+ BOFF >= 0, there is only one public non-virtual SUBTYPE base at offset BOFF,
+ and there are no public virtual SUBTYPE bases.
+ BOFF == -1, SUBTYPE occurs as multiple public non-virtual bases.
+ BOFF == -2, SUBTYPE occurs as multiple public virtual or non-virtual bases.
+ BOFF == -3, SUBTYPE is not a public base. */
+
+tree
+get_dynamic_cast_base_type (subtype, target)
+ tree subtype;
+ tree target;
+{
+ tree offset = NULL_TREE;
+ int boff = dynamic_cast_base_recurse (subtype, TYPE_BINFO (target),
+ 0, &offset);
+
+ if (!boff)
+ return offset;
+ return build_int_2 (boff, -1);
+}
+
/* Search for a member with name NAME in a multiple inheritance lattice
specified by TYPE. If it does not exist, return NULL_TREE.
If the member is ambiguously referenced, return `error_mark_node'.
__rtti_user (void *addr, const char *name)
{ new (addr) __user_type_info (name); }
-// dynamic_cast helper methods.
-// Returns 1 if the cast succeeds, 0 otherwise. Stores the adjusted value
-// in VALP.
-
+// Upcast for catch checking. OBJPTR points to the thrown object and might be
+// NULL. Return 0 on failure, non-zero on success. Set *ADJPTR to adjusted
+// object pointer.
int __user_type_info::
-dcast (const type_info& to, int, void *addr, void **valp,
- const type_info *, void *) const
+upcast (const type_info &target, void *objptr,
+ void **adjptr) const
+{
+ upcast_result result;
+
+ if (do_upcast (contained_public, target, objptr, result))
+ return 0;
+ *adjptr = result.target_obj;
+ return contained_public_p (result.whole2target);
+}
+
+// Down or cross cast for dynamic_cast. OBJPTR points to the most derrived
+// object, SUBPTR points to the static base object. Both must not be NULL.
+// TARGET specifies the desired target type, SUBTYPE specifies the static
+// type. Both must be defined. Returns adjusted object pointer on success,
+// NULL on failure. [expr.dynamic.cast]/8 says 'unambiguous public base'. This
+// itself is an ambiguous statement. We choose it to mean the base must be
+// separately unambiguous and public, rather than unambiguous considering only
+// public bases.
+void *__user_type_info::
+dyncast (int boff,
+ const type_info &target, void *objptr,
+ const type_info &subtype, void *subptr) const
{
- *valp = addr;
- return (*this == to);
+ dyncast_result result;
+
+ do_dyncast (boff, contained_public,
+ target, objptr, subtype, subptr, result);
+ if (!result.target_obj)
+ return NULL;
+ if (contained_public_p (result.target2sub))
+ return result.target_obj;
+ if (contained_public_p (sub_kind (result.whole2sub & result.whole2target)))
+ // Found a valid cross cast
+ return result.target_obj;
+ if (contained_nonvirtual_p (result.whole2sub))
+ // Found an invalid cross cast, which cannot also be a down cast
+ return NULL;
+ if (result.target2sub == unknown)
+ result.target2sub = static_cast <const __user_type_info &> (target)
+ .find_public_subobj (boff, subtype,
+ result.target_obj, subptr);
+ if (contained_public_p (result.target2sub))
+ // Found a valid down cast
+ return result.target_obj;
+ // Must be an invalid down cast, or the cross cast wasn't bettered
+ return NULL;
}
-int __si_type_info::
-dcast (const type_info& to, int require_public, void *addr, void **valp,
- const type_info *sub, void *subptr) const
+// Catch cast helper. ACCESS_PATH is the access from the complete thrown
+// object to this base. TARGET is the desired type we want to catch. OBJPTR
+// points to this base within the throw object, it might be NULL. Fill in
+// RESULT with what we find. Return true, should we determine catch must fail.
+bool __user_type_info::
+do_upcast (sub_kind access_path,
+ const type_info &target, void *objptr,
+ upcast_result &__restrict result) const
{
- if (*this == to)
+ if (*this == target)
{
- *valp = addr;
- return 1;
+ result.target_obj = objptr;
+ result.base_type = nonvirtual_base_type;
+ result.whole2target = access_path;
+ return contained_nonpublic_p (access_path);
}
- return base.dcast (to, require_public, addr, valp, sub, subptr);
+ return false;
}
-int __class_type_info::
-dcast (const type_info& desired, int is_public, void *objptr, void **valp,
- const type_info *sub, void *subptr) const
+// dynamic cast helper. ACCESS_PATH gives the access from the most derived
+// object to this base. TARGET indicates the desired type we want. OBJPTR
+// points to this base within the object. SUBTYPE indicates the static type
+// started from and SUBPTR points to that base within the most derived object.
+// Fill in RESULT with what we find. Return true if we have located an
+// ambiguous match.
+bool __user_type_info::
+do_dyncast (int, sub_kind access_path,
+ const type_info &target, void *objptr,
+ const type_info &subtype, void *subptr,
+ dyncast_result &__restrict result) const
{
- *valp = objptr;
+ if (objptr == subptr && *this == subtype)
+ {
+ // The subobject we started from. Indicate how we are accessible from
+ // the most derived object.
+ result.whole2sub = access_path;
+ return false;
+ }
+ if (*this == target)
+ {
+ result.target_obj = objptr;
+ result.whole2target = access_path;
+ result.target2sub = not_contained;
+ return false;
+ }
+ return false;
+}
- if (*this == desired)
- return 1;
+// find_public_subobj helper. Return contained_public if we are the desired
+// subtype. OBJPTR points to this base type, SUBPTR points to the desired base
+// object.
+__user_type_info::sub_kind __user_type_info::
+do_find_public_subobj (int, const type_info &, void *objptr, void *subptr) const
+{
+ if (subptr == objptr)
+ // Must be our type, as the pointers match.
+ return contained_public;
+ return not_contained;
+}
- int match_found = 0;
- void *match = 0;
+// catch helper for single public inheritance types. See
+// __user_type_info::do_upcast for semantics.
+bool __si_type_info::
+do_upcast (sub_kind access_path,
+ const type_info &target, void *objptr,
+ upcast_result &__restrict result) const
+{
+ if (*this == target)
+ {
+ result.target_obj = objptr;
+ result.base_type = nonvirtual_base_type;
+ result.whole2target = access_path;
+ return contained_nonpublic_p (access_path);
+ }
+ return base.do_upcast (access_path, target, objptr, result);
+}
- for (size_t i = 0; i < n_bases; i++)
+// dynamic cast helper for single public inheritance types. See
+// __user_type_info::do_dyncast for semantics. BOFF indicates how SUBTYPE
+// types are inherited by TARGET types.
+bool __si_type_info::
+do_dyncast (int boff, sub_kind access_path,
+ const type_info &target, void *objptr,
+ const type_info &subtype, void *subptr,
+ dyncast_result &__restrict result) const
+{
+ if (objptr == subptr && *this == subtype)
{
- if (is_public && base_list[i].access != PUBLIC)
- continue;
+ // The subobject we started from. Indicate how we are accessible from
+ // the most derived object.
+ result.whole2sub = access_path;
+ return false;
+ }
+ if (*this == target)
+ {
+ result.target_obj = objptr;
+ result.whole2target = access_path;
+ if (boff >= 0)
+ result.target2sub = ((char *)subptr - (char *)objptr) == boff
+ ? contained_public : not_contained;
+ else if (boff == -3)
+ result.target2sub = not_contained;
+ return false;
+ }
+ return base.do_dyncast (boff, access_path,
+ target, objptr, subtype, subptr, result);
+}
+
+// find_public_subobj helper. See __user_type_info::do_find_public_subobj or
+// semantics. BOFF indicates how SUBTYPE types are inherited by the original
+// target object.
+__user_type_info::sub_kind __si_type_info::
+do_find_public_subobj (int boff, const type_info &subtype, void *objptr, void *subptr) const
+{
+ if (subptr == objptr && subtype == *this)
+ return contained_public;
+ return base.do_find_public_subobj (boff, subtype, objptr, subptr);
+}
- void *p;
+// catch helper for multiple or non-public inheritance types. See
+// __user_type_info::do_upcast for semantics.
+bool __class_type_info::
+do_upcast (sub_kind access_path,
+ const type_info &target, void *objptr,
+ upcast_result &__restrict result) const
+{
+ if (*this == target)
+ {
+ result.target_obj = objptr;
+ result.base_type = nonvirtual_base_type;
+ result.whole2target = access_path;
+ return contained_nonpublic_p (access_path);
+ }
+
+ for (size_t i = n_bases; i--;)
+ {
+ upcast_result result2;
+ void *p = objptr;
+ sub_kind sub_access = access_path;
+ if (p)
+ p = (char *)p + base_list[i].offset;
+ if (base_list[i].is_virtual)
+ {
+ if (p)
+ p = *(void **)p;
+ sub_access = sub_kind (sub_access | contained_virtual_mask);
+ }
+ if (base_list[i].access != PUBLIC)
+ sub_access = sub_kind (sub_access & ~contained_public_mask);
+ if (base_list[i].base->do_upcast (sub_access, target, p, result2))
+ return true; // must fail
+ if (result2.base_type)
+ {
+ if (result2.base_type == nonvirtual_base_type
+ && base_list[i].is_virtual)
+ result2.base_type = base_list[i].base;
+ if (!result.base_type)
+ result = result2;
+ else if (result.target_obj != result2.target_obj)
+ {
+ // Found an ambiguity.
+ result.target_obj = NULL;
+ result.whole2target = contained_ambig;
+ return true;
+ }
+ else if (result.target_obj)
+ {
+ // Ok, found real object via a virtual path.
+ result.whole2target
+ = sub_kind (result.whole2target | result2.whole2target);
+ }
+ else
+ {
+ // Dealing with a null pointer, need to check vbase
+ // containing each of the two choices.
+ if (result2.base_type == nonvirtual_base_type
+ || result.base_type == nonvirtual_base_type
+ || !(*result2.base_type == *result.base_type))
+ {
+ // Already ambiguous, not virtual or via different virtuals.
+ // Cannot match.
+ result.whole2target = contained_ambig;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
- if (objptr)
- {
- p = (char *)objptr + base_list[i].offset;
- if (base_list[i].is_virtual)
- p = *(void **)p;
- }
- else
- /* Preserve null pointer. */
- p = objptr;
-
- if (base_list[i].base->dcast (desired, is_public, p, &p, sub, subptr))
- {
- if (! match_found)
- {
- match_found = 1;
- match = p;
- }
- else if (match != 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, &os);
- void *ns;
- d.dcast (*sub, 1, p, &ns);
-
- if (os == ns)
- // Both have the same subobject, so we can't disambiguate;
- // i.e. subptr is a virtual base.
- return 0;
- else if (os == subptr)
- continue;
- else if (ns == subptr)
- {
- match = p;
- continue;
- }
- }
- else
- // We're not downcasting, so we can't disambiguate.
- return 0;
- }
+// dynamic cast helper for non-public or multiple inheritance types. See
+// __user_type_info::do_dyncast for overall semantics.
+// This is a big hairy function. Although the run-time behaviour of
+// dynamic_cast is simple to describe, it gives rise to some non-obvious
+// behaviour. We also desire to determine as early as possible any definite
+// answer we can get. Because it is unknown what the run-time ratio of
+// succeeding to failing dynamic casts is, we do not know in which direction
+// to bias any optimizations. To that end we make no particular effort towards
+// early fail answers or early success answers. Instead we try to minimize
+// work by filling in things lazily (when we know we need the information),
+// and opportunisticly take early success or failure results.
+bool __class_type_info::
+do_dyncast (int boff, sub_kind access_path,
+ const type_info &target, void *objptr,
+ const type_info &subtype, void *subptr,
+ dyncast_result &__restrict result) const
+{
+ if (objptr == subptr && *this == subtype)
+ {
+ // The subobject we started from. Indicate how we are accessible from
+ // the most derived object.
+ result.whole2sub = access_path;
+ return false;
+ }
+ if (*this == target)
+ {
+ result.target_obj = objptr;
+ result.whole2target = access_path;
+ if (boff >= 0)
+ result.target2sub = ((char *)subptr - (char *)objptr) == boff
+ ? contained_public : not_contained;
+ else if (boff == -3)
+ result.target2sub = not_contained;
+ return false;
+ }
+ bool result_ambig = false;
+ for (size_t i = n_bases; i--;)
+ {
+ dyncast_result result2;
+ void *p = (char *)objptr + base_list[i].offset;
+ sub_kind sub_access = access_path;
+ if (base_list[i].is_virtual)
+ {
+ p = *(void **)p;
+ sub_access = sub_kind (sub_access | contained_virtual_mask);
}
+ if (base_list[i].access != PUBLIC)
+ sub_access = sub_kind (sub_access & ~contained_public_mask);
+
+ bool result2_ambig
+ = base_list[i].base->do_dyncast (boff, sub_access,
+ target, p, subtype, subptr, result2);
+ result.whole2sub = sub_kind (result.whole2sub | result2.whole2sub);
+ if (result2.target2sub == contained_public
+ || result2.target2sub == contained_ambig)
+ {
+ result.target_obj = result2.target_obj;
+ result.whole2target = result2.whole2target;
+ result.target2sub = result2.target2sub;
+ // Found a downcast which can't be bettered or an ambiguous downcast
+ // which can't be disambiguated
+ return result2_ambig;
+ }
+
+ if (!result_ambig && !result.target_obj)
+ {
+ // Not found anything yet.
+ result.target_obj = result2.target_obj;
+ result.whole2target = result2.whole2target;
+ result_ambig = result2_ambig;
+ }
+ else if (result.target_obj && result.target_obj == result2.target_obj)
+ {
+ // Found at same address, must be via virtual. Pick the most
+ // accessible path.
+ result.whole2target =
+ sub_kind (result.whole2target | result2.whole2target);
+ }
+ else if ((result.target_obj && result2.target_obj)
+ || (result_ambig && result2.target_obj)
+ || (result2_ambig && result.target_obj))
+ {
+ // Found two different TARGET bases, or a valid one and a set of
+ // ambiguous ones, must disambiguate. See whether SUBOBJ is
+ // contained publicly within one of the non-ambiguous 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.target2sub;
+ sub_kind old_sub_kind = result.target2sub;
+
+ if (contained_nonvirtual_p (result.whole2sub))
+ {
+ // We already found SUBOBJ as a non-virtual base of most
+ // derived. Therefore if it is in either choice, it can only be
+ // in one of them, and we will already know.
+ if (old_sub_kind == unknown)
+ old_sub_kind = not_contained;
+ if (new_sub_kind == unknown)
+ new_sub_kind = not_contained;
+ }
+ else
+ {
+ const __user_type_info &t =
+ static_cast <const __user_type_info &> (target);
+
+ if (old_sub_kind >= not_contained)
+ ;// already calculated
+ else if (contained_nonvirtual_p (new_sub_kind))
+ // Already found non-virtually inside the other choice,
+ // cannot be in this.
+ old_sub_kind = not_contained;
+ else
+ old_sub_kind = t.find_public_subobj (boff, subtype,
+ result.target_obj, subptr);
+
+ if (new_sub_kind >= not_contained)
+ ;// already calculated
+ else if (contained_nonvirtual_p (old_sub_kind))
+ // Already found non-virtually inside the other choice,
+ // cannot be in this.
+ new_sub_kind = not_contained;
+ else
+ new_sub_kind = t.find_public_subobj (boff, subtype,
+ result2.target_obj, subptr);
+ }
+
+ // Neither sub_kind can be contained_ambig -- we bail out early
+ // when we find those.
+ if (contained_p (sub_kind (new_sub_kind ^ old_sub_kind)))
+ {
+ // Only on one choice, not ambiguous.
+ if (contained_p (new_sub_kind))
+ {
+ // Only in new.
+ result.target_obj = result2.target_obj;
+ result.whole2target = result2.whole2target;
+ result_ambig = false;
+ old_sub_kind = new_sub_kind;
+ }
+ result.target2sub = old_sub_kind;
+ if (result.target2sub == contained_public)
+ return false; // Can't be an ambiguating downcast for later discovery.
+ }
+ else if (contained_p (sub_kind (new_sub_kind & old_sub_kind)))
+ {
+ // In both.
+ result.target_obj = NULL;
+ result.target2sub = contained_ambig;
+ return true; // Fail.
+ }
+ else
+ {
+ // In neither publicly, ambiguous for the moment, but keep
+ // looking. It is possible that it was private in one or
+ // both and therefore we should fail, but that's just tough.
+ result.target_obj = NULL;
+ result.target2sub = not_contained;
+ result_ambig = true;
+ }
+ }
+
+ if (result.whole2sub == contained_private)
+ // We found SUBOBJ as a private non-virtual base, therefore all
+ // cross casts will fail. We have already found a down cast, if
+ // there is one.
+ return result_ambig;
}
- *valp = match;
- return match_found;
+ return result_ambig;
+}
+
+// find_public_subobj helper for non-public or multiple inheritance types. See
+// __user_type_info::do_find_public_subobj for semantics. We make use of BOFF
+// to prune the base class walk.
+__user_type_info::sub_kind __class_type_info::
+do_find_public_subobj (int boff, const type_info &subtype, void *objptr, void *subptr) const
+{
+ if (objptr == subptr && subtype == *this)
+ return contained_public;
+
+ for (size_t i = n_bases; i--;)
+ {
+ if (base_list[i].access != PUBLIC)
+ continue; // Not public, can't be here.
+ void *p = (char *)objptr + base_list[i].offset;
+ if (base_list[i].is_virtual)
+ {
+ if (boff == -1)
+ continue; // Not a virtual base, so can't be here.
+ p = *(void **)p;
+ }
+
+ sub_kind base_kind = base_list[i].base->do_find_public_subobj
+ (boff, subtype, p, subptr);
+ if (contained_p (base_kind))
+ {
+ if (base_list[i].is_virtual)
+ base_kind = sub_kind (base_kind | contained_virtual_mask);
+ return base_kind;
+ }
+ }
+
+ return not_contained;
}
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 int dcast (const type_info &, int, void *, void **,
- const type_info * = 0, void * = 0) const;
+ // If our type can be upcast to a public and unambiguous base, then return
+ // non-zero and set RES to point to the base object. OBJ points to the throw
+ // object and can be NULL, if there is no object to adjust.
+ int upcast (const type_info &target, void *obj, void **res) const;
+
+ // If our type can be dynamicly cast to the target type, then return
+ // pointer to the target object. OBJ is the pointer to the most derived
+ // type and cannot be NULL. SUBTYPE and SUBOBJ indicate the static type
+ // base object from whence we came, it cannot be NULL. SUBTYPE cannot be
+ // the same as TARGET. TARGET cannot be a base of SUBTYPE.
+ // BOFF indicates how SUBTYPE is related to TARGET.
+ // BOFF >= 0, there is only one public non-virtual SUBTYPE base at offset
+ // BOFF, and there are no public virtual SUBTYPE bases.
+ // Therefore check if SUBOBJ is at offset BOFF when we find a target
+ // BOFF == -1, SUBTYPE occurs as multiple public non-virtual bases.
+ // Lazily search the non-virtual bases of TARGET.
+ // BOFF == -2, SUBTYPE occurs as multiple public virtual or non-virtual bases.
+ // Lazily search all the bases of TARGET.
+ // BOFF == -3, SUBTYPE is not a public base.
+ // For backwards compatibility set BOFF to -2, that is the safe `don't know'
+ // value. We don't care about SUBTYPES as private bases of TARGET, as they
+ // can never succeed as downcasts, only as crosscasts -- and then only if
+ // they are virtual. This is more complicated that it might seem.
+ void *dyncast (int boff,
+ const type_info &target, void *obj,
+ const type_info &subtype, void *subobj) const;
+
+ // non_virtual_base_type is used to indicate that a base class is via a
+ // non-virtual access path.
+ static const type_info *const nonvirtual_base_type
+ = static_cast <const type_info *> (0) + 1;
+
+ // sub_kind tells us about how a base object is contained within a derived
+ // object. We often do this lazily, hence the UNKNOWN value. At other times
+ // we may use NOT_CONTAINED to mean not publicly contained.
+ enum sub_kind
+ {
+ unknown = 0, // we have no idea
+ not_contained, // not contained within us (in some
+ // circumstances this might mean not contained
+ // publicly)
+ 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
+ };
+ // some predicate functions for sub_kind
+ static inline bool contained_p (sub_kind access_path)
+ {
+ return access_path >= contained_mask;
+ }
+ static inline bool contained_public_p (sub_kind access_path)
+ {
+ return access_path >= contained_public;
+ }
+ static inline bool contained_nonpublic_p (sub_kind access_path)
+ {
+ return (access_path & contained_public) == contained_mask;
+ }
+ static inline bool contained_nonvirtual_p (sub_kind access_path)
+ {
+ return (access_path & (contained_mask | contained_virtual_mask))
+ == contained_mask;
+ }
+
+ struct upcast_result
+ {
+ void *target_obj; // pointer to target object or NULL (init NULL)
+ sub_kind whole2target; // path from most derived object to target
+ const type_info *base_type; // where we found the target, (init NULL)
+ // if in vbase the __user_type_info of vbase)
+ // if a non-virtual base then 1
+ // else NULL
+ public:
+ upcast_result ()
+ :target_obj (NULL), whole2target (unknown), base_type (NULL)
+ {}
+ };
+ struct dyncast_result
+ {
+ void *target_obj; // pointer to target object or NULL (init NULL)
+ sub_kind whole2target; // path from most derived object to target
+ sub_kind whole2sub; // path from most derived object to sub object
+ sub_kind target2sub; // path from target to sub object
+
+ public:
+ dyncast_result ()
+ :target_obj (NULL), whole2target (unknown),
+ whole2sub (unknown), target2sub (unknown)
+ {}
+ };
+
+ public:
+ // Helper for upcast. See if TARGET is us, or one of our bases. ACCESS_PATH
+ // gives the access from the start object. Return TRUE if we know the catch
+ // fails.
+ virtual bool do_upcast (sub_kind access_path,
+ const type_info &target, void *obj,
+ upcast_result &__restrict result) const;
+ // Helper for dyncast. BOFF indicates how the SUBTYPE is related to TARGET.
+ // ACCESS_PATH indicates the access from the most derived object. It is
+ // used to prune the DAG walk. All information about what we find is put
+ // into RESULT. Return true, if the match we have found is ambiguous.
+ virtual bool do_dyncast (int boff, sub_kind access_path,
+ const type_info &target, void *obj,
+ const type_info &subtype, void *subptr,
+ dyncast_result &__restrict result) const;
+ public:
+ // Indicate whether SUBPTR of type SUBTYPE is contained publicly within
+ // OBJPTR. OBJPTR points to this base object. BOFF indicates how SUBTYPE
+ // objects might be contained within this type. If SUBPTR is one of our
+ // SUBTYPE bases, indicate virtuality. Returns not_contained for non
+ // containment or private containment.
+ sub_kind find_public_subobj (int boff, const type_info &subtype,
+ void *objptr, void *subptr) const
+ {
+ if (boff >= 0)
+ return ((char *)subptr - (char *)objptr) == boff
+ ? contained_public : not_contained;
+ if (boff == -3)
+ return not_contained;
+ return do_find_public_subobj (boff, subtype, objptr, subptr);
+ }
+
+ public:
+ // Helper for find_subobj. BOFF indicates how SUBTYPE bases are inherited by
+ // the type started from -- which is not necessarily the current type.
+ // OBJPTR points to the current base.
+ virtual sub_kind do_find_public_subobj (int boff, const type_info &subtype,
+ void *objptr, void *subptr) const;
};
// type_info for a class with one public, nonvirtual base class.
__si_type_info (const char *n, const __user_type_info &b)
: __user_type_info (n), base (b) { }
- virtual int dcast (const type_info &, int, void *, void **,
- const type_info * = 0, void * = 0) const;
+ private:
+ virtual bool do_upcast (sub_kind access_path,
+ const type_info &target, void *obj,
+ upcast_result &__restrict result) const;
+ virtual bool do_dyncast (int boff, sub_kind access_path,
+ const type_info &target, void *obj,
+ const type_info &subtype, void *subptr,
+ dyncast_result &__restrict result) const;
+ virtual sub_kind do_find_public_subobj (int boff, const type_info &subtype,
+ void *objptr, void *subptr) const;
};
// type_info for a general class.
__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 int dcast (const type_info &, int, void *, void **,
- const type_info * = 0, void * = 0) const;
+ public:
+ virtual bool do_upcast (sub_kind access_path,
+ const type_info &target, void *obj,
+ upcast_result &__restrict result) const;
+ virtual bool do_dyncast (int boff, sub_kind access_path,
+ const type_info &target, void *obj,
+ const type_info &subtype, void *subptr,
+ dyncast_result &__restrict result) const;
+ virtual sub_kind do_find_public_subobj (int boff, const type_info &subtype,
+ void *objptr, void *subptr) const;
};
if (const __user_type_info *p
= dynamic_cast <const __user_type_info *> (&throw_type))
{
- /* The 1 skips conversions to private bases. */
- return p->dcast (catch_type, 1, objptr, valp);
+ return p->upcast (catch_type, objptr, valp);
}
else if (const __pointer_type_info *fr =
dynamic_cast <const __pointer_type_info *> (&throw_type))
return 1;
else if (const __user_type_info *p
= dynamic_cast <const __user_type_info *> (subfr))
- {
- /* The 1 skips conversions to private bases. */
- return p->dcast (*subto, 1, objptr, valp);
- }
+ return p->upcast (*subto, objptr, valp);
else if (const __pointer_type_info *pfr
= dynamic_cast <const __pointer_type_info *> (subfr))
{
extern "C" void *
__dynamic_cast (const type_info& (*from)(void), const type_info& (*to)(void),
- int require_public, void *address,
- const type_info & (*sub)(void), void *subptr)
+ int require_public, void *address, const type_info & (*sub)(void), void *subptr)
{
- void *ret;
- if (static_cast <const __user_type_info &> (from ()).dcast
- (to (), require_public, address, &ret, &(sub ()), subptr))
- return ret;
- return 0;
+ if (!require_public) abort();
+ return static_cast <__user_type_info const &> (from ()).dyncast
+ (/*boff=*/-2, to (), address, sub (), subptr);
+}
+
+extern "C" void *
+__dynamic_cast_2 (const type_info& (*from)(void), const type_info& (*to)(void),
+ int boff,
+ void *address, const type_info & (*sub)(void), void *subptr)
+{
+ return static_cast <__user_type_info const &> (from ()).dyncast
+ (boff, to (), address, sub (), subptr);
}
// type_info nodes and functions for the builtin types. The mangling here