aboutsummaryrefslogtreecommitdiff
path: root/gcc/is-a.h
blob: cc5cd71910ee26b0c309c45cc829a548f3f92f63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/* Dynamic testing for abstract is-a relationships.
   Copyright (C) 2012-2021 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  */