Home | History | Annotate | Line # | Download | only in gcc
      1 /* Dynamic testing for abstract is-a relationships.
      2    Copyright (C) 2012-2024 Free Software Foundation, Inc.
      3    Contributed by Lawrence Crowl.
      4 
      5 This file is part of GCC.
      6 
      7 GCC is free software; you can redistribute it and/or modify it under
      8 the terms of the GNU General Public License as published by the Free
      9 Software Foundation; either version 3, or (at your option) any later
     10 version.
     11 
     12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15 for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with GCC; see the file COPYING3.  If not see
     19 <http://www.gnu.org/licenses/>.  */
     20 
     21 
     22 /* This header generic type query and conversion functions.
     23 
     24 
     25 USING THE GENERIC TYPE FACILITY
     26 
     27 
     28 The user functions are:
     29 
     30 bool is_a <TYPE> (pointer)
     31 
     32     Tests whether the pointer actually points to a more derived TYPE.
     33 
     34     Suppose you have a symtab_node *ptr, AKA symtab_node *ptr.  You can test
     35     whether it points to a 'derived' cgraph_node as follows.
     36 
     37       if (is_a <cgraph_node *> (ptr))
     38         ....
     39 
     40 
     41 TYPE as_a <TYPE> (pointer)
     42 
     43     Converts pointer to a TYPE.
     44 
     45     You can just assume that it is such a node.
     46 
     47       do_something_with (as_a <cgraph_node *> *ptr);
     48 
     49 TYPE safe_as_a <TYPE> (pointer)
     50 
     51     Like as_a <TYPE> (pointer), but where pointer could be NULL.  This
     52     adds a check against NULL where the regular is_a_helper hook for TYPE
     53     assumes non-NULL.
     54 
     55       do_something_with (safe_as_a <cgraph_node *> *ptr);
     56 
     57 TYPE dyn_cast <TYPE> (pointer)
     58 
     59     Converts pointer to TYPE if and only if "is_a <TYPE> pointer".  Otherwise,
     60     returns NULL.  This function is essentially a checked down cast.
     61 
     62     This functions reduce compile time and increase type safety when treating a
     63     generic item as a more specific item.
     64 
     65     You can test and obtain a pointer to the 'derived' type in one indivisible
     66     operation.
     67 
     68       if (cgraph_node *cptr = dyn_cast <cgraph_node *> (ptr))
     69         ....
     70 
     71     As an example, the code change is from
     72 
     73       if (symtab_function_p (node))
     74         {
     75           struct cgraph_node *cnode = cgraph (node);
     76           ....
     77         }
     78 
     79     to
     80 
     81       if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
     82         {
     83           ....
     84         }
     85 
     86     The necessary conditional test defines a variable that holds a known good
     87     pointer to the specific item and avoids subsequent conversion calls and
     88     the assertion checks that may come with them.
     89 
     90     When, the property test is embedded within a larger condition, the
     91     variable declaration gets pulled out of the condition.  (This approach
     92     leaves some room for using the variable inappropriately.)
     93 
     94       if (symtab_variable_p (node) && varpool (node)->finalized)
     95         varpool_analyze_node (varpool (node));
     96 
     97     becomes
     98 
     99       varpool_node *vnode = dyn_cast <varpool_node *> (node);
    100       if (vnode && vnode->finalized)
    101         varpool_analyze_node (vnode);
    102 
    103     Note that we have converted two sets of assertions in the calls to varpool
    104     into safe and efficient use of a variable.
    105 
    106 TYPE safe_dyn_cast <TYPE> (pointer)
    107 
    108     Like dyn_cast <TYPE> (pointer), except that it accepts null pointers
    109     and returns null results for them.
    110 
    111 
    112 If you use these functions and get a 'inline function not defined' or a
    113 'missing symbol' error message for 'is_a_helper<....>::test', it means that
    114 the connection between the types has not been made.  See below.
    115 
    116 
    117 EXTENDING THE GENERIC TYPE FACILITY
    118 
    119 Method 1
    120 --------
    121 
    122 If DERIVED is derived from BASE, and if BASE contains enough information
    123 to determine whether an object is actually an instance of DERIVED,
    124 then you can make the above routines work for DERIVED by defining
    125 a specialization of is_a_helper such as:
    126 
    127   template<>
    128   struct is_a_helper<DERIVED *> : static_is_a_helper<DERIVED *>
    129   {
    130     static inline bool test (const BASE *p) { return ...; }
    131   };
    132 
    133 This test function should return true if P is an instanced of DERIVED.
    134 This on its own is enough; the comments below for method 2 do not apply.
    135 
    136 Method 2
    137 --------
    138 
    139 Alternatively, if two types are connected in ways other than C++
    140 inheritance, each connection between them must be made by defining a
    141 specialization of the template member function 'test' of the template
    142 class 'is_a_helper'.  For example,
    143 
    144   template <>
    145   template <>
    146   inline bool
    147   is_a_helper <cgraph_node *>::test (symtab_node *p)
    148   {
    149     return p->type == SYMTAB_FUNCTION;
    150   }
    151 
    152 If a simple reinterpret_cast between the pointer types is incorrect, then you
    153 must also specialize the template member function 'cast'.  Failure to do so
    154 when needed may result in a crash.  For example,
    155 
    156   template <>
    157   template <>
    158   inline bool
    159   is_a_helper <cgraph_node *>::cast (symtab_node *p)
    160   {
    161     return &p->x_function;
    162   }
    163 
    164 */
    165 
    166 #ifndef GCC_IS_A_H
    167 #define GCC_IS_A_H
    168 
    169 /* A base class that specializations of is_a_helper can use if casting
    170    U * to T is simply a reinterpret_cast.  */
    171 
    172 template <typename T>
    173 struct reinterpret_is_a_helper
    174 {
    175   template <typename U>
    176   static inline T cast (U *p) { return reinterpret_cast <T> (p); }
    177 };
    178 
    179 /* A base class that specializations of is_a_helper can use if casting
    180    U * to T is simply a static_cast.  This is more type-safe than
    181    reinterpret_is_a_helper.  */
    182 
    183 template <typename T>
    184 struct static_is_a_helper
    185 {
    186   template <typename U>
    187   static inline T cast (U *p) { return static_cast <T> (p); }
    188 };
    189 
    190 /* A generic type conversion internal helper class.  */
    191 
    192 template <typename T>
    193 struct is_a_helper : reinterpret_is_a_helper<T>
    194 {
    195   template <typename U>
    196   static inline bool test (U *p);
    197 };
    198 
    199 /* Reuse the definition of is_a_helper<T *> to implement
    200    is_a_helper<const T *>.  */
    201 
    202 template <typename T>
    203 struct is_a_helper<const T *>
    204 {
    205   template <typename U>
    206   static inline const T *cast (const U *p)
    207   {
    208     return is_a_helper<T *>::cast (const_cast <U *> (p));
    209   }
    210   template <typename U>
    211   static inline bool test (const U *p)
    212   {
    213     return is_a_helper<T *>::test (p);
    214   }
    215 };
    216 
    217 /* Note that we deliberately do not define the 'test' member template.  Not
    218    doing so will result in a build-time error for type relationships that have
    219    not been defined, rather than a run-time error.  See the discussion above
    220    for when to define this member.  */
    221 
    222 /* The public interface.  */
    223 
    224 /* A generic test for a type relationship.  See the discussion above for when
    225    to use this function.  The question answered is "Is type T a derived type of
    226    type U?".  */
    227 
    228 template <typename T, typename U>
    229 inline bool
    230 is_a (U *p)
    231 {
    232   return is_a_helper<T>::test (p);
    233 }
    234 
    235 /* Similar to is_a<>, but where the pointer can be NULL, even if
    236    is_a_helper<T> doesn't check for NULL.  */
    237 
    238 template <typename T, typename U>
    239 inline bool
    240 safe_is_a (U *p)
    241 {
    242   if (p)
    243     return is_a_helper <T>::test (p);
    244   else
    245     return false;
    246 }
    247 
    248 /* A generic conversion from a base type U to a derived type T.  See the
    249    discussion above for when to use this function.  */
    250 
    251 template <typename T, typename U>
    252 inline T
    253 as_a (U *p)
    254 {
    255   gcc_checking_assert (is_a <T> (p));
    256   return is_a_helper <T>::cast (p);
    257 }
    258 
    259 /* Similar to as_a<>, but where the pointer can be NULL, even if
    260    is_a_helper<T> doesn't check for NULL.  */
    261 
    262 template <typename T, typename U>
    263 inline T
    264 safe_as_a (U *p)
    265 {
    266   if (p)
    267     {
    268       gcc_checking_assert (is_a <T> (p));
    269       return is_a_helper <T>::cast (p);
    270     }
    271   else
    272     return NULL;
    273 }
    274 
    275 /* A generic checked conversion from a base type U to a derived type T.  See
    276    the discussion above for when to use this function.  */
    277 
    278 template <typename T, typename U>
    279 inline T
    280 dyn_cast (U *p)
    281 {
    282   if (is_a <T> (p))
    283     return is_a_helper <T>::cast (p);
    284   else
    285     return static_cast <T> (0);
    286 }
    287 
    288 /* Similar to dyn_cast, except that the pointer may be null.  */
    289 
    290 template <typename T, typename U>
    291 inline T
    292 safe_dyn_cast (U *p)
    293 {
    294   return p ? dyn_cast <T> (p) : 0;
    295 }
    296 
    297 #endif  /* GCC_IS_A_H  */
    298