ASTTypeTraits.h revision ff9a01000ff74a994aa3da26ea2ec732c97291b7
1//===--- ASTTypeTraits.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_AST_TYPE_TRAITS_H
16#define LLVM_CLANG_AST_AST_TYPE_TRAITS_H
17
18#include "clang/AST/Decl.h"
19#include "clang/AST/Stmt.h"
20#include "clang/AST/TypeLoc.h"
21#include "llvm/Support/AlignOf.h"
22
23namespace clang {
24namespace ast_type_traits {
25
26/// \brief A dynamically typed AST node container.
27///
28/// Stores an AST node in a type safe way. This allows writing code that
29/// works with different kinds of AST nodes, despite the fact that they don't
30/// have a common base class.
31///
32/// Use \c create(Node) to create a \c DynTypedNode from an AST node,
33/// and \c get<T>() to retrieve the node as type T if the types match.
34///
35/// See \c NodeTypeTag for which node base types are currently supported;
36/// You can create DynTypedNodes for all nodes in the inheritance hierarchy of
37/// the supported base types.
38class DynTypedNode {
39public:
40  /// \brief Creates a \c DynTypedNode from \c Node.
41  template <typename T>
42  static DynTypedNode create(const T &Node) {
43    return BaseConverter<T>::create(Node);
44  }
45
46  /// \brief Retrieve the stored node as type \c T.
47  ///
48  /// Returns NULL if the stored node does not have a type that is
49  /// convertible to \c T.
50  ///
51  /// For types that have identity via their pointer in the AST
52  /// (like \c Stmt and \c Decl) the returned pointer points to the
53  /// referenced AST node.
54  /// For other types (like \c QualType) the value is stored directly
55  /// in the \c DynTypedNode, and the returned pointer points at
56  /// the storage inside DynTypedNode. For those nodes, do not
57  /// use the pointer outside the scope of the DynTypedNode.
58  template <typename T>
59  const T *get() const {
60    return BaseConverter<T>::get(Tag, Storage.buffer);
61  }
62
63  /// \brief Returns a pointer that identifies the stored AST node.
64  ///
65  /// Note that this is not supported by all AST nodes. For AST nodes
66  /// that don't have a pointer-defined identity inside the AST, this
67  /// method returns NULL.
68  const void *getMemoizationData() const;
69
70private:
71  /// \brief Takes care of converting from and to \c T.
72  template <typename T, typename EnablerT = void> struct BaseConverter;
73
74  /// \brief Supported base node types.
75  enum NodeTypeTag {
76    NT_Decl,
77    NT_Stmt,
78    NT_NestedNameSpecifier,
79    NT_NestedNameSpecifierLoc,
80    NT_QualType,
81    NT_Type,
82    NT_TypeLoc
83  } Tag;
84
85  /// \brief Stores the data of the node.
86  ///
87  /// Note that we can store \c Decls and \c Stmts by pointer as they are
88  /// guaranteed to be unique pointers pointing to dedicated storage in the
89  /// AST. \c QualTypes on the other hand do not have storage or unique
90  /// pointers and thus need to be stored by value.
91  llvm::AlignedCharArrayUnion<Decl *, Stmt *, NestedNameSpecifier,
92                              NestedNameSpecifierLoc, QualType, Type,
93                              TypeLoc> Storage;
94};
95
96// FIXME: Pull out abstraction for the following.
97template<typename T> struct DynTypedNode::BaseConverter<T,
98    typename llvm::enable_if<llvm::is_base_of<Decl, T> >::type> {
99  static const T *get(NodeTypeTag Tag, const char Storage[]) {
100    if (Tag == NT_Decl)
101      return dyn_cast<T>(*reinterpret_cast<Decl*const*>(Storage));
102    return NULL;
103  }
104  static DynTypedNode create(const Decl &Node) {
105    DynTypedNode Result;
106    Result.Tag = NT_Decl;
107    new (Result.Storage.buffer) const Decl*(&Node);
108    return Result;
109  }
110};
111template<typename T> struct DynTypedNode::BaseConverter<T,
112    typename llvm::enable_if<llvm::is_base_of<Stmt, T> >::type> {
113  static const T *get(NodeTypeTag Tag, const char Storage[]) {
114    if (Tag == NT_Stmt)
115      return dyn_cast<T>(*reinterpret_cast<Stmt*const*>(Storage));
116    return NULL;
117  }
118  static DynTypedNode create(const Stmt &Node) {
119    DynTypedNode Result;
120    Result.Tag = NT_Stmt;
121    new (Result.Storage.buffer) const Stmt*(&Node);
122    return Result;
123  }
124};
125template<typename T> struct DynTypedNode::BaseConverter<T,
126    typename llvm::enable_if<llvm::is_base_of<Type, T> >::type> {
127  static const T *get(NodeTypeTag Tag, const char Storage[]) {
128    if (Tag == NT_Type)
129      return dyn_cast<T>(*reinterpret_cast<Type*const*>(Storage));
130    return NULL;
131  }
132  static DynTypedNode create(const Type &Node) {
133    DynTypedNode Result;
134    Result.Tag = NT_Type;
135    new (Result.Storage.buffer) const Type*(&Node);
136    return Result;
137  }
138};
139template<> struct DynTypedNode::BaseConverter<NestedNameSpecifier, void> {
140  static const NestedNameSpecifier *get(NodeTypeTag Tag, const char Storage[]) {
141    if (Tag == NT_NestedNameSpecifier)
142      return *reinterpret_cast<NestedNameSpecifier*const*>(Storage);
143    return NULL;
144  }
145  static DynTypedNode create(const NestedNameSpecifier &Node) {
146    DynTypedNode Result;
147    Result.Tag = NT_NestedNameSpecifier;
148    new (Result.Storage.buffer) const NestedNameSpecifier*(&Node);
149    return Result;
150  }
151};
152template<> struct DynTypedNode::BaseConverter<NestedNameSpecifierLoc, void> {
153  static const NestedNameSpecifierLoc *get(NodeTypeTag Tag,
154                                           const char Storage[]) {
155    if (Tag == NT_NestedNameSpecifierLoc)
156      return reinterpret_cast<const NestedNameSpecifierLoc*>(Storage);
157    return NULL;
158  }
159  static DynTypedNode create(const NestedNameSpecifierLoc &Node) {
160    DynTypedNode Result;
161    Result.Tag = NT_NestedNameSpecifierLoc;
162    new (Result.Storage.buffer) NestedNameSpecifierLoc(Node);
163    return Result;
164  }
165};
166template<> struct DynTypedNode::BaseConverter<QualType, void> {
167  static const QualType *get(NodeTypeTag Tag, const char Storage[]) {
168    if (Tag == NT_QualType)
169      return reinterpret_cast<const QualType*>(Storage);
170    return NULL;
171  }
172  static DynTypedNode create(const QualType &Node) {
173    DynTypedNode Result;
174    Result.Tag = NT_QualType;
175    new (Result.Storage.buffer) QualType(Node);
176    return Result;
177  }
178};
179template<> struct DynTypedNode::BaseConverter<TypeLoc, void> {
180  static const TypeLoc *get(NodeTypeTag Tag, const char Storage[]) {
181    if (Tag == NT_TypeLoc)
182      return reinterpret_cast<const TypeLoc*>(Storage);
183    return NULL;
184  }
185  static DynTypedNode create(const TypeLoc &Node) {
186    DynTypedNode Result;
187    Result.Tag = NT_TypeLoc;
188    new (Result.Storage.buffer) TypeLoc(Node);
189    return Result;
190  }
191};
192// The only operation we allow on unsupported types is \c get.
193// This allows to conveniently use \c DynTypedNode when having an arbitrary
194// AST node that is not supported, but prevents misuse - a user cannot create
195// a DynTypedNode from arbitrary types.
196template <typename T, typename EnablerT> struct DynTypedNode::BaseConverter {
197  static const T *get(NodeTypeTag Tag, const char Storage[]) { return NULL; }
198};
199
200inline const void *DynTypedNode::getMemoizationData() const {
201  switch (Tag) {
202    case NT_Decl: return BaseConverter<Decl>::get(Tag, Storage.buffer);
203    case NT_Stmt: return BaseConverter<Stmt>::get(Tag, Storage.buffer);
204    default: return NULL;
205  };
206}
207
208} // end namespace ast_type_traits
209} // end namespace clang
210
211#endif // LLVM_CLANG_AST_AST_TYPE_TRAITS_H
212