Line data Source code
1 : /* Dynamic testing for abstract is-a relationships. 2 : Copyright (C) 2012-2023 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 115460952 : is_a (U *p) 231 : { 232 135244790 : 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 1156037764 : dyn_cast (U *p) 281 : { 282 184921123 : if (is_a <T> (p)) 283 : return is_a_helper <T>::cast (p); 284 : else 285 111139870 : 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 */