diff --git a/clang/include/clang/AST/ASTFwd.h b/clang/include/clang/AST/ASTFwd.h new file mode 100644 index 000000000000..27e7faa01536 --- /dev/null +++ b/clang/include/clang/AST/ASTFwd.h @@ -0,0 +1,27 @@ +//===--- ASTFwd.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------===// +/// +/// \file +/// \brief Forward declaration of all AST node types. +/// +//===-------------------------------------------------------------===// + +namespace clang { + +class Decl; +#define DECL(DERIVED, BASE) class DERIVED##Decl; +#include "clang/AST/DeclNodes.inc" +class Stmt; +#define STMT(DERIVED, BASE) class DERIVED; +#include "clang/AST/StmtNodes.inc" +class Type; +#define TYPE(DERIVED, BASE) class DERIVED##Type; +#include "clang/AST/TypeNodes.def" + +} // end namespace clang diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h index 1139e0e1857a..d98ad33a2ae0 100644 --- a/clang/include/clang/AST/ASTTypeTraits.h +++ b/clang/include/clang/AST/ASTTypeTraits.h @@ -7,22 +7,117 @@ // //===----------------------------------------------------------------------===// // -// 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. +// Provides a dynamic type identifier and 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/ASTFwd.h" #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" #include "clang/AST/TypeLoc.h" +#include "clang/Basic/LLVM.h" #include "llvm/Support/AlignOf.h" namespace clang { namespace ast_type_traits { +/// \brief Kind identifier. +/// +/// It can be constructed from any node kind and allows for runtime type +/// hierarchy checks. +/// Use getFromNodeKind() to construct them. +class ASTNodeKind { +public: + /// \brief Empty identifier. It matches nothing. + ASTNodeKind() : KindId(NKI_None) {} + + /// \brief Construct an identifier for T. + template + static ASTNodeKind getFromNodeKind() { + return ASTNodeKind(KindToKindId::Id); + } + + /// \brief Returns \c true if \c this and \c Other represent the same kind. + bool isSame(ASTNodeKind Other); + + /// \brief Returns \c true if \c this is a base kind of (or same as) \c Other + bool isBaseOf(ASTNodeKind Other); + + /// \brief String representation of the kind. + StringRef asStringRef() const; + +private: + /// \brief Kind ids. + /// + /// Includes all possible base and derived kinds. + enum NodeKindId { + NKI_None, + NKI_NestedNameSpecifier, + NKI_NestedNameSpecifierLoc, + NKI_QualType, + NKI_TypeLoc, + NKI_Decl, +#define DECL(DERIVED, BASE) NKI_##DERIVED##Decl, +#include "clang/AST/DeclNodes.inc" + NKI_Stmt, +#define STMT(DERIVED, BASE) NKI_##DERIVED, +#include "clang/AST/StmtNodes.inc" + NKI_Type, +#define TYPE(DERIVED, BASE) NKI_##DERIVED##Type, +#include "clang/AST/TypeNodes.def" + NKI_NumberOfKinds + }; + + /// \brief Use getFromNodeKind() to construct the kind. + ASTNodeKind(NodeKindId KindId) : KindId(KindId) {} + + /// \brief Returns \c true if \c Base is a base kind of (or same as) \c + /// Derived + static bool isBaseOf(NodeKindId Base, NodeKindId Derived); + + /// \brief Helper meta-function to convert a kind T to its enum value. + /// + /// This struct is specialized below for all known kinds. + template struct KindToKindId { + static const NodeKindId Id = NKI_None; + }; + + /// \brief Per kind info. + struct KindInfo { + /// \brief The id of the parent kind, or None if it has no parent. + NodeKindId ParentId; + /// \brief Name of the kind. + const char *Name; + }; + static const KindInfo AllKindInfo[NKI_NumberOfKinds]; + + NodeKindId KindId; +}; + +#define KIND_TO_KIND_ID(Class) \ + template <> struct ASTNodeKind::KindToKindId { \ + static const NodeKindId Id = NKI_##Class; \ + }; +KIND_TO_KIND_ID(NestedNameSpecifier) +KIND_TO_KIND_ID(NestedNameSpecifierLoc) +KIND_TO_KIND_ID(QualType) +KIND_TO_KIND_ID(TypeLoc) +KIND_TO_KIND_ID(Decl) +KIND_TO_KIND_ID(Stmt) +KIND_TO_KIND_ID(Type) +#define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl) +#include "clang/AST/DeclNodes.inc" +#define STMT(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED) +#include "clang/AST/StmtNodes.inc" +#define TYPE(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Type) +#include "clang/AST/TypeNodes.def" +#undef KIND_TO_KIND_ID + /// \brief A dynamically typed AST node container. /// /// Stores an AST node in a type safe way. This allows writing code that @@ -57,7 +152,7 @@ public: /// use the pointer outside the scope of the DynTypedNode. template const T *get() const { - return BaseConverter::get(Tag, Storage.buffer); + return BaseConverter::get(NodeKind, Storage.buffer); } /// \brief Returns a pointer that identifies the stored AST node. @@ -90,16 +185,7 @@ private: /// \brief Takes care of converting from and to \c T. template struct BaseConverter; - /// \brief Supported base node types. - enum NodeTypeTag { - NT_Decl, - NT_Stmt, - NT_NestedNameSpecifier, - NT_NestedNameSpecifierLoc, - NT_QualType, - NT_Type, - NT_TypeLoc - } Tag; + ASTNodeKind NodeKind; /// \brief Stores the data of the node. /// @@ -107,103 +193,105 @@ private: /// 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 Storage; }; // FIXME: Pull out abstraction for the following. template struct DynTypedNode::BaseConverter >::type> { - static const T *get(NodeTypeTag Tag, const char Storage[]) { - if (Tag == NT_Decl) + static const T *get(ASTNodeKind NodeKind, const char Storage[]) { + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) return dyn_cast(*reinterpret_cast(Storage)); return NULL; } static DynTypedNode create(const Decl &Node) { DynTypedNode Result; - Result.Tag = NT_Decl; + Result.NodeKind = ASTNodeKind::getFromNodeKind(); new (Result.Storage.buffer) const Decl*(&Node); return Result; } }; template struct DynTypedNode::BaseConverter >::type> { - static const T *get(NodeTypeTag Tag, const char Storage[]) { - if (Tag == NT_Stmt) + static const T *get(ASTNodeKind NodeKind, const char Storage[]) { + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) return dyn_cast(*reinterpret_cast(Storage)); return NULL; } static DynTypedNode create(const Stmt &Node) { DynTypedNode Result; - Result.Tag = NT_Stmt; + Result.NodeKind = ASTNodeKind::getFromNodeKind(); new (Result.Storage.buffer) const Stmt*(&Node); return Result; } }; template struct DynTypedNode::BaseConverter >::type> { - static const T *get(NodeTypeTag Tag, const char Storage[]) { - if (Tag == NT_Type) + static const T *get(ASTNodeKind NodeKind, const char Storage[]) { + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) return dyn_cast(*reinterpret_cast(Storage)); return NULL; } static DynTypedNode create(const Type &Node) { DynTypedNode Result; - Result.Tag = NT_Type; + Result.NodeKind = ASTNodeKind::getFromNodeKind(); new (Result.Storage.buffer) const Type*(&Node); return Result; } }; template<> struct DynTypedNode::BaseConverter { - static const NestedNameSpecifier *get(NodeTypeTag Tag, const char Storage[]) { - if (Tag == NT_NestedNameSpecifier) + static const NestedNameSpecifier *get(ASTNodeKind NodeKind, + const char Storage[]) { + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) return *reinterpret_cast(Storage); return NULL; } static DynTypedNode create(const NestedNameSpecifier &Node) { DynTypedNode Result; - Result.Tag = NT_NestedNameSpecifier; + Result.NodeKind = ASTNodeKind::getFromNodeKind(); new (Result.Storage.buffer) const NestedNameSpecifier*(&Node); return Result; } }; template<> struct DynTypedNode::BaseConverter { - static const NestedNameSpecifierLoc *get(NodeTypeTag Tag, + static const NestedNameSpecifierLoc *get(ASTNodeKind NodeKind, const char Storage[]) { - if (Tag == NT_NestedNameSpecifierLoc) + if (ASTNodeKind::getFromNodeKind().isBaseOf( + NodeKind)) return reinterpret_cast(Storage); return NULL; } static DynTypedNode create(const NestedNameSpecifierLoc &Node) { DynTypedNode Result; - Result.Tag = NT_NestedNameSpecifierLoc; + Result.NodeKind = ASTNodeKind::getFromNodeKind(); new (Result.Storage.buffer) NestedNameSpecifierLoc(Node); return Result; } }; template<> struct DynTypedNode::BaseConverter { - static const QualType *get(NodeTypeTag Tag, const char Storage[]) { - if (Tag == NT_QualType) + static const QualType *get(ASTNodeKind NodeKind, const char Storage[]) { + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) return reinterpret_cast(Storage); return NULL; } static DynTypedNode create(const QualType &Node) { DynTypedNode Result; - Result.Tag = NT_QualType; + Result.NodeKind = ASTNodeKind::getFromNodeKind(); new (Result.Storage.buffer) QualType(Node); return Result; } }; template<> struct DynTypedNode::BaseConverter { - static const TypeLoc *get(NodeTypeTag Tag, const char Storage[]) { - if (Tag == NT_TypeLoc) + static const TypeLoc *get(ASTNodeKind NodeKind, const char Storage[]) { + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) return reinterpret_cast(Storage); return NULL; } static DynTypedNode create(const TypeLoc &Node) { DynTypedNode Result; - Result.Tag = NT_TypeLoc; + Result.NodeKind = ASTNodeKind::getFromNodeKind(); new (Result.Storage.buffer) TypeLoc(Node); return Result; } @@ -213,15 +301,18 @@ template<> struct DynTypedNode::BaseConverter { // AST node that is not supported, but prevents misuse - a user cannot create // a DynTypedNode from arbitrary types. template struct DynTypedNode::BaseConverter { - static const T *get(NodeTypeTag Tag, const char Storage[]) { return NULL; } + static const T *get(ASTNodeKind NodeKind, const char Storage[]) { + return NULL; + } }; inline const void *DynTypedNode::getMemoizationData() const { - switch (Tag) { - case NT_Decl: return BaseConverter::get(Tag, Storage.buffer); - case NT_Stmt: return BaseConverter::get(Tag, Storage.buffer); - default: return NULL; - }; + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) { + return BaseConverter::get(NodeKind, Storage.buffer); + } else if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) { + return BaseConverter::get(NodeKind, Storage.buffer); + } + return NULL; } } // end namespace ast_type_traits diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp new file mode 100644 index 000000000000..40e669d7ad90 --- /dev/null +++ b/clang/lib/AST/ASTTypeTraits.cpp @@ -0,0 +1,56 @@ +//===--- ASTTypeTraits.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Provides a dynamic type identifier and 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTTypeTraits.h" + +namespace clang { +namespace ast_type_traits { + +const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = { + { NKI_None, "" }, + { NKI_None, "NestedNameSpecifier" }, + { NKI_None, "NestedNameSpecifierLoc" }, + { NKI_None, "QualType" }, + { NKI_None, "TypeLoc" }, + { NKI_None, "Decl" }, +#define DECL(DERIVED, BASE) { NKI_##BASE, #DERIVED "Decl" }, +#include "clang/AST/DeclNodes.inc" + { NKI_None, "Stmt" }, +#define STMT(DERIVED, BASE) { NKI_##BASE, #DERIVED }, +#include "clang/AST/StmtNodes.inc" + { NKI_None, "Type" }, +#define TYPE(DERIVED, BASE) { NKI_##BASE, #DERIVED "Type" }, +#include "clang/AST/TypeNodes.def" +}; + +bool ASTNodeKind::isBaseOf(ASTNodeKind Other) { + return isBaseOf(KindId, Other.KindId); +} + +bool ASTNodeKind::isSame(ASTNodeKind Other) { + return KindId != NKI_None && KindId == Other.KindId; +} + +bool ASTNodeKind::isBaseOf(NodeKindId Base, NodeKindId Derived) { + if (Base == NKI_None || Derived == NKI_None) return false; + while (Derived != Base && Derived != NKI_None) + Derived = AllKindInfo[Derived].ParentId; + return Derived == Base; +} + +StringRef ASTNodeKind::asStringRef() const { return AllKindInfo[KindId].Name; } + +} // end namespace ast_type_traits +} // end namespace clang diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index e804fe720558..b50814468482 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_library(clangAST ASTDiagnostic.cpp ASTDumper.cpp ASTImporter.cpp + ASTTypeTraits.cpp AttrImpl.cpp CXXInheritance.cpp Comment.cpp diff --git a/clang/unittests/AST/ASTTypeTraitsTest.cpp b/clang/unittests/AST/ASTTypeTraitsTest.cpp new file mode 100644 index 000000000000..5e3abe25d117 --- /dev/null +++ b/clang/unittests/AST/ASTTypeTraitsTest.cpp @@ -0,0 +1,62 @@ +//===- unittest/AST/ASTTypeTraits.cpp - AST type traits unit tests ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// + + +#include "clang/AST/ASTTypeTraits.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ast_type_traits { + +TEST(ASTNodeKind, NoKind) { + EXPECT_FALSE(ASTNodeKind().isBaseOf(ASTNodeKind())); + EXPECT_FALSE(ASTNodeKind().isSame(ASTNodeKind())); +} + +template static ASTNodeKind DNT() { + return ASTNodeKind::getFromNodeKind(); +} + +TEST(ASTNodeKind, Bases) { + EXPECT_TRUE(DNT().isBaseOf(DNT())); + EXPECT_FALSE(DNT().isSame(DNT())); + EXPECT_FALSE(DNT().isBaseOf(DNT())); + + EXPECT_TRUE(DNT().isSame(DNT())); +} + +TEST(ASTNodeKind, SameBase) { + EXPECT_TRUE(DNT().isBaseOf(DNT())); + EXPECT_TRUE(DNT().isBaseOf(DNT())); + EXPECT_FALSE(DNT().isBaseOf(DNT())); + EXPECT_FALSE(DNT().isBaseOf(DNT())); +} + +TEST(ASTNodeKind, DiffBase) { + EXPECT_FALSE(DNT().isBaseOf(DNT())); + EXPECT_FALSE(DNT().isBaseOf(DNT())); + EXPECT_FALSE(DNT().isSame(DNT())); +} + +struct Foo {}; + +TEST(ASTNodeKind, UnknownKind) { + // We can construct one, but it is nowhere in the hierarchy. + EXPECT_FALSE(DNT().isSame(DNT())); +} + +TEST(ASTNodeKind, Name) { + EXPECT_EQ("Decl", DNT().asStringRef()); + EXPECT_EQ("CallExpr", DNT().asStringRef()); + EXPECT_EQ("ConstantArrayType", DNT().asStringRef()); + EXPECT_EQ("", ASTNodeKind().asStringRef()); +} + +} // namespace ast_type_traits +} // namespace clang diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt index 3ef2a5e1537a..c414ae3b237f 100644 --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -1,5 +1,6 @@ add_clang_unittest(ASTTests ASTContextParentMapTest.cpp + ASTTypeTraitsTest.cpp CommentLexer.cpp CommentParser.cpp DeclPrinterTest.cpp