Line data Source code
1 : /* Dynamic testing for abstract is-a relationships.
2 : Copyright (C) 2012-2026 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 501450713 : static inline bool test (const U *p)
212 : {
213 501450713 : 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 >25680*10^7 : is_a (U *p)
231 : {
232 >47604*10^7 : 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 198639011 : safe_is_a (U *p)
241 : {
242 151864243 : if (p)
243 102102342 : 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 37921130579 : as_a (U *p)
254 : {
255 37821223441 : gcc_checking_assert (is_a <T> (p));
256 37921130579 : 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 >18710*10^7 : safe_as_a (U *p)
265 : {
266 >18710*10^7 : if (p)
267 : {
268 >17821*10^7 : 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 >26765*10^7 : dyn_cast (U *p)
281 : {
282 >30588*10^7 : if (is_a <T> (p))
283 : return is_a_helper <T>::cast (p);
284 : else
285 1703234392 : 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 1905158509 : safe_dyn_cast (U *p)
293 : {
294 5749060879 : return p ? dyn_cast <T> (p) : 0;
295 : }
296 :
297 : #endif /* GCC_IS_A_H */
|