mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-20 01:14:37 +08:00
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. From-SVN: r29544
This commit is contained in:
parent
e36e6e0261
commit
4a9e5c6725
@ -1,3 +1,34 @@
|
||||
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.
|
||||
|
@ -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));
|
||||
|
@ -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<D&>(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))))));
|
||||
|
@ -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'.
|
||||
|
513
gcc/cp/tinfo.cc
513
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 <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;
|
||||
}
|
||||
|
||||
// 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 <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;
|
||||
}
|
||||
// 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 <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;
|
||||
}
|
||||
|
161
gcc/cp/tinfo.h
161
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 <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.
|
||||
@ -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;
|
||||
};
|
||||
|
@ -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 <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))
|
||||
@ -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 <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))
|
||||
{
|
||||
@ -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 <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
|
||||
|
Loading…
Reference in New Issue
Block a user