//===- NestedNameSpecifier.h - C++ nested name specifiers -------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the NestedNameSpecifier class, which represents // a C++ nested-name-specifier. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_AST_NESTEDNAMESPECIFIERBASE_H #define LLVM_CLANG_AST_NESTEDNAMESPECIFIERBASE_H #include "clang/AST/DependenceFlags.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include #include #include namespace clang { class ASTContext; class CXXRecordDecl; class NamedDecl; class IdentifierInfo; class LangOptions; class NamespaceBaseDecl; struct PrintingPolicy; class Type; class TypeLoc; struct NamespaceAndPrefix; struct alignas(8) NamespaceAndPrefixStorage; /// Represents a C++ nested name specifier, such as /// "\::std::vector::". /// /// C++ nested name specifiers are the prefixes to qualified /// names. For example, "foo::" in "foo::x" is a nested name /// specifier. Nested name specifiers are made up of a sequence of /// specifiers, each of which can be a namespace, type, decltype specifier, or /// the global specifier ('::'). The last two specifiers can only appear at the /// start of a nested-namespace-specifier. class NestedNameSpecifier { enum class FlagKind { Null, Global, Invalid }; enum class StoredKind { Type, NamespaceOrSuper, NamespaceWithGlobal, NamespaceWithNamespace }; static constexpr uintptr_t FlagBits = 2, FlagMask = (1u << FlagBits) - 1u, FlagOffset = 1, PtrOffset = FlagBits + FlagOffset, PtrMask = (1u << PtrOffset) - 1u; uintptr_t StoredOrFlag; explicit NestedNameSpecifier(uintptr_t StoredOrFlag) : StoredOrFlag(StoredOrFlag) {} struct PtrKind { StoredKind SK; const void *Ptr; }; explicit NestedNameSpecifier(PtrKind PK) : StoredOrFlag(uintptr_t(PK.Ptr) | (uintptr_t(PK.SK) << FlagOffset)) { assert(PK.Ptr != nullptr); assert((uintptr_t(PK.Ptr) & ((1u << PtrOffset) - 1u)) == 0); assert((uintptr_t(PK.Ptr) >> PtrOffset) != 0); } explicit constexpr NestedNameSpecifier(FlagKind K) : StoredOrFlag(uintptr_t(K) << FlagOffset) {} bool isStoredKind() const { return (StoredOrFlag >> PtrOffset) != 0; } std::pair getStored() const { assert(isStoredKind()); return {StoredKind(StoredOrFlag >> FlagOffset & FlagMask), reinterpret_cast(StoredOrFlag & ~PtrMask)}; } FlagKind getFlagKind() const { assert(!isStoredKind()); return FlagKind(StoredOrFlag >> FlagOffset); } static const NamespaceAndPrefixStorage * MakeNamespaceAndPrefixStorage(const ASTContext &Ctx, const NamespaceBaseDecl *Namespace, NestedNameSpecifier Prefix); static inline PtrKind MakeNamespacePtrKind(const ASTContext &Ctx, const NamespaceBaseDecl *Namespace, NestedNameSpecifier Prefix); public: static constexpr NestedNameSpecifier getInvalid() { return NestedNameSpecifier(FlagKind::Invalid); } static constexpr NestedNameSpecifier getGlobal() { return NestedNameSpecifier(FlagKind::Global); } NestedNameSpecifier() : NestedNameSpecifier(FlagKind::Invalid) {} /// The kind of specifier that completes this nested name /// specifier. enum class Kind { /// Empty. Null, /// The global specifier '::'. There is no stored value. Global, /// A type, stored as a Type*. Type, /// A namespace-like entity, stored as a NamespaceBaseDecl*. Namespace, /// Microsoft's '__super' specifier, stored as a CXXRecordDecl* of /// the class it appeared in. MicrosoftSuper, }; inline Kind getKind() const; NestedNameSpecifier(std::nullopt_t) : StoredOrFlag(0) {} explicit inline NestedNameSpecifier(const Type *T); /// Builds a nested name specifier that names a namespace. inline NestedNameSpecifier(const ASTContext &Ctx, const NamespaceBaseDecl *Namespace, NestedNameSpecifier Prefix); /// Builds a nested name specifier that names a class through microsoft's /// __super specifier. explicit inline NestedNameSpecifier(CXXRecordDecl *RD); explicit operator bool() const { return StoredOrFlag != 0; } void *getAsVoidPointer() const { return reinterpret_cast(StoredOrFlag); } static NestedNameSpecifier getFromVoidPointer(const void *Ptr) { return NestedNameSpecifier(reinterpret_cast(Ptr)); } const Type *getAsType() const { auto [Kind, Ptr] = getStored(); assert(Kind == StoredKind::Type); assert(Ptr != nullptr); return static_cast(Ptr); } inline NamespaceAndPrefix getAsNamespaceAndPrefix() const; CXXRecordDecl *getAsMicrosoftSuper() const { auto [Kind, Ptr] = getStored(); assert(Kind == StoredKind::NamespaceOrSuper); assert(Ptr != nullptr); return static_cast(const_cast(Ptr)); } /// Retrieve the record declaration stored in this nested name /// specifier, or null. inline CXXRecordDecl *getAsRecordDecl() const; friend bool operator==(NestedNameSpecifier LHS, NestedNameSpecifier RHS) { return LHS.StoredOrFlag == RHS.StoredOrFlag; } friend bool operator!=(NestedNameSpecifier LHS, NestedNameSpecifier RHS) { return LHS.StoredOrFlag != RHS.StoredOrFlag; } /// Retrieves the "canonical" nested name specifier for a /// given nested name specifier. /// /// The canonical nested name specifier is a nested name specifier /// that uniquely identifies a type or namespace within the type /// system. For example, given: /// /// \code /// namespace N { /// struct S { /// template struct X { typename T* type; }; /// }; /// } /// /// template struct Y { /// typename N::S::X::type member; /// }; /// \endcode /// /// Here, the nested-name-specifier for N::S::X:: will be /// S::X, since 'S' and 'X' are uniquely defined /// by declarations in the type system and the canonical type for /// the template type parameter 'T' is template-param-0-0. inline NestedNameSpecifier getCanonical() const; /// Whether this nested name specifier is canonical. inline bool isCanonical() const; /// Whether this nested name specifier starts with a '::'. bool isFullyQualified() const; NestedNameSpecifierDependence getDependence() const; /// Whether this nested name specifier refers to a dependent /// type or not. bool isDependent() const { return getDependence() & NestedNameSpecifierDependence::Dependent; } /// Whether this nested name specifier involves a template /// parameter. bool isInstantiationDependent() const { return getDependence() & NestedNameSpecifierDependence::Instantiation; } /// Whether this nested-name-specifier contains an unexpanded /// parameter pack (for C++11 variadic templates). bool containsUnexpandedParameterPack() const { return getDependence() & NestedNameSpecifierDependence::UnexpandedPack; } /// Whether this nested name specifier contains an error. bool containsErrors() const { return getDependence() & NestedNameSpecifierDependence::Error; } /// Print this nested name specifier to the given output stream. If /// `ResolveTemplateArguments` is true, we'll print actual types, e.g. /// `ns::SomeTemplate` instead of /// `ns::SomeTemplate`. void print(raw_ostream &OS, const PrintingPolicy &Policy, bool ResolveTemplateArguments = false, bool PrintFinalScopeResOp = true) const; void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(StoredOrFlag); } /// Dump the nested name specifier to aid in debugging. void dump(llvm::raw_ostream *OS = nullptr, const LangOptions *LO = nullptr) const; void dump(const LangOptions &LO) const; void dump(llvm::raw_ostream &OS) const; void dump(llvm::raw_ostream &OS, const LangOptions &LO) const; static constexpr auto NumLowBitsAvailable = FlagOffset; }; struct NamespaceAndPrefix { const NamespaceBaseDecl *Namespace; NestedNameSpecifier Prefix; }; struct alignas(8) NamespaceAndPrefixStorage : NamespaceAndPrefix, llvm::FoldingSetNode { NamespaceAndPrefixStorage(const NamespaceBaseDecl *Namespace, NestedNameSpecifier Prefix) : NamespaceAndPrefix{Namespace, Prefix} {} void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, Namespace, Prefix); } static void Profile(llvm::FoldingSetNodeID &ID, const NamespaceBaseDecl *Namespace, NestedNameSpecifier Prefix) { ID.AddPointer(Namespace); Prefix.Profile(ID); } }; NamespaceAndPrefix NestedNameSpecifier::getAsNamespaceAndPrefix() const { auto [Kind, Ptr] = getStored(); switch (Kind) { case StoredKind::NamespaceOrSuper: case StoredKind::NamespaceWithGlobal: return {static_cast(Ptr), Kind == StoredKind::NamespaceWithGlobal ? NestedNameSpecifier::getGlobal() : std::nullopt}; case StoredKind::NamespaceWithNamespace: return *static_cast(Ptr); case StoredKind::Type:; } llvm_unreachable("unexpected stored kind"); } struct NamespaceAndPrefixLoc; /// A C++ nested-name-specifier augmented with source location /// information. class NestedNameSpecifierLoc { NestedNameSpecifier Qualifier = std::nullopt; void *Data = nullptr; /// Load a (possibly unaligned) source location from a given address /// and offset. SourceLocation LoadSourceLocation(unsigned Offset) const { SourceLocation::UIntTy Raw; memcpy(&Raw, static_cast(Data) + Offset, sizeof(Raw)); return SourceLocation::getFromRawEncoding(Raw); } /// Load a (possibly unaligned) pointer from a given address and /// offset. void *LoadPointer(unsigned Offset) const { void *Result; memcpy(&Result, static_cast(Data) + Offset, sizeof(void *)); return Result; } /// Determines the data length for the last component in the /// given nested-name-specifier. static inline unsigned getLocalDataLength(NestedNameSpecifier Qualifier); /// Determines the data length for the entire /// nested-name-specifier. static inline unsigned getDataLength(NestedNameSpecifier Qualifier); public: /// Construct an empty nested-name-specifier. NestedNameSpecifierLoc() = default; /// Construct a nested-name-specifier with source location information /// from NestedNameSpecifierLoc(NestedNameSpecifier Qualifier, void *Data) : Qualifier(Qualifier), Data(Data) {} /// Evaluates true when this nested-name-specifier location is /// non-empty. explicit operator bool() const { return bool(Qualifier); } /// Evaluates true when this nested-name-specifier location is /// non-empty. bool hasQualifier() const { return bool(Qualifier); } /// Retrieve the nested-name-specifier to which this instance /// refers. NestedNameSpecifier getNestedNameSpecifier() const { return Qualifier; } /// Retrieve the opaque pointer that refers to source-location data. void *getOpaqueData() const { return Data; } /// Retrieve the source range covering the entirety of this /// nested-name-specifier. /// /// For example, if this instance refers to a nested-name-specifier /// \c \::std::vector::, the returned source range would cover /// from the initial '::' to the last '::'. inline SourceRange getSourceRange() const LLVM_READONLY; /// Retrieve the source range covering just the last part of /// this nested-name-specifier, not including the prefix. /// /// Note that this is the source range of this NestedNameSpecifier chunk, /// and for a type this includes the prefix of that type. /// /// For example, if this instance refers to a nested-name-specifier /// \c \::std::vector::, the returned source range would cover /// from "vector" to the last '::'. inline SourceRange getLocalSourceRange() const; /// Retrieve the location of the beginning of this /// nested-name-specifier. SourceLocation getBeginLoc() const; /// Retrieve the location of the end of this /// nested-name-specifier. inline SourceLocation getEndLoc() const; /// Retrieve the location of the beginning of this /// component of the nested-name-specifier. inline SourceLocation getLocalBeginLoc() const; /// Retrieve the location of the end of this component of the /// nested-name-specifier. inline SourceLocation getLocalEndLoc() const; /// For a nested-name-specifier that refers to a namespace, /// retrieve the namespace and its prefix. /// /// For example, if this instance refers to a nested-name-specifier /// \c \::std::chrono::, the prefix is \c \::std::. Note that the /// returned prefix may be empty, if this is the first component of /// the nested-name-specifier. inline NamespaceAndPrefixLoc castAsNamespaceAndPrefix() const; inline NamespaceAndPrefixLoc getAsNamespaceAndPrefix() const; /// For a nested-name-specifier that refers to a type, /// retrieve the type with source-location information. inline TypeLoc castAsTypeLoc() const; inline TypeLoc getAsTypeLoc() const; /// Determines the data length for the entire /// nested-name-specifier. inline unsigned getDataLength() const; friend bool operator==(NestedNameSpecifierLoc X, NestedNameSpecifierLoc Y) { return X.Qualifier == Y.Qualifier && X.Data == Y.Data; } friend bool operator!=(NestedNameSpecifierLoc X, NestedNameSpecifierLoc Y) { return !(X == Y); } }; struct NamespaceAndPrefixLoc { const NamespaceBaseDecl *Namespace = nullptr; NestedNameSpecifierLoc Prefix; explicit operator bool() const { return Namespace != nullptr; } }; /// Class that aids in the construction of nested-name-specifiers along /// with source-location information for all of the components of the /// nested-name-specifier. class NestedNameSpecifierLocBuilder { /// The current representation of the nested-name-specifier we're /// building. NestedNameSpecifier Representation = std::nullopt; /// Buffer used to store source-location information for the /// nested-name-specifier. /// /// Note that we explicitly manage the buffer (rather than using a /// SmallVector) because \c Declarator expects it to be possible to memcpy() /// a \c CXXScopeSpec, and CXXScopeSpec uses a NestedNameSpecifierLocBuilder. char *Buffer = nullptr; /// The size of the buffer used to store source-location information /// for the nested-name-specifier. unsigned BufferSize = 0; /// The capacity of the buffer used to store source-location /// information for the nested-name-specifier. unsigned BufferCapacity = 0; void PushTrivial(ASTContext &Context, NestedNameSpecifier Qualifier, SourceRange R); public: NestedNameSpecifierLocBuilder() = default; NestedNameSpecifierLocBuilder(const NestedNameSpecifierLocBuilder &Other); NestedNameSpecifierLocBuilder & operator=(const NestedNameSpecifierLocBuilder &Other); ~NestedNameSpecifierLocBuilder() { if (BufferCapacity) free(Buffer); } /// Retrieve the representation of the nested-name-specifier. NestedNameSpecifier getRepresentation() const { return Representation; } /// Make a nested-name-specifier of the form 'type::'. /// /// \param Context The AST context in which this nested-name-specifier /// resides. /// /// \param TL The TypeLoc that describes the type preceding the '::'. /// /// \param ColonColonLoc The location of the trailing '::'. void Make(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc); /// Extend the current nested-name-specifier by another /// nested-name-specifier component of the form 'namespace::'. /// /// \param Context The AST context in which this nested-name-specifier /// resides. /// /// \param Namespace The namespace. /// /// \param NamespaceLoc The location of the namespace name. /// /// \param ColonColonLoc The location of the trailing '::'. void Extend(ASTContext &Context, const NamespaceBaseDecl *Namespace, SourceLocation NamespaceLoc, SourceLocation ColonColonLoc); /// Turn this (empty) nested-name-specifier into the global /// nested-name-specifier '::'. void MakeGlobal(ASTContext &Context, SourceLocation ColonColonLoc); /// Turns this (empty) nested-name-specifier into '__super' /// nested-name-specifier. /// /// \param Context The AST context in which this nested-name-specifier /// resides. /// /// \param RD The declaration of the class in which nested-name-specifier /// appeared. /// /// \param SuperLoc The location of the '__super' keyword. /// name. /// /// \param ColonColonLoc The location of the trailing '::'. void MakeMicrosoftSuper(ASTContext &Context, CXXRecordDecl *RD, SourceLocation SuperLoc, SourceLocation ColonColonLoc); /// Make a new nested-name-specifier from incomplete source-location /// information. /// /// This routine should be used very, very rarely, in cases where we /// need to synthesize a nested-name-specifier. Most code should instead use /// \c Adopt() with a proper \c NestedNameSpecifierLoc. void MakeTrivial(ASTContext &Context, NestedNameSpecifier Qualifier, SourceRange R) { Representation = Qualifier; BufferSize = 0; PushTrivial(Context, Qualifier, R); } /// Adopt an existing nested-name-specifier (with source-range /// information). void Adopt(NestedNameSpecifierLoc Other); /// Retrieve the source range covered by this nested-name-specifier. inline SourceRange getSourceRange() const LLVM_READONLY; /// Retrieve a nested-name-specifier with location information, /// copied into the given AST context. /// /// \param Context The context into which this nested-name-specifier will be /// copied. NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const; /// Retrieve a nested-name-specifier with location /// information based on the information in this builder. /// /// This loc will contain references to the builder's internal data and may /// be invalidated by any change to the builder. NestedNameSpecifierLoc getTemporary() const { return NestedNameSpecifierLoc(Representation, Buffer); } /// Clear out this builder, and prepare it to build another /// nested-name-specifier with source-location information. void Clear() { Representation = std::nullopt; BufferSize = 0; } /// Retrieve the underlying buffer. /// /// \returns A pair containing a pointer to the buffer of source-location /// data and the size of the source-location data that resides in that /// buffer. std::pair getBuffer() const { return std::make_pair(Buffer, BufferSize); } }; /// Insertion operator for diagnostics. This allows sending /// NestedNameSpecifiers into a diagnostic with <<. inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, NestedNameSpecifier NNS) { DB.AddTaggedVal(reinterpret_cast(NNS.getAsVoidPointer()), DiagnosticsEngine::ak_nestednamespec); return DB; } } // namespace clang namespace llvm { template <> struct PointerLikeTypeTraits { static void *getAsVoidPointer(clang::NestedNameSpecifier P) { return P.getAsVoidPointer(); } static clang::NestedNameSpecifier getFromVoidPointer(const void *P) { return clang::NestedNameSpecifier::getFromVoidPointer(P); } static constexpr int NumLowBitsAvailable = clang::NestedNameSpecifier::NumLowBitsAvailable; }; } // namespace llvm #endif // LLVM_CLANG_AST_NESTEDNAMESPECIFIERBASE_H