diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 624e01cba31..2feaf83ae72 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,34 @@ +1999-09-21 Nathan Sidwell + + 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 * cp-tree.h (finish_stmt_expr): Change prototype. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 8744b7fdc17..78ec70e1314 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -3596,6 +3596,7 @@ extern int types_overlap_p PROTO((tree, tree)); 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)); diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c index 5ab8d63f85b..8ce5102e6fc 100644 --- a/gcc/cp/rtti.c +++ b/gcc/cp/rtti.c @@ -588,6 +588,7 @@ build_dynamic_cast_1 (type, expr) { 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(b) (b an object) cannot succeed. */ @@ -632,20 +633,23 @@ build_dynamic_cast_1 (type, expr) 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 @@ -656,7 +660,7 @@ build_dynamic_cast_1 (type, expr) 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)))))); diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 1f599c457a0..b14871f7fcd 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -90,6 +90,7 @@ static tree dfs_no_overlap_yet PROTO((tree, void *)); 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 @@ -494,6 +495,77 @@ get_base_distance (parent, binfo, protect, path_ptr) 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'. diff --git a/gcc/cp/tinfo.cc b/gcc/cp/tinfo.cc index fe22e8de074..1d4885c9264 100644 --- a/gcc/cp/tinfo.cc +++ b/gcc/cp/tinfo.cc @@ -62,100 +62,453 @@ extern "C" void __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 { - *valp = addr; - return (*this == to); + upcast_result result; + + if (do_upcast (contained_public, target, objptr, result)) + return 0; + *adjptr = result.target_obj; + return contained_public_p (result.whole2target); } -int __si_type_info:: -dcast (const type_info& to, int require_public, void *addr, void **valp, - const type_info *sub, void *subptr) const +// 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 { - if (*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 (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; +} + +// 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 == 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 (*this == desired) - return 1; - - int match_found = 0; - void *match = 0; - - for (size_t i = 0; i < n_bases; i++) + 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; + result.target2sub = not_contained; + return false; + } + return false; +} - void *p; +// 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; +} - 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 (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; - } +// 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); +} + +// 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) + { + // 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); +} + +// 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; +} + +// 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 (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; } diff --git a/gcc/cp/tinfo.h b/gcc/cp/tinfo.h index ffca9c35cf1..39be84edde5 100644 --- a/gcc/cp/tinfo.h +++ b/gcc/cp/tinfo.h @@ -10,10 +10,138 @@ 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 (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. @@ -25,8 +153,16 @@ public: __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. @@ -49,7 +185,14 @@ struct __class_type_info : public __user_type_info { __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; }; diff --git a/gcc/cp/tinfo2.cc b/gcc/cp/tinfo2.cc index 877872f4e70..f24b59bd4ed 100644 --- a/gcc/cp/tinfo2.cc +++ b/gcc/cp/tinfo2.cc @@ -108,8 +108,7 @@ __throw_type_match_rtti_2 (const void *catch_type_r, const void *throw_type_r, if (const __user_type_info *p = dynamic_cast (&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 (&throw_type)) @@ -154,10 +153,7 @@ __throw_type_match_rtti_2 (const void *catch_type_r, const void *throw_type_r, return 1; else if (const __user_type_info *p = dynamic_cast (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 (subfr)) { @@ -274,14 +270,20 @@ __rtti_array (void *addr, const char *name) 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 (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