]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/is-a.h
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / is-a.h
CommitLineData
5d59b5e1 1/* Dynamic testing for abstract is-a relationships.
99dee823 2 Copyright (C) 2012-2021 Free Software Foundation, Inc.
5d59b5e1
LC
3 Contributed by Lawrence Crowl.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
25USING THE GENERIC TYPE FACILITY
26
27
28The user functions are:
29
30bool is_a <TYPE> (pointer)
31
32 Tests whether the pointer actually points to a more derived TYPE.
33
5e20cdc9 34 Suppose you have a symtab_node *ptr, AKA symtab_node *ptr. You can test
5d59b5e1
LC
35 whether it points to a 'derived' cgraph_node as follows.
36
7de90a6c 37 if (is_a <cgraph_node *> (ptr))
5d59b5e1
LC
38 ....
39
40
7de90a6c 41TYPE as_a <TYPE> (pointer)
5d59b5e1 42
7de90a6c 43 Converts pointer to a TYPE.
5d59b5e1
LC
44
45 You can just assume that it is such a node.
46
7de90a6c 47 do_something_with (as_a <cgraph_node *> *ptr);
5d59b5e1 48
26b3538b
DM
49TYPE 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
7de90a6c 57TYPE dyn_cast <TYPE> (pointer)
5d59b5e1 58
7de90a6c 59 Converts pointer to TYPE if and only if "is_a <TYPE> pointer". Otherwise,
5d59b5e1
LC
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
7de90a6c 68 if (cgraph_node *cptr = dyn_cast <cgraph_node *> (ptr))
5d59b5e1
LC
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
7de90a6c 81 if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
5d59b5e1
LC
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
7de90a6c 99 varpool_node *vnode = dyn_cast <varpool_node *> (node);
5d59b5e1
LC
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
d305ca88
RS
106TYPE 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
5d59b5e1
LC
111
112If 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
114the connection between the types has not been made. See below.
115
116
117EXTENDING THE GENERIC TYPE FACILITY
118
1498b1a8
RS
119Method 1
120--------
121
122If DERIVED is derived from BASE, and if BASE contains enough information
123to determine whether an object is actually an instance of DERIVED,
124then you can make the above routines work for DERIVED by defining
125a 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
133This test function should return true if P is an instanced of DERIVED.
134This on its own is enough; the comments below for method 2 do not apply.
135
136Method 2
137--------
138
139Alternatively, if two types are connected in ways other than C++
140inheritance, each connection between them must be made by defining a
141specialization of the template member function 'test' of the template
142class 'is_a_helper'. For example,
5d59b5e1
LC
143
144 template <>
145 template <>
146 inline bool
7de90a6c 147 is_a_helper <cgraph_node *>::test (symtab_node *p)
5d59b5e1 148 {
67348ccc 149 return p->type == SYMTAB_FUNCTION;
5d59b5e1
LC
150 }
151
152If a simple reinterpret_cast between the pointer types is incorrect, then you
153must also specialize the template member function 'cast'. Failure to do so
154when needed may result in a crash. For example,
155
156 template <>
157 template <>
158 inline bool
7de90a6c 159 is_a_helper <cgraph_node *>::cast (symtab_node *p)
5d59b5e1
LC
160 {
161 return &p->x_function;
162 }
163
164*/
165
166#ifndef GCC_IS_A_H
167#define GCC_IS_A_H
168
1498b1a8
RS
169/* A base class that specializations of is_a_helper can use if casting
170 U * to T is simply a reinterpret_cast. */
171
172template <typename T>
173struct 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
183template <typename T>
184struct static_is_a_helper
185{
186 template <typename U>
187 static inline T cast (U *p) { return static_cast <T> (p); }
188};
189
5d59b5e1
LC
190/* A generic type conversion internal helper class. */
191
192template <typename T>
1498b1a8 193struct is_a_helper : reinterpret_is_a_helper<T>
5d59b5e1
LC
194{
195 template <typename U>
196 static inline bool test (U *p);
1498b1a8
RS
197};
198
199/* Reuse the definition of is_a_helper<T *> to implement
200 is_a_helper<const T *>. */
201
202template <typename T>
203struct 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 }
5d59b5e1 210 template <typename U>
1498b1a8
RS
211 static inline bool test (const U *p)
212 {
213 return is_a_helper<T *>::test (p);
214 }
5d59b5e1
LC
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
5d59b5e1
LC
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
228template <typename T, typename U>
229inline bool
230is_a (U *p)
231{
232 return is_a_helper<T>::test (p);
233}
234
235/* A generic conversion from a base type U to a derived type T. See the
236 discussion above for when to use this function. */
237
238template <typename T, typename U>
7de90a6c 239inline T
5d59b5e1
LC
240as_a (U *p)
241{
f046e81b 242 gcc_checking_assert (is_a <T> (p));
5d59b5e1
LC
243 return is_a_helper <T>::cast (p);
244}
245
26b3538b
DM
246/* Similar to as_a<>, but where the pointer can be NULL, even if
247 is_a_helper<T> doesn't check for NULL. */
248
249template <typename T, typename U>
250inline T
251safe_as_a (U *p)
252{
253 if (p)
254 {
255 gcc_checking_assert (is_a <T> (p));
256 return is_a_helper <T>::cast (p);
257 }
258 else
259 return NULL;
260}
261
5d59b5e1
LC
262/* A generic checked conversion from a base type U to a derived type T. See
263 the discussion above for when to use this function. */
264
265template <typename T, typename U>
7de90a6c 266inline T
5d59b5e1
LC
267dyn_cast (U *p)
268{
269 if (is_a <T> (p))
270 return is_a_helper <T>::cast (p);
271 else
7de90a6c 272 return static_cast <T> (0);
5d59b5e1
LC
273}
274
d305ca88
RS
275/* Similar to dyn_cast, except that the pointer may be null. */
276
277template <typename T, typename U>
278inline T
279safe_dyn_cast (U *p)
280{
281 return p ? dyn_cast <T> (p) : 0;
282}
283
5d59b5e1 284#endif /* GCC_IS_A_H */