GTIRB  v2.1.0
GrammaTech Intermediate Representation for Binaries: C++ API
Casting.hpp
Go to the documentation of this file.
1 //===- Casting.hpp ----------------------------------------------*- C++ -*-===//
2 //
3 // Copyright (C) 2020 GrammaTech, Inc.
4 //
5 // This code is licensed under the MIT license. See the LICENSE file in the
6 // project root for license terms.
7 //
8 // This project is sponsored by the Office of Naval Research, One Liberty
9 // Center, 875 N. Randolph Street, Arlington, VA 22203 under contract #
10 // N68335-17-C-0700. The content of the information does not necessarily
11 // reflect the position or policy of the Government and no official
12 // endorsement should be inferred.
13 //
14 //===-Addition License Information-----------------------------------------===//
15 //
16 // This file was initially written for the LLVM Compiler Infrastructure
17 // project where it is distributed under the University of Illinois Open Source
18 // License. See the LICENSE file in the project root for license terms.
19 //
20 //===----------------------------------------------------------------------===//
21 #ifndef GTIRB_CASTING_H
22 #define GTIRB_CASTING_H
23 
24 #include <cassert>
25 #include <type_traits>
26 
33 
158 
161 
164 
167 
170 
173 
176 
179 
182 
185 
188 
191 
194 
197 
198 //===----------------------------------------------------------------------===//
199 // isa<x> Support Templates
200 //===----------------------------------------------------------------------===//
201 
203 // Define a template that can be specialized by smart pointers to reflect the
204 // fact that they are automatically dereferenced, and are not involved with the
205 // template selection process... the default implementation is a noop.
206 //
207 template <typename From> struct simplify_type {
208  using SimpleType = From; // The real type this represents...
209 
210  // An accessor to get the real value...
211  static SimpleType& getSimplifiedValue(From& Val) { return Val; }
212 };
214 
216 // The core of the implementation of isa<X> is here; To and From should be
217 // the names of classes. This template can be specialized to customize the
218 // implementation of isa<> without rewriting it from scratch.
219 template <typename To, typename From, typename Enabler = void> struct isa_impl {
220  static inline bool doit(const From& Val) { return To::classof(&Val); }
221 };
223 
225 // Always allow upcasts, and perform no dynamic check for them.
226 template <typename To, typename From>
227 struct isa_impl<
228  To, From, typename std::enable_if<std::is_base_of<To, From>::value>::type> {
229  static inline bool doit(const From&) { return true; }
230 };
232 
234 template <typename To, typename From> struct isa_impl_cl {
235  static inline bool doit(const From& Val) {
236  return isa_impl<To, From>::doit(Val);
237  }
238 };
240 
242 template <typename To, typename From> struct isa_impl_cl<To, const From> {
243  static inline bool doit(const From& Val) {
244  return isa_impl<To, From>::doit(Val);
245  }
246 };
248 
250 template <typename To, typename From> struct isa_impl_cl<To, From*> {
251  static inline bool doit(const From* Val) {
252  assert(Val && "isa<> used on a null pointer");
253  return isa_impl<To, From>::doit(*Val);
254  }
255 };
257 
259 template <typename To, typename From> struct isa_impl_cl<To, From* const> {
260  static inline bool doit(const From* Val) {
261  assert(Val && "isa<> used on a null pointer");
262  return isa_impl<To, From>::doit(*Val);
263  }
264 };
266 
268 template <typename To, typename From> struct isa_impl_cl<To, const From*> {
269  static inline bool doit(const From* Val) {
270  assert(Val && "isa<> used on a null pointer");
271  return isa_impl<To, From>::doit(*Val);
272  }
273 };
275 
277 template <typename To, typename From>
278 struct isa_impl_cl<To, const From* const> {
279  static inline bool doit(const From* Val) {
280  assert(Val && "isa<> used on a null pointer");
281  return isa_impl<To, From>::doit(*Val);
282  }
283 };
285 
287 template <typename To, typename From, typename SimpleFrom>
288 struct isa_impl_wrap {
289  // When From != SimplifiedType, we can simplify the type some more by using
290  // the simplify_type template.
291  static bool doit(const From& Val) {
292  return isa_impl_wrap<To, SimpleFrom,
293  typename simplify_type<SimpleFrom>::SimpleType>::
294  doit(simplify_type<const From>::getSimplifiedValue(Val));
295  }
296 };
298 
300 template <typename To, typename FromTy>
301 struct isa_impl_wrap<To, FromTy, FromTy> {
302  // When From == SimpleType, we are as simple as we are going to get.
303  static bool doit(const FromTy& Val) {
304  return isa_impl_cl<To, FromTy>::doit(Val);
305  }
306 };
308 
309 // isa<X> - Return true if the parameter to the template is an instance of the
310 // template type argument. Used like this:
311 //
312 // if (isa<Type>(myVal)) { ... }
313 //
314 template <class X, class Y>[[nodiscard]] inline bool isa(const Y& Val) {
315  return isa_impl_wrap<X, const Y,
316  typename simplify_type<const Y>::SimpleType>::doit(Val);
317 }
318 
319 //===----------------------------------------------------------------------===//
320 // cast<x> Support Templates
321 //===----------------------------------------------------------------------===//
323 template <class To, class From> struct cast_retty;
325 
327 // Calculate what type the 'cast' function should return, based on a requested
328 // type of To and a source type of From.
329 template <class To, class From> struct cast_retty_impl {
330  using ret_type = To&; // Normal case, return Ty&
331 };
332 template <class To, class From> struct cast_retty_impl<To, const From> {
333  using ret_type = const To&; // Normal case, return Ty&
334 };
335 
336 template <class To, class From> struct cast_retty_impl<To, From*> {
337  using ret_type = To*; // Pointer arg case, return Ty*
338 };
339 
340 template <class To, class From> struct cast_retty_impl<To, const From*> {
341  using ret_type = const To*; // Constant pointer arg case, return const Ty*
342 };
343 
344 template <class To, class From> struct cast_retty_impl<To, const From* const> {
345  using ret_type = const To*; // Constant pointer arg case, return const Ty*
346 };
348 
350 template <class To, class From, class SimpleFrom> struct cast_retty_wrap {
351  // When the simplified type and the from type are not the same, use the type
352  // simplifier to reduce the type, then reuse cast_retty_impl to get the
353  // resultant type.
354  using ret_type = typename cast_retty<To, SimpleFrom>::ret_type;
355 };
356 
357 template <class To, class FromTy> struct cast_retty_wrap<To, FromTy, FromTy> {
358  // When the simplified type is equal to the from type, use it directly.
359  using ret_type = typename cast_retty_impl<To, FromTy>::ret_type;
360 };
361 
362 template <class To, class From> struct cast_retty {
363  using ret_type = typename cast_retty_wrap<
364  To, From, typename simplify_type<From>::SimpleType>::ret_type;
365 };
367 
369 // Ensure the non-simple values are converted using the simplify_type template
370 // that may be specialized by smart pointers...
371 //
372 template <class To, class From, class SimpleFrom> struct cast_convert_val {
373  // This is not a simple type, use the template to simplify it...
374  static typename cast_retty<To, From>::ret_type doit(From& Val) {
375  return cast_convert_val<To, SimpleFrom,
376  typename simplify_type<SimpleFrom>::SimpleType>::
377  doit(simplify_type<From>::getSimplifiedValue(Val));
378  }
379 };
380 
381 template <class To, class FromTy> struct cast_convert_val<To, FromTy, FromTy> {
382  // This _is_ a simple type, just cast it.
383  static typename cast_retty<To, FromTy>::ret_type doit(const FromTy& Val) {
384  typename cast_retty<To, FromTy>::ret_type Res2 =
385  (typename cast_retty<To, FromTy>::ret_type) const_cast<FromTy&>(Val);
386  return Res2;
387  }
388 };
390 
392 template <class X> struct is_simple_type {
393  static const bool value =
394  std::is_same<X, typename simplify_type<X>::SimpleType>::value;
395 };
397 
398 // cast<X> - Return the argument parameter cast to the specified type. This
399 // casting operator asserts that the type is correct, so it does not return null
400 // on failure. It does not allow a null argument (use cast_or_null for that).
401 // It is typically used like this:
402 //
403 // cast<Instruction>(myVal)->getParent()
404 //
405 template <class X, class Y>
406 inline typename std::enable_if<!is_simple_type<Y>::value,
407  typename cast_retty<X, const Y>::ret_type>::type
408 cast(const Y& Val) {
409  assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
410  return cast_convert_val<
411  X, const Y, typename simplify_type<const Y>::SimpleType>::doit(Val);
412 }
413 
414 template <class X, class Y>
415 inline typename cast_retty<X, Y>::ret_type cast(Y& Val) {
416  assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
417  return cast_convert_val<X, Y, typename simplify_type<Y>::SimpleType>::doit(
418  Val);
419 }
420 
421 template <class X, class Y>
422 inline typename cast_retty<X, Y*>::ret_type cast(Y* Val) {
423  assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
424  return cast_convert_val<X, Y*, typename simplify_type<Y*>::SimpleType>::doit(
425  Val);
426 }
427 
428 // cast_or_null<X> - Functionally identical to cast, except that a null value is
429 // accepted.
430 //
431 template <class X, class Y>
432 [[nodiscard]] inline
433  typename std::enable_if<!is_simple_type<Y>::value,
434  typename cast_retty<X, const Y>::ret_type>::type
435  cast_or_null(const Y& Val) {
436  if (!Val)
437  return nullptr;
438  assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
439  return cast<X>(Val);
440 }
441 
442 template <class X, class Y>
443 [[nodiscard]] inline
444  typename std::enable_if<!is_simple_type<Y>::value,
445  typename cast_retty<X, Y>::ret_type>::type
446  cast_or_null(Y& Val) {
447  if (!Val)
448  return nullptr;
449  assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
450  return cast<X>(Val);
451 }
452 
453 template <class X, class Y>
454 [[nodiscard]] inline typename cast_retty<X, Y*>::ret_type cast_or_null(Y* Val) {
455  if (!Val)
456  return nullptr;
457  assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
458  return cast<X>(Val);
459 }
460 
461 // dyn_cast<X> - Return the argument parameter cast to the specified type. This
462 // casting operator returns null if the argument is of the wrong type, so it can
463 // be used to test for a type as well as cast if successful. This should be
464 // used in the context of an if statement like this:
465 //
466 // if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... }
467 //
468 
469 template <class X, class Y>
470 [[nodiscard]] inline
471  typename std::enable_if<!is_simple_type<Y>::value,
472  typename cast_retty<X, const Y>::ret_type>::type
473  dyn_cast(const Y& Val) {
474  return isa<X>(Val) ? cast<X>(Val) : nullptr;
475 }
476 
477 template <class X, class Y>
478 [[nodiscard]] inline typename cast_retty<X, Y>::ret_type dyn_cast(Y& Val) {
479  return isa<X>(Val) ? cast<X>(Val) : nullptr;
480 }
481 
482 template <class X, class Y>
483 [[nodiscard]] inline typename cast_retty<X, Y*>::ret_type dyn_cast(Y* Val) {
484  return isa<X>(Val) ? cast<X>(Val) : nullptr;
485 }
486 
487 // dyn_cast_or_null<X> - Functionally identical to dyn_cast, except that a null
488 // value is accepted.
489 //
490 template <class X, class Y>
491 [[nodiscard]] inline
492  typename std::enable_if<!is_simple_type<Y>::value,
493  typename cast_retty<X, const Y>::ret_type>::type
494  dyn_cast_or_null(const Y& Val) {
495  return (Val && isa<X>(Val)) ? cast<X>(Val) : nullptr;
496 }
497 
498 template <class X, class Y>
499 [[nodiscard]] inline
500  typename std::enable_if<!is_simple_type<Y>::value,
501  typename cast_retty<X, Y>::ret_type>::type
503  return (Val && isa<X>(Val)) ? cast<X>(Val) : nullptr;
504 }
505 
506 template <class X, class Y>
507 [[nodiscard]] inline typename cast_retty<X, Y*>::ret_type
509  return (Val && isa<X>(Val)) ? cast<X>(Val) : nullptr;
510 }
511 
512 #endif // GTIRB_CASTING_H
isa
bool isa(const Y &Val)
Definition: Casting.hpp:314
dyn_cast
std::enable_if<!is_simple_type< Y >::value, typename cast_retty< X, const Y >::ret_type >::type dyn_cast(const Y &Val)
Definition: Casting.hpp:473
cast_or_null
std::enable_if<!is_simple_type< Y >::value, typename cast_retty< X, const Y >::ret_type >::type cast_or_null(const Y &Val)
Definition: Casting.hpp:435
cast
std::enable_if<!is_simple_type< Y >::value, typename cast_retty< X, const Y >::ret_type >::type cast(const Y &Val)
Definition: Casting.hpp:408
std
Definition: Addr.hpp:359
dyn_cast_or_null
std::enable_if<!is_simple_type< Y >::value, typename cast_retty< X, const Y >::ret_type >::type dyn_cast_or_null(const Y &Val)
Definition: Casting.hpp:494