285 lines
7.5 KiB
C
285 lines
7.5 KiB
C
|
/* Dynamic testing for abstract is-a relationships.
|
||
|
Copyright (C) 2012-2023 Free Software Foundation, Inc.
|
||
|
Contributed by Lawrence Crowl.
|
||
|
|
||
|
This file is part of GCC.
|
||
|
|
||
|
GCC is free software; you can redistribute it and/or modify it under
|
||
|
the terms of the GNU General Public License as published by the Free
|
||
|
Software Foundation; either version 3, or (at your option) any later
|
||
|
version.
|
||
|
|
||
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
|
for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with GCC; see the file COPYING3. If not see
|
||
|
<http://www.gnu.org/licenses/>. */
|
||
|
|
||
|
|
||
|
/* This header generic type query and conversion functions.
|
||
|
|
||
|
|
||
|
USING THE GENERIC TYPE FACILITY
|
||
|
|
||
|
|
||
|
The user functions are:
|
||
|
|
||
|
bool is_a <TYPE> (pointer)
|
||
|
|
||
|
Tests whether the pointer actually points to a more derived TYPE.
|
||
|
|
||
|
Suppose you have a symtab_node *ptr, AKA symtab_node *ptr. You can test
|
||
|
whether it points to a 'derived' cgraph_node as follows.
|
||
|
|
||
|
if (is_a <cgraph_node *> (ptr))
|
||
|
....
|
||
|
|
||
|
|
||
|
TYPE as_a <TYPE> (pointer)
|
||
|
|
||
|
Converts pointer to a TYPE.
|
||
|
|
||
|
You can just assume that it is such a node.
|
||
|
|
||
|
do_something_with (as_a <cgraph_node *> *ptr);
|
||
|
|
||
|
TYPE safe_as_a <TYPE> (pointer)
|
||
|
|
||
|
Like as_a <TYPE> (pointer), but where pointer could be NULL. This
|
||
|
adds a check against NULL where the regular is_a_helper hook for TYPE
|
||
|
assumes non-NULL.
|
||
|
|
||
|
do_something_with (safe_as_a <cgraph_node *> *ptr);
|
||
|
|
||
|
TYPE dyn_cast <TYPE> (pointer)
|
||
|
|
||
|
Converts pointer to TYPE if and only if "is_a <TYPE> pointer". Otherwise,
|
||
|
returns NULL. This function is essentially a checked down cast.
|
||
|
|
||
|
This functions reduce compile time and increase type safety when treating a
|
||
|
generic item as a more specific item.
|
||
|
|
||
|
You can test and obtain a pointer to the 'derived' type in one indivisible
|
||
|
operation.
|
||
|
|
||
|
if (cgraph_node *cptr = dyn_cast <cgraph_node *> (ptr))
|
||
|
....
|
||
|
|
||
|
As an example, the code change is from
|
||
|
|
||
|
if (symtab_function_p (node))
|
||
|
{
|
||
|
struct cgraph_node *cnode = cgraph (node);
|
||
|
....
|
||
|
}
|
||
|
|
||
|
to
|
||
|
|
||
|
if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
|
||
|
{
|
||
|
....
|
||
|
}
|
||
|
|
||
|
The necessary conditional test defines a variable that holds a known good
|
||
|
pointer to the specific item and avoids subsequent conversion calls and
|
||
|
the assertion checks that may come with them.
|
||
|
|
||
|
When, the property test is embedded within a larger condition, the
|
||
|
variable declaration gets pulled out of the condition. (This approach
|
||
|
leaves some room for using the variable inappropriately.)
|
||
|
|
||
|
if (symtab_variable_p (node) && varpool (node)->finalized)
|
||
|
varpool_analyze_node (varpool (node));
|
||
|
|
||
|
becomes
|
||
|
|
||
|
varpool_node *vnode = dyn_cast <varpool_node *> (node);
|
||
|
if (vnode && vnode->finalized)
|
||
|
varpool_analyze_node (vnode);
|
||
|
|
||
|
Note that we have converted two sets of assertions in the calls to varpool
|
||
|
into safe and efficient use of a variable.
|
||
|
|
||
|
TYPE safe_dyn_cast <TYPE> (pointer)
|
||
|
|
||
|
Like dyn_cast <TYPE> (pointer), except that it accepts null pointers
|
||
|
and returns null results for them.
|
||
|
|
||
|
|
||
|
If you use these functions and get a 'inline function not defined' or a
|
||
|
'missing symbol' error message for 'is_a_helper<....>::test', it means that
|
||
|
the connection between the types has not been made. See below.
|
||
|
|
||
|
|
||
|
EXTENDING THE GENERIC TYPE FACILITY
|
||
|
|
||
|
Method 1
|
||
|
--------
|
||
|
|
||
|
If DERIVED is derived from BASE, and if BASE contains enough information
|
||
|
to determine whether an object is actually an instance of DERIVED,
|
||
|
then you can make the above routines work for DERIVED by defining
|
||
|
a specialization of is_a_helper such as:
|
||
|
|
||
|
template<>
|
||
|
struct is_a_helper<DERIVED *> : static_is_a_helper<DERIVED *>
|
||
|
{
|
||
|
static inline bool test (const BASE *p) { return ...; }
|
||
|
};
|
||
|
|
||
|
This test function should return true if P is an instanced of DERIVED.
|
||
|
This on its own is enough; the comments below for method 2 do not apply.
|
||
|
|
||
|
Method 2
|
||
|
--------
|
||
|
|
||
|
Alternatively, if two types are connected in ways other than C++
|
||
|
inheritance, each connection between them must be made by defining a
|
||
|
specialization of the template member function 'test' of the template
|
||
|
class 'is_a_helper'. For example,
|
||
|
|
||
|
template <>
|
||
|
template <>
|
||
|
inline bool
|
||
|
is_a_helper <cgraph_node *>::test (symtab_node *p)
|
||
|
{
|
||
|
return p->type == SYMTAB_FUNCTION;
|
||
|
}
|
||
|
|
||
|
If a simple reinterpret_cast between the pointer types is incorrect, then you
|
||
|
must also specialize the template member function 'cast'. Failure to do so
|
||
|
when needed may result in a crash. For example,
|
||
|
|
||
|
template <>
|
||
|
template <>
|
||
|
inline bool
|
||
|
is_a_helper <cgraph_node *>::cast (symtab_node *p)
|
||
|
{
|
||
|
return &p->x_function;
|
||
|
}
|
||
|
|
||
|
*/
|
||
|
|
||
|
#ifndef GCC_IS_A_H
|
||
|
#define GCC_IS_A_H
|
||
|
|
||
|
/* A base class that specializations of is_a_helper can use if casting
|
||
|
U * to T is simply a reinterpret_cast. */
|
||
|
|
||
|
template <typename T>
|
||
|
struct reinterpret_is_a_helper
|
||
|
{
|
||
|
template <typename U>
|
||
|
static inline T cast (U *p) { return reinterpret_cast <T> (p); }
|
||
|
};
|
||
|
|
||
|
/* A base class that specializations of is_a_helper can use if casting
|
||
|
U * to T is simply a static_cast. This is more type-safe than
|
||
|
reinterpret_is_a_helper. */
|
||
|
|
||
|
template <typename T>
|
||
|
struct static_is_a_helper
|
||
|
{
|
||
|
template <typename U>
|
||
|
static inline T cast (U *p) { return static_cast <T> (p); }
|
||
|
};
|
||
|
|
||
|
/* A generic type conversion internal helper class. */
|
||
|
|
||
|
template <typename T>
|
||
|
struct is_a_helper : reinterpret_is_a_helper<T>
|
||
|
{
|
||
|
template <typename U>
|
||
|
static inline bool test (U *p);
|
||
|
};
|
||
|
|
||
|
/* Reuse the definition of is_a_helper<T *> to implement
|
||
|
is_a_helper<const T *>. */
|
||
|
|
||
|
template <typename T>
|
||
|
struct is_a_helper<const T *>
|
||
|
{
|
||
|
template <typename U>
|
||
|
static inline const T *cast (const U *p)
|
||
|
{
|
||
|
return is_a_helper<T *>::cast (const_cast <U *> (p));
|
||
|
}
|
||
|
template <typename U>
|
||
|
static inline bool test (const U *p)
|
||
|
{
|
||
|
return is_a_helper<T *>::test (p);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/* Note that we deliberately do not define the 'test' member template. Not
|
||
|
doing so will result in a build-time error for type relationships that have
|
||
|
not been defined, rather than a run-time error. See the discussion above
|
||
|
for when to define this member. */
|
||
|
|
||
|
/* The public interface. */
|
||
|
|
||
|
/* A generic test for a type relationship. See the discussion above for when
|
||
|
to use this function. The question answered is "Is type T a derived type of
|
||
|
type U?". */
|
||
|
|
||
|
template <typename T, typename U>
|
||
|
inline bool
|
||
|
is_a (U *p)
|
||
|
{
|
||
|
return is_a_helper<T>::test (p);
|
||
|
}
|
||
|
|
||
|
/* A generic conversion from a base type U to a derived type T. See the
|
||
|
discussion above for when to use this function. */
|
||
|
|
||
|
template <typename T, typename U>
|
||
|
inline T
|
||
|
as_a (U *p)
|
||
|
{
|
||
|
gcc_checking_assert (is_a <T> (p));
|
||
|
return is_a_helper <T>::cast (p);
|
||
|
}
|
||
|
|
||
|
/* Similar to as_a<>, but where the pointer can be NULL, even if
|
||
|
is_a_helper<T> doesn't check for NULL. */
|
||
|
|
||
|
template <typename T, typename U>
|
||
|
inline T
|
||
|
safe_as_a (U *p)
|
||
|
{
|
||
|
if (p)
|
||
|
{
|
||
|
gcc_checking_assert (is_a <T> (p));
|
||
|
return is_a_helper <T>::cast (p);
|
||
|
}
|
||
|
else
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* A generic checked conversion from a base type U to a derived type T. See
|
||
|
the discussion above for when to use this function. */
|
||
|
|
||
|
template <typename T, typename U>
|
||
|
inline T
|
||
|
dyn_cast (U *p)
|
||
|
{
|
||
|
if (is_a <T> (p))
|
||
|
return is_a_helper <T>::cast (p);
|
||
|
else
|
||
|
return static_cast <T> (0);
|
||
|
}
|
||
|
|
||
|
/* Similar to dyn_cast, except that the pointer may be null. */
|
||
|
|
||
|
template <typename T, typename U>
|
||
|
inline T
|
||
|
safe_dyn_cast (U *p)
|
||
|
{
|
||
|
return p ? dyn_cast <T> (p) : 0;
|
||
|
}
|
||
|
|
||
|
#endif /* GCC_IS_A_H */
|