1 //===--- ASTTypeTraits.h ----------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Provides a dynamic type identifier and a dynamically typed node container 10 // that can be used to store an AST base node at runtime in the same storage in 11 // a type safe way. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef LLVM_CLANG_AST_ASTTYPETRAITS_H 16 #define LLVM_CLANG_AST_ASTTYPETRAITS_H 17 18 #include "clang/AST/ASTFwd.h" 19 #include "clang/AST/DeclCXX.h" 20 #include "clang/AST/NestedNameSpecifier.h" 21 #include "clang/AST/TemplateBase.h" 22 #include "clang/AST/TypeLoc.h" 23 #include "clang/Basic/LLVM.h" 24 #include "llvm/ADT/DenseMapInfo.h" 25 #include "llvm/Support/AlignOf.h" 26 27 namespace llvm { 28 29 class raw_ostream; 30 31 } 32 33 namespace clang { 34 35 struct PrintingPolicy; 36 37 /// Defines how we descend a level in the AST when we pass 38 /// through expressions. 39 enum TraversalKind { 40 /// Will traverse all child nodes. 41 TK_AsIs, 42 43 /// Will not traverse implicit casts and parentheses. 44 /// Corresponds to Expr::IgnoreParenImpCasts() 45 TK_IgnoreImplicitCastsAndParentheses, 46 47 /// Ignore AST nodes not written in the source 48 TK_IgnoreUnlessSpelledInSource 49 }; 50 51 /// Kind identifier. 52 /// 53 /// It can be constructed from any node kind and allows for runtime type 54 /// hierarchy checks. 55 /// Use getFromNodeKind<T>() to construct them. 56 class ASTNodeKind { 57 public: 58 /// Empty identifier. It matches nothing. ASTNodeKind()59 ASTNodeKind() : KindId(NKI_None) {} 60 61 /// Construct an identifier for T. 62 template <class T> getFromNodeKind()63 static ASTNodeKind getFromNodeKind() { 64 return ASTNodeKind(KindToKindId<T>::Id); 65 } 66 67 /// \{ 68 /// Construct an identifier for the dynamic type of the node 69 static ASTNodeKind getFromNode(const Decl &D); 70 static ASTNodeKind getFromNode(const Stmt &S); 71 static ASTNodeKind getFromNode(const Type &T); 72 static ASTNodeKind getFromNode(const OMPClause &C); 73 /// \} 74 75 /// Returns \c true if \c this and \c Other represent the same kind. isSame(ASTNodeKind Other)76 bool isSame(ASTNodeKind Other) const { 77 return KindId != NKI_None && KindId == Other.KindId; 78 } 79 80 /// Returns \c true only for the default \c ASTNodeKind() isNone()81 bool isNone() const { return KindId == NKI_None; } 82 83 /// Returns \c true if \c this is a base kind of (or same as) \c Other. 84 /// \param Distance If non-null, used to return the distance between \c this 85 /// and \c Other in the class hierarchy. 86 bool isBaseOf(ASTNodeKind Other, unsigned *Distance = nullptr) const; 87 88 /// String representation of the kind. 89 StringRef asStringRef() const; 90 91 /// Strict weak ordering for ASTNodeKind. 92 bool operator<(const ASTNodeKind &Other) const { 93 return KindId < Other.KindId; 94 } 95 96 /// Return the most derived type between \p Kind1 and \p Kind2. 97 /// 98 /// Return ASTNodeKind() if they are not related. 99 static ASTNodeKind getMostDerivedType(ASTNodeKind Kind1, ASTNodeKind Kind2); 100 101 /// Return the most derived common ancestor between Kind1 and Kind2. 102 /// 103 /// Return ASTNodeKind() if they are not related. 104 static ASTNodeKind getMostDerivedCommonAncestor(ASTNodeKind Kind1, 105 ASTNodeKind Kind2); 106 107 /// Hooks for using ASTNodeKind as a key in a DenseMap. 108 struct DenseMapInfo { 109 // ASTNodeKind() is a good empty key because it is represented as a 0. getEmptyKeyDenseMapInfo110 static inline ASTNodeKind getEmptyKey() { return ASTNodeKind(); } 111 // NKI_NumberOfKinds is not a valid value, so it is good for a 112 // tombstone key. getTombstoneKeyDenseMapInfo113 static inline ASTNodeKind getTombstoneKey() { 114 return ASTNodeKind(NKI_NumberOfKinds); 115 } getHashValueDenseMapInfo116 static unsigned getHashValue(const ASTNodeKind &Val) { return Val.KindId; } isEqualDenseMapInfo117 static bool isEqual(const ASTNodeKind &LHS, const ASTNodeKind &RHS) { 118 return LHS.KindId == RHS.KindId; 119 } 120 }; 121 122 /// Check if the given ASTNodeKind identifies a type that offers pointer 123 /// identity. This is useful for the fast path in DynTypedNode. hasPointerIdentity()124 bool hasPointerIdentity() const { 125 return KindId > NKI_LastKindWithoutPointerIdentity; 126 } 127 128 private: 129 /// Kind ids. 130 /// 131 /// Includes all possible base and derived kinds. 132 enum NodeKindId { 133 NKI_None, 134 NKI_TemplateArgument, 135 NKI_TemplateArgumentLoc, 136 NKI_TemplateName, 137 NKI_NestedNameSpecifierLoc, 138 NKI_QualType, 139 NKI_TypeLoc, 140 NKI_LastKindWithoutPointerIdentity = NKI_TypeLoc, 141 NKI_CXXBaseSpecifier, 142 NKI_CXXCtorInitializer, 143 NKI_NestedNameSpecifier, 144 NKI_Decl, 145 #define DECL(DERIVED, BASE) NKI_##DERIVED##Decl, 146 #include "clang/AST/DeclNodes.inc" 147 NKI_Stmt, 148 #define STMT(DERIVED, BASE) NKI_##DERIVED, 149 #include "clang/AST/StmtNodes.inc" 150 NKI_Type, 151 #define TYPE(DERIVED, BASE) NKI_##DERIVED##Type, 152 #include "clang/AST/TypeNodes.inc" 153 NKI_OMPClause, 154 #define OMP_CLAUSE_CLASS(Enum, Str, Class) NKI_##Class, 155 #include "llvm/Frontend/OpenMP/OMPKinds.def" 156 NKI_NumberOfKinds 157 }; 158 159 /// Use getFromNodeKind<T>() to construct the kind. ASTNodeKind(NodeKindId KindId)160 ASTNodeKind(NodeKindId KindId) : KindId(KindId) {} 161 162 /// Returns \c true if \c Base is a base kind of (or same as) \c 163 /// Derived. 164 /// \param Distance If non-null, used to return the distance between \c Base 165 /// and \c Derived in the class hierarchy. 166 static bool isBaseOf(NodeKindId Base, NodeKindId Derived, unsigned *Distance); 167 168 /// Helper meta-function to convert a kind T to its enum value. 169 /// 170 /// This struct is specialized below for all known kinds. 171 template <class T> struct KindToKindId { 172 static const NodeKindId Id = NKI_None; 173 }; 174 template <class T> 175 struct KindToKindId<const T> : KindToKindId<T> {}; 176 177 /// Per kind info. 178 struct KindInfo { 179 /// The id of the parent kind, or None if it has no parent. 180 NodeKindId ParentId; 181 /// Name of the kind. 182 const char *Name; 183 }; 184 static const KindInfo AllKindInfo[NKI_NumberOfKinds]; 185 186 NodeKindId KindId; 187 }; 188 189 #define KIND_TO_KIND_ID(Class) \ 190 template <> struct ASTNodeKind::KindToKindId<Class> { \ 191 static const NodeKindId Id = NKI_##Class; \ 192 }; 193 KIND_TO_KIND_ID(CXXCtorInitializer) 194 KIND_TO_KIND_ID(TemplateArgument) 195 KIND_TO_KIND_ID(TemplateArgumentLoc) 196 KIND_TO_KIND_ID(TemplateName) 197 KIND_TO_KIND_ID(NestedNameSpecifier) 198 KIND_TO_KIND_ID(NestedNameSpecifierLoc) 199 KIND_TO_KIND_ID(QualType) 200 KIND_TO_KIND_ID(TypeLoc) 201 KIND_TO_KIND_ID(Decl) 202 KIND_TO_KIND_ID(Stmt) 203 KIND_TO_KIND_ID(Type) 204 KIND_TO_KIND_ID(OMPClause) 205 KIND_TO_KIND_ID(CXXBaseSpecifier) 206 #define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl) 207 #include "clang/AST/DeclNodes.inc" 208 #define STMT(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED) 209 #include "clang/AST/StmtNodes.inc" 210 #define TYPE(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Type) 211 #include "clang/AST/TypeNodes.inc" 212 #define OMP_CLAUSE_CLASS(Enum, Str, Class) KIND_TO_KIND_ID(Class) 213 #include "llvm/Frontend/OpenMP/OMPKinds.def" 214 #undef KIND_TO_KIND_ID 215 216 inline raw_ostream &operator<<(raw_ostream &OS, ASTNodeKind K) { 217 OS << K.asStringRef(); 218 return OS; 219 } 220 221 /// A dynamically typed AST node container. 222 /// 223 /// Stores an AST node in a type safe way. This allows writing code that 224 /// works with different kinds of AST nodes, despite the fact that they don't 225 /// have a common base class. 226 /// 227 /// Use \c create(Node) to create a \c DynTypedNode from an AST node, 228 /// and \c get<T>() to retrieve the node as type T if the types match. 229 /// 230 /// See \c ASTNodeKind for which node base types are currently supported; 231 /// You can create DynTypedNodes for all nodes in the inheritance hierarchy of 232 /// the supported base types. 233 class DynTypedNode { 234 public: 235 /// Creates a \c DynTypedNode from \c Node. 236 template <typename T> 237 static DynTypedNode create(const T &Node) { 238 return BaseConverter<T>::create(Node); 239 } 240 241 /// Retrieve the stored node as type \c T. 242 /// 243 /// Returns NULL if the stored node does not have a type that is 244 /// convertible to \c T. 245 /// 246 /// For types that have identity via their pointer in the AST 247 /// (like \c Stmt, \c Decl, \c Type and \c NestedNameSpecifier) the returned 248 /// pointer points to the referenced AST node. 249 /// For other types (like \c QualType) the value is stored directly 250 /// in the \c DynTypedNode, and the returned pointer points at 251 /// the storage inside DynTypedNode. For those nodes, do not 252 /// use the pointer outside the scope of the DynTypedNode. 253 template <typename T> 254 const T *get() const { 255 return BaseConverter<T>::get(NodeKind, Storage.buffer); 256 } 257 258 /// Retrieve the stored node as type \c T. 259 /// 260 /// Similar to \c get(), but asserts that the type is what we are expecting. 261 template <typename T> 262 const T &getUnchecked() const { 263 return BaseConverter<T>::getUnchecked(NodeKind, Storage.buffer); 264 } 265 266 ASTNodeKind getNodeKind() const { return NodeKind; } 267 268 /// Returns a pointer that identifies the stored AST node. 269 /// 270 /// Note that this is not supported by all AST nodes. For AST nodes 271 /// that don't have a pointer-defined identity inside the AST, this 272 /// method returns NULL. 273 const void *getMemoizationData() const { 274 return NodeKind.hasPointerIdentity() 275 ? *reinterpret_cast<void *const *>(Storage.buffer) 276 : nullptr; 277 } 278 279 /// Prints the node to the given output stream. 280 void print(llvm::raw_ostream &OS, const PrintingPolicy &PP) const; 281 282 /// Dumps the node to the given output stream. 283 void dump(llvm::raw_ostream &OS, const ASTContext &Context) const; 284 285 /// For nodes which represent textual entities in the source code, 286 /// return their SourceRange. For all other nodes, return SourceRange(). 287 SourceRange getSourceRange() const; 288 289 /// @{ 290 /// Imposes an order on \c DynTypedNode. 291 /// 292 /// Supports comparison of nodes that support memoization. 293 /// FIXME: Implement comparison for other node types (currently 294 /// only Stmt, Decl, Type and NestedNameSpecifier return memoization data). 295 bool operator<(const DynTypedNode &Other) const { 296 if (!NodeKind.isSame(Other.NodeKind)) 297 return NodeKind < Other.NodeKind; 298 299 if (ASTNodeKind::getFromNodeKind<QualType>().isSame(NodeKind)) 300 return getUnchecked<QualType>().getAsOpaquePtr() < 301 Other.getUnchecked<QualType>().getAsOpaquePtr(); 302 303 if (ASTNodeKind::getFromNodeKind<TypeLoc>().isSame(NodeKind)) { 304 auto TLA = getUnchecked<TypeLoc>(); 305 auto TLB = Other.getUnchecked<TypeLoc>(); 306 return std::make_pair(TLA.getType().getAsOpaquePtr(), 307 TLA.getOpaqueData()) < 308 std::make_pair(TLB.getType().getAsOpaquePtr(), 309 TLB.getOpaqueData()); 310 } 311 312 if (ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>().isSame( 313 NodeKind)) { 314 auto NNSLA = getUnchecked<NestedNameSpecifierLoc>(); 315 auto NNSLB = Other.getUnchecked<NestedNameSpecifierLoc>(); 316 return std::make_pair(NNSLA.getNestedNameSpecifier(), 317 NNSLA.getOpaqueData()) < 318 std::make_pair(NNSLB.getNestedNameSpecifier(), 319 NNSLB.getOpaqueData()); 320 } 321 322 assert(getMemoizationData() && Other.getMemoizationData()); 323 return getMemoizationData() < Other.getMemoizationData(); 324 } 325 bool operator==(const DynTypedNode &Other) const { 326 // DynTypedNode::create() stores the exact kind of the node in NodeKind. 327 // If they contain the same node, their NodeKind must be the same. 328 if (!NodeKind.isSame(Other.NodeKind)) 329 return false; 330 331 // FIXME: Implement for other types. 332 if (ASTNodeKind::getFromNodeKind<QualType>().isSame(NodeKind)) 333 return getUnchecked<QualType>() == Other.getUnchecked<QualType>(); 334 335 if (ASTNodeKind::getFromNodeKind<TypeLoc>().isSame(NodeKind)) 336 return getUnchecked<TypeLoc>() == Other.getUnchecked<TypeLoc>(); 337 338 if (ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>().isSame(NodeKind)) 339 return getUnchecked<NestedNameSpecifierLoc>() == 340 Other.getUnchecked<NestedNameSpecifierLoc>(); 341 342 assert(getMemoizationData() && Other.getMemoizationData()); 343 return getMemoizationData() == Other.getMemoizationData(); 344 } 345 bool operator!=(const DynTypedNode &Other) const { 346 return !operator==(Other); 347 } 348 /// @} 349 350 /// Hooks for using DynTypedNode as a key in a DenseMap. 351 struct DenseMapInfo { 352 static inline DynTypedNode getEmptyKey() { 353 DynTypedNode Node; 354 Node.NodeKind = ASTNodeKind::DenseMapInfo::getEmptyKey(); 355 return Node; 356 } 357 static inline DynTypedNode getTombstoneKey() { 358 DynTypedNode Node; 359 Node.NodeKind = ASTNodeKind::DenseMapInfo::getTombstoneKey(); 360 return Node; 361 } 362 static unsigned getHashValue(const DynTypedNode &Val) { 363 // FIXME: Add hashing support for the remaining types. 364 if (ASTNodeKind::getFromNodeKind<TypeLoc>().isSame(Val.NodeKind)) { 365 auto TL = Val.getUnchecked<TypeLoc>(); 366 return llvm::hash_combine(TL.getType().getAsOpaquePtr(), 367 TL.getOpaqueData()); 368 } 369 370 if (ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>().isSame( 371 Val.NodeKind)) { 372 auto NNSL = Val.getUnchecked<NestedNameSpecifierLoc>(); 373 return llvm::hash_combine(NNSL.getNestedNameSpecifier(), 374 NNSL.getOpaqueData()); 375 } 376 377 assert(Val.getMemoizationData()); 378 return llvm::hash_value(Val.getMemoizationData()); 379 } 380 static bool isEqual(const DynTypedNode &LHS, const DynTypedNode &RHS) { 381 auto Empty = ASTNodeKind::DenseMapInfo::getEmptyKey(); 382 auto TombStone = ASTNodeKind::DenseMapInfo::getTombstoneKey(); 383 return (ASTNodeKind::DenseMapInfo::isEqual(LHS.NodeKind, Empty) && 384 ASTNodeKind::DenseMapInfo::isEqual(RHS.NodeKind, Empty)) || 385 (ASTNodeKind::DenseMapInfo::isEqual(LHS.NodeKind, TombStone) && 386 ASTNodeKind::DenseMapInfo::isEqual(RHS.NodeKind, TombStone)) || 387 LHS == RHS; 388 } 389 }; 390 391 private: 392 /// Takes care of converting from and to \c T. 393 template <typename T, typename EnablerT = void> struct BaseConverter; 394 395 /// Converter that uses dyn_cast<T> from a stored BaseT*. 396 template <typename T, typename BaseT> struct DynCastPtrConverter { 397 static const T *get(ASTNodeKind NodeKind, const char Storage[]) { 398 if (ASTNodeKind::getFromNodeKind<T>().isBaseOf(NodeKind)) 399 return &getUnchecked(NodeKind, Storage); 400 return nullptr; 401 } 402 static const T &getUnchecked(ASTNodeKind NodeKind, const char Storage[]) { 403 assert(ASTNodeKind::getFromNodeKind<T>().isBaseOf(NodeKind)); 404 return *cast<T>(static_cast<const BaseT *>( 405 *reinterpret_cast<const void *const *>(Storage))); 406 } 407 static DynTypedNode create(const BaseT &Node) { 408 DynTypedNode Result; 409 Result.NodeKind = ASTNodeKind::getFromNode(Node); 410 new (Result.Storage.buffer) const void *(&Node); 411 return Result; 412 } 413 }; 414 415 /// Converter that stores T* (by pointer). 416 template <typename T> struct PtrConverter { 417 static const T *get(ASTNodeKind NodeKind, const char Storage[]) { 418 if (ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind)) 419 return &getUnchecked(NodeKind, Storage); 420 return nullptr; 421 } 422 static const T &getUnchecked(ASTNodeKind NodeKind, const char Storage[]) { 423 assert(ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind)); 424 return *static_cast<const T *>( 425 *reinterpret_cast<const void *const *>(Storage)); 426 } 427 static DynTypedNode create(const T &Node) { 428 DynTypedNode Result; 429 Result.NodeKind = ASTNodeKind::getFromNodeKind<T>(); 430 new (Result.Storage.buffer) const void *(&Node); 431 return Result; 432 } 433 }; 434 435 /// Converter that stores T (by value). 436 template <typename T> struct ValueConverter { 437 static const T *get(ASTNodeKind NodeKind, const char Storage[]) { 438 if (ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind)) 439 return reinterpret_cast<const T *>(Storage); 440 return nullptr; 441 } 442 static const T &getUnchecked(ASTNodeKind NodeKind, const char Storage[]) { 443 assert(ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind)); 444 return *reinterpret_cast<const T *>(Storage); 445 } 446 static DynTypedNode create(const T &Node) { 447 DynTypedNode Result; 448 Result.NodeKind = ASTNodeKind::getFromNodeKind<T>(); 449 new (Result.Storage.buffer) T(Node); 450 return Result; 451 } 452 }; 453 454 ASTNodeKind NodeKind; 455 456 /// Stores the data of the node. 457 /// 458 /// Note that we can store \c Decls, \c Stmts, \c Types, 459 /// \c NestedNameSpecifiers and \c CXXCtorInitializer by pointer as they are 460 /// guaranteed to be unique pointers pointing to dedicated storage in the AST. 461 /// \c QualTypes, \c NestedNameSpecifierLocs, \c TypeLocs, 462 /// \c TemplateArguments and \c TemplateArgumentLocs on the other hand do not 463 /// have storage or unique pointers and thus need to be stored by value. 464 llvm::AlignedCharArrayUnion<const void *, TemplateArgument, 465 TemplateArgumentLoc, NestedNameSpecifierLoc, 466 QualType, TypeLoc> 467 Storage; 468 }; 469 470 template <typename T> 471 struct DynTypedNode::BaseConverter< 472 T, std::enable_if_t<std::is_base_of<Decl, T>::value>> 473 : public DynCastPtrConverter<T, Decl> {}; 474 475 template <typename T> 476 struct DynTypedNode::BaseConverter< 477 T, std::enable_if_t<std::is_base_of<Stmt, T>::value>> 478 : public DynCastPtrConverter<T, Stmt> {}; 479 480 template <typename T> 481 struct DynTypedNode::BaseConverter< 482 T, std::enable_if_t<std::is_base_of<Type, T>::value>> 483 : public DynCastPtrConverter<T, Type> {}; 484 485 template <typename T> 486 struct DynTypedNode::BaseConverter< 487 T, std::enable_if_t<std::is_base_of<OMPClause, T>::value>> 488 : public DynCastPtrConverter<T, OMPClause> {}; 489 490 template <> 491 struct DynTypedNode::BaseConverter< 492 NestedNameSpecifier, void> : public PtrConverter<NestedNameSpecifier> {}; 493 494 template <> 495 struct DynTypedNode::BaseConverter< 496 CXXCtorInitializer, void> : public PtrConverter<CXXCtorInitializer> {}; 497 498 template <> 499 struct DynTypedNode::BaseConverter< 500 TemplateArgument, void> : public ValueConverter<TemplateArgument> {}; 501 502 template <> 503 struct DynTypedNode::BaseConverter<TemplateArgumentLoc, void> 504 : public ValueConverter<TemplateArgumentLoc> {}; 505 506 template <> 507 struct DynTypedNode::BaseConverter< 508 TemplateName, void> : public ValueConverter<TemplateName> {}; 509 510 template <> 511 struct DynTypedNode::BaseConverter< 512 NestedNameSpecifierLoc, 513 void> : public ValueConverter<NestedNameSpecifierLoc> {}; 514 515 template <> 516 struct DynTypedNode::BaseConverter<QualType, 517 void> : public ValueConverter<QualType> {}; 518 519 template <> 520 struct DynTypedNode::BaseConverter< 521 TypeLoc, void> : public ValueConverter<TypeLoc> {}; 522 523 template <> 524 struct DynTypedNode::BaseConverter<CXXBaseSpecifier, void> 525 : public PtrConverter<CXXBaseSpecifier> {}; 526 527 // The only operation we allow on unsupported types is \c get. 528 // This allows to conveniently use \c DynTypedNode when having an arbitrary 529 // AST node that is not supported, but prevents misuse - a user cannot create 530 // a DynTypedNode from arbitrary types. 531 template <typename T, typename EnablerT> struct DynTypedNode::BaseConverter { 532 static const T *get(ASTNodeKind NodeKind, const char Storage[]) { 533 return NULL; 534 } 535 }; 536 537 // Previously these types were defined in the clang::ast_type_traits namespace. 538 // Provide typedefs so that legacy code can be fixed asynchronously. 539 namespace ast_type_traits { 540 using DynTypedNode = ::clang::DynTypedNode; 541 using ASTNodeKind = ::clang::ASTNodeKind; 542 using TraversalKind = ::clang::TraversalKind; 543 544 constexpr TraversalKind TK_AsIs = ::clang::TK_AsIs; 545 constexpr TraversalKind TK_IgnoreImplicitCastsAndParentheses = 546 ::clang::TK_IgnoreImplicitCastsAndParentheses; 547 constexpr TraversalKind TK_IgnoreUnlessSpelledInSource = 548 ::clang::TK_IgnoreUnlessSpelledInSource; 549 } // namespace ast_type_traits 550 551 } // end namespace clang 552 553 namespace llvm { 554 555 template <> 556 struct DenseMapInfo<clang::ASTNodeKind> : clang::ASTNodeKind::DenseMapInfo {}; 557 558 template <> 559 struct DenseMapInfo<clang::DynTypedNode> : clang::DynTypedNode::DenseMapInfo {}; 560 561 } // end namespace llvm 562 563 #endif 564