1//===--- ASTMatchersTypeTraits.h --------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  Provides a dynamically typed node container that can be used to store
11//  an AST base node at runtime in the same storage in a type safe way.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_CLANG_AST_MATCHERS_AST_TYPE_TRAITS_H
16#define LLVM_CLANG_AST_MATCHERS_AST_TYPE_TRAITS_H
17
18#include "clang/AST/Decl.h"
19#include "clang/AST/Stmt.h"
20#include "llvm/Support/AlignOf.h"
21
22namespace clang {
23namespace ast_type_traits {
24
25/// \brief A dynamically typed AST node container.
26///
27/// Stores an AST node in a type safe way. This allows writing code that
28/// works with different kinds of AST nodes, despite the fact that they don't
29/// have a common base class.
30///
31/// Use \c create(Node) to create a \c DynTypedNode from an AST node,
32/// and \c get<T>() to retrieve the node as type T if the types match.
33///
34/// See \c NodeTypeTag for which node base types are currently supported;
35/// You can create DynTypedNodes for all nodes in the inheritance hierarchy of
36/// the supported base types.
37class DynTypedNode {
38public:
39  /// \brief Creates a \c DynTypedNode from \c Node.
40  template <typename T>
41  static DynTypedNode create(const T &Node) {
42    return BaseConverter<T>::create(Node);
43  }
44
45  /// \brief Retrieve the stored node as type \c T.
46  ///
47  /// Returns NULL if the stored node does not have a type that is
48  /// convertible to \c T.
49  ///
50  /// For types that have identity via their pointer in the AST
51  /// (like \c Stmt and \c Decl) the returned pointer points to the
52  /// referenced AST node.
53  /// For other types (like \c QualType) the value is stored directly
54  /// in the \c DynTypedNode, and the returned pointer points at
55  /// the storage inside DynTypedNode. For those nodes, do not
56  /// use the pointer outside the scope of the DynTypedNode.
57  template <typename T>
58  const T *get() const {
59    return BaseConverter<T>::get(Tag, Storage.buffer);
60  }
61
62  /// \brief Returns a pointer that identifies the stored AST node.
63  ///
64  /// Note that this is not supported by all AST nodes. For AST nodes
65  /// that don't have a pointer-defined identity inside the AST, this
66  /// method returns NULL.
67  const void *getMemoizationData() const;
68
69private:
70  /// \brief Takes care of converting from and to \c T.
71  template <typename T, typename EnablerT = void> struct BaseConverter;
72
73  /// \brief Supported base node types.
74  enum NodeTypeTag {
75    NT_Decl,
76    NT_Stmt,
77    NT_QualType
78  } Tag;
79
80  /// \brief Stores the data of the node.
81  ///
82  /// Note that we can store \c Decls and \c Stmts by pointer as they are
83  /// guaranteed to be unique pointers pointing to dedicated storage in the
84  /// AST. \c QualTypes on the other hand do not have storage or unique
85  /// pointers and thus need to be stored by value.
86  llvm::AlignedCharArrayUnion<Decl*, Stmt*, QualType> Storage;
87};
88template<typename T> struct DynTypedNode::BaseConverter<T,
89    typename llvm::enable_if<llvm::is_base_of<Decl, T> >::type> {
90  static const T *get(NodeTypeTag Tag, const char Storage[]) {
91    if (Tag == NT_Decl)
92      return dyn_cast<T>(*reinterpret_cast<Decl*const*>(Storage));
93    return NULL;
94  }
95  static DynTypedNode create(const Decl &Node) {
96    DynTypedNode Result;
97    Result.Tag = NT_Decl;
98    new (Result.Storage.buffer) const Decl*(&Node);
99    return Result;
100  }
101};
102template<typename T> struct DynTypedNode::BaseConverter<T,
103    typename llvm::enable_if<llvm::is_base_of<Stmt, T> >::type> {
104  static const T *get(NodeTypeTag Tag, const char Storage[]) {
105    if (Tag == NT_Stmt)
106      return dyn_cast<T>(*reinterpret_cast<Stmt*const*>(Storage));
107    return NULL;
108  }
109  static DynTypedNode create(const Stmt &Node) {
110    DynTypedNode Result;
111    Result.Tag = NT_Stmt;
112    new (Result.Storage.buffer) const Stmt*(&Node);
113    return Result;
114  }
115};
116template<> struct DynTypedNode::BaseConverter<QualType, void> {
117  static const QualType *get(NodeTypeTag Tag, const char Storage[]) {
118    if (Tag == NT_QualType)
119      return reinterpret_cast<const QualType*>(Storage);
120    return NULL;
121  }
122  static DynTypedNode create(const QualType &Node) {
123    DynTypedNode Result;
124    Result.Tag = NT_QualType;
125    new (Result.Storage.buffer) QualType(Node);
126    return Result;
127  }
128};
129// The only operation we allow on unsupported types is \c get.
130// This allows to conveniently use \c DynTypedNode when having an arbitrary
131// AST node that is not supported, but prevents misuse - a user cannot create
132// a DynTypedNode from arbitrary types.
133template <typename T, typename EnablerT> struct DynTypedNode::BaseConverter {
134  static const T *get(NodeTypeTag Tag, const char Storage[]) { return NULL; }
135};
136
137inline const void *DynTypedNode::getMemoizationData() const {
138  switch (Tag) {
139    case NT_Decl: return BaseConverter<Decl>::get(Tag, Storage.buffer);
140    case NT_Stmt: return BaseConverter<Stmt>::get(Tag, Storage.buffer);
141    default: return NULL;
142  };
143}
144
145} // end namespace ast_type_traits
146} // end namespace clang
147
148#endif // LLVM_CLANG_AST_MATCHERS_AST_TYPE_TRAITS_H
149
150