aboutsummaryrefslogtreecommitdiff
path: root/include/clang/AST/ASTTypeTraits.h
blob: 4688b12de7016f0d5cd8d10587484b04547911b1 (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
//===--- ASTTypeTraits.h ----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  Provides a dynamically typed node container that can be used to store
//  an AST base node at runtime in the same storage in a type safe way.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_AST_TYPE_TRAITS_H
#define LLVM_CLANG_AST_AST_TYPE_TRAITS_H

#include "clang/AST/Decl.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TypeLoc.h"
#include "llvm/Support/AlignOf.h"

namespace clang {
namespace ast_type_traits {

/// \brief A dynamically typed AST node container.
///
/// Stores an AST node in a type safe way. This allows writing code that
/// works with different kinds of AST nodes, despite the fact that they don't
/// have a common base class.
///
/// Use \c create(Node) to create a \c DynTypedNode from an AST node,
/// and \c get<T>() to retrieve the node as type T if the types match.
///
/// See \c NodeTypeTag for which node base types are currently supported;
/// You can create DynTypedNodes for all nodes in the inheritance hierarchy of
/// the supported base types.
class DynTypedNode {
public:
  /// \brief Creates a \c DynTypedNode from \c Node.
  template <typename T>
  static DynTypedNode create(const T &Node) {
    return BaseConverter<T>::create(Node);
  }

  /// \brief Retrieve the stored node as type \c T.
  ///
  /// Returns NULL if the stored node does not have a type that is
  /// convertible to \c T.
  ///
  /// For types that have identity via their pointer in the AST
  /// (like \c Stmt and \c Decl) the returned pointer points to the
  /// referenced AST node.
  /// For other types (like \c QualType) the value is stored directly
  /// in the \c DynTypedNode, and the returned pointer points at
  /// the storage inside DynTypedNode. For those nodes, do not
  /// use the pointer outside the scope of the DynTypedNode.
  template <typename T>
  const T *get() const {
    return BaseConverter<T>::get(Tag, Storage.buffer);
  }

  /// \brief Returns a pointer that identifies the stored AST node.
  ///
  /// Note that this is not supported by all AST nodes. For AST nodes
  /// that don't have a pointer-defined identity inside the AST, this
  /// method returns NULL.
  const void *getMemoizationData() const;

private:
  /// \brief Takes care of converting from and to \c T.
  template <typename T, typename EnablerT = void> struct BaseConverter;

  /// \brief Supported base node types.
  enum NodeTypeTag {
    NT_Decl,
    NT_Stmt,
    NT_NestedNameSpecifier,
    NT_NestedNameSpecifierLoc,
    NT_QualType,
    NT_Type,
    NT_TypeLoc
  } Tag;

  /// \brief Stores the data of the node.
  ///
  /// Note that we can store \c Decls and \c Stmts by pointer as they are
  /// guaranteed to be unique pointers pointing to dedicated storage in the
  /// AST. \c QualTypes on the other hand do not have storage or unique
  /// pointers and thus need to be stored by value.
  llvm::AlignedCharArrayUnion<Decl *, Stmt *, NestedNameSpecifier,
                              NestedNameSpecifierLoc, QualType, Type,
                              TypeLoc> Storage;
};

// FIXME: Pull out abstraction for the following.
template<typename T> struct DynTypedNode::BaseConverter<T,
    typename llvm::enable_if<llvm::is_base_of<Decl, T> >::type> {
  static const T *get(NodeTypeTag Tag, const char Storage[]) {
    if (Tag == NT_Decl)
      return dyn_cast<T>(*reinterpret_cast<Decl*const*>(Storage));
    return NULL;
  }
  static DynTypedNode create(const Decl &Node) {
    DynTypedNode Result;
    Result.Tag = NT_Decl;
    new (Result.Storage.buffer) const Decl*(&Node);
    return Result;
  }
};
template<typename T> struct DynTypedNode::BaseConverter<T,
    typename llvm::enable_if<llvm::is_base_of<Stmt, T> >::type> {
  static const T *get(NodeTypeTag Tag, const char Storage[]) {
    if (Tag == NT_Stmt)
      return dyn_cast<T>(*reinterpret_cast<Stmt*const*>(Storage));
    return NULL;
  }
  static DynTypedNode create(const Stmt &Node) {
    DynTypedNode Result;
    Result.Tag = NT_Stmt;
    new (Result.Storage.buffer) const Stmt*(&Node);
    return Result;
  }
};
template<typename T> struct DynTypedNode::BaseConverter<T,
    typename llvm::enable_if<llvm::is_base_of<Type, T> >::type> {
  static const T *get(NodeTypeTag Tag, const char Storage[]) {
    if (Tag == NT_Type)
      return dyn_cast<T>(*reinterpret_cast<Type*const*>(Storage));
    return NULL;
  }
  static DynTypedNode create(const Type &Node) {
    DynTypedNode Result;
    Result.Tag = NT_Type;
    new (Result.Storage.buffer) const Type*(&Node);
    return Result;
  }
};
template<> struct DynTypedNode::BaseConverter<NestedNameSpecifier, void> {
  static const NestedNameSpecifier *get(NodeTypeTag Tag, const char Storage[]) {
    if (Tag == NT_NestedNameSpecifier)
      return *reinterpret_cast<NestedNameSpecifier*const*>(Storage);
    return NULL;
  }
  static DynTypedNode create(const NestedNameSpecifier &Node) {
    DynTypedNode Result;
    Result.Tag = NT_NestedNameSpecifier;
    new (Result.Storage.buffer) const NestedNameSpecifier*(&Node);
    return Result;
  }
};
template<> struct DynTypedNode::BaseConverter<NestedNameSpecifierLoc, void> {
  static const NestedNameSpecifierLoc *get(NodeTypeTag Tag,
                                           const char Storage[]) {
    if (Tag == NT_NestedNameSpecifierLoc)
      return reinterpret_cast<const NestedNameSpecifierLoc*>(Storage);
    return NULL;
  }
  static DynTypedNode create(const NestedNameSpecifierLoc &Node) {
    DynTypedNode Result;
    Result.Tag = NT_NestedNameSpecifierLoc;
    new (Result.Storage.buffer) NestedNameSpecifierLoc(Node);
    return Result;
  }
};
template<> struct DynTypedNode::BaseConverter<QualType, void> {
  static const QualType *get(NodeTypeTag Tag, const char Storage[]) {
    if (Tag == NT_QualType)
      return reinterpret_cast<const QualType*>(Storage);
    return NULL;
  }
  static DynTypedNode create(const QualType &Node) {
    DynTypedNode Result;
    Result.Tag = NT_QualType;
    new (Result.Storage.buffer) QualType(Node);
    return Result;
  }
};
template<> struct DynTypedNode::BaseConverter<TypeLoc, void> {
  static const TypeLoc *get(NodeTypeTag Tag, const char Storage[]) {
    if (Tag == NT_TypeLoc)
      return reinterpret_cast<const TypeLoc*>(Storage);
    return NULL;
  }
  static DynTypedNode create(const TypeLoc &Node) {
    DynTypedNode Result;
    Result.Tag = NT_TypeLoc;
    new (Result.Storage.buffer) TypeLoc(Node);
    return Result;
  }
};
// The only operation we allow on unsupported types is \c get.
// This allows to conveniently use \c DynTypedNode when having an arbitrary
// AST node that is not supported, but prevents misuse - a user cannot create
// a DynTypedNode from arbitrary types.
template <typename T, typename EnablerT> struct DynTypedNode::BaseConverter {
  static const T *get(NodeTypeTag Tag, const char Storage[]) { return NULL; }
};

inline const void *DynTypedNode::getMemoizationData() const {
  switch (Tag) {
    case NT_Decl: return BaseConverter<Decl>::get(Tag, Storage.buffer);
    case NT_Stmt: return BaseConverter<Stmt>::get(Tag, Storage.buffer);
    default: return NULL;
  };
}

} // end namespace ast_type_traits
} // end namespace clang

#endif // LLVM_CLANG_AST_AST_TYPE_TRAITS_H