//===- NestedNameSpecifier.cpp - C++ nested name specifiers ---------------===// // // 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. // //===----------------------------------------------------------------------===// #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DependenceFlags.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include using namespace clang; NestedNameSpecifier * NestedNameSpecifier::FindOrInsert(const ASTContext &Context, const NestedNameSpecifier &Mockup) { llvm::FoldingSetNodeID ID; Mockup.Profile(ID); void *InsertPos = nullptr; NestedNameSpecifier *NNS = Context.NestedNameSpecifiers.FindNodeOrInsertPos(ID, InsertPos); if (!NNS) { NNS = new (Context, alignof(NestedNameSpecifier)) NestedNameSpecifier(Mockup); Context.NestedNameSpecifiers.InsertNode(NNS, InsertPos); } return NNS; } NestedNameSpecifier *NestedNameSpecifier::Create(const ASTContext &Context, NestedNameSpecifier *Prefix, const IdentifierInfo *II) { assert(II && "Identifier cannot be NULL"); assert((!Prefix || Prefix->isDependent()) && "Prefix must be dependent"); NestedNameSpecifier Mockup; Mockup.Prefix.setPointer(Prefix); Mockup.Prefix.setInt(StoredIdentifier); Mockup.Specifier = const_cast(II); return FindOrInsert(Context, Mockup); } NestedNameSpecifier * NestedNameSpecifier::Create(const ASTContext &Context, NestedNameSpecifier *Prefix, const NamespaceDecl *NS) { assert(NS && "Namespace cannot be NULL"); assert((!Prefix || (Prefix->getAsType() == nullptr && Prefix->getAsIdentifier() == nullptr)) && "Broken nested name specifier"); NestedNameSpecifier Mockup; Mockup.Prefix.setPointer(Prefix); Mockup.Prefix.setInt(StoredDecl); Mockup.Specifier = const_cast(NS); return FindOrInsert(Context, Mockup); } NestedNameSpecifier * NestedNameSpecifier::Create(const ASTContext &Context, NestedNameSpecifier *Prefix, const NamespaceAliasDecl *Alias) { assert(Alias && "Namespace alias cannot be NULL"); assert((!Prefix || (Prefix->getAsType() == nullptr && Prefix->getAsIdentifier() == nullptr)) && "Broken nested name specifier"); NestedNameSpecifier Mockup; Mockup.Prefix.setPointer(Prefix); Mockup.Prefix.setInt(StoredDecl); Mockup.Specifier = const_cast(Alias); return FindOrInsert(Context, Mockup); } NestedNameSpecifier * NestedNameSpecifier::Create(const ASTContext &Context, NestedNameSpecifier *Prefix, bool Template, const Type *T) { assert(T && "Type cannot be NULL"); NestedNameSpecifier Mockup; Mockup.Prefix.setPointer(Prefix); Mockup.Prefix.setInt(Template? StoredTypeSpecWithTemplate : StoredTypeSpec); Mockup.Specifier = const_cast(T); return FindOrInsert(Context, Mockup); } NestedNameSpecifier *NestedNameSpecifier::Create(const ASTContext &Context, const IdentifierInfo *II) { assert(II && "Identifier cannot be NULL"); NestedNameSpecifier Mockup; Mockup.Prefix.setPointer(nullptr); Mockup.Prefix.setInt(StoredIdentifier); Mockup.Specifier = const_cast(II); return FindOrInsert(Context, Mockup); } NestedNameSpecifier * NestedNameSpecifier::GlobalSpecifier(const ASTContext &Context) { if (!Context.GlobalNestedNameSpecifier) Context.GlobalNestedNameSpecifier = new (Context, alignof(NestedNameSpecifier)) NestedNameSpecifier(); return Context.GlobalNestedNameSpecifier; } NestedNameSpecifier * NestedNameSpecifier::SuperSpecifier(const ASTContext &Context, CXXRecordDecl *RD) { NestedNameSpecifier Mockup; Mockup.Prefix.setPointer(nullptr); Mockup.Prefix.setInt(StoredDecl); Mockup.Specifier = RD; return FindOrInsert(Context, Mockup); } NestedNameSpecifier::SpecifierKind NestedNameSpecifier::getKind() const { if (!Specifier) return Global; switch (Prefix.getInt()) { case StoredIdentifier: return Identifier; case StoredDecl: { NamedDecl *ND = static_cast(Specifier); if (isa(ND)) return Super; return isa(ND) ? Namespace : NamespaceAlias; } case StoredTypeSpec: return TypeSpec; case StoredTypeSpecWithTemplate: return TypeSpecWithTemplate; } llvm_unreachable("Invalid NNS Kind!"); } /// Retrieve the namespace stored in this nested name specifier. NamespaceDecl *NestedNameSpecifier::getAsNamespace() const { if (Prefix.getInt() == StoredDecl) return dyn_cast(static_cast(Specifier)); return nullptr; } /// Retrieve the namespace alias stored in this nested name specifier. NamespaceAliasDecl *NestedNameSpecifier::getAsNamespaceAlias() const { if (Prefix.getInt() == StoredDecl) return dyn_cast(static_cast(Specifier)); return nullptr; } /// Retrieve the record declaration stored in this nested name specifier. CXXRecordDecl *NestedNameSpecifier::getAsRecordDecl() const { switch (Prefix.getInt()) { case StoredIdentifier: return nullptr; case StoredDecl: return dyn_cast(static_cast(Specifier)); case StoredTypeSpec: case StoredTypeSpecWithTemplate: return getAsType()->getAsCXXRecordDecl(); } llvm_unreachable("Invalid NNS Kind!"); } NestedNameSpecifierDependence NestedNameSpecifier::getDependence() const { switch (getKind()) { case Identifier: { // Identifier specifiers always represent dependent types auto F = NestedNameSpecifierDependence::Dependent | NestedNameSpecifierDependence::Instantiation; // Prefix can contain unexpanded template parameters. if (getPrefix()) return F | getPrefix()->getDependence(); return F; } case Namespace: case NamespaceAlias: case Global: return NestedNameSpecifierDependence::None; case Super: { CXXRecordDecl *RD = static_cast(Specifier); for (const auto &Base : RD->bases()) if (Base.getType()->isDependentType()) // FIXME: must also be instantiation-dependent. return NestedNameSpecifierDependence::Dependent; return NestedNameSpecifierDependence::None; } case TypeSpec: case TypeSpecWithTemplate: return toNestedNameSpecifierDependendence(getAsType()->getDependence()); } llvm_unreachable("Invalid NNS Kind!"); } bool NestedNameSpecifier::isDependent() const { return getDependence() & NestedNameSpecifierDependence::Dependent; } bool NestedNameSpecifier::isInstantiationDependent() const { return getDependence() & NestedNameSpecifierDependence::Instantiation; } bool NestedNameSpecifier::containsUnexpandedParameterPack() const { return getDependence() & NestedNameSpecifierDependence::UnexpandedPack; } bool NestedNameSpecifier::containsErrors() const { return getDependence() & NestedNameSpecifierDependence::Error; } /// Print this nested name specifier to the given output /// stream. void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, bool ResolveTemplateArguments) const { if (getPrefix()) getPrefix()->print(OS, Policy); switch (getKind()) { case Identifier: OS << getAsIdentifier()->getName(); break; case Namespace: if (getAsNamespace()->isAnonymousNamespace()) return; OS << getAsNamespace()->getName(); break; case NamespaceAlias: OS << getAsNamespaceAlias()->getName(); break; case Global: break; case Super: OS << "__super"; break; case TypeSpecWithTemplate: OS << "template "; // Fall through to print the type. [[fallthrough]]; case TypeSpec: { const auto *Record = dyn_cast_or_null(getAsRecordDecl()); if (ResolveTemplateArguments && Record) { // Print the type trait with resolved template parameters. Record->printName(OS, Policy); printTemplateArgumentList( OS, Record->getTemplateArgs().asArray(), Policy, Record->getSpecializedTemplate()->getTemplateParameters()); break; } const Type *T = getAsType(); PrintingPolicy InnerPolicy(Policy); InnerPolicy.SuppressScope = true; // Nested-name-specifiers are intended to contain minimally-qualified // types. An actual ElaboratedType will not occur, since we'll store // just the type that is referred to in the nested-name-specifier (e.g., // a TypedefType, TagType, etc.). However, when we are dealing with // dependent template-id types (e.g., Outer::template Inner), // the type requires its own nested-name-specifier for uniqueness, so we // suppress that nested-name-specifier during printing. assert(!isa(T) && "Elaborated type in nested-name-specifier"); if (const TemplateSpecializationType *SpecType = dyn_cast(T)) { // Print the template name without its corresponding // nested-name-specifier. SpecType->getTemplateName().print(OS, InnerPolicy, TemplateName::Qualified::None); // Print the template argument list. printTemplateArgumentList(OS, SpecType->template_arguments(), InnerPolicy); } else if (const auto *DepSpecType = dyn_cast(T)) { // Print the template name without its corresponding // nested-name-specifier. OS << DepSpecType->getIdentifier()->getName(); // Print the template argument list. printTemplateArgumentList(OS, DepSpecType->template_arguments(), InnerPolicy); } else { // Print the type normally QualType(T, 0).print(OS, InnerPolicy); } break; } } OS << "::"; } LLVM_DUMP_METHOD void NestedNameSpecifier::dump(const LangOptions &LO) const { dump(llvm::errs(), LO); } LLVM_DUMP_METHOD void NestedNameSpecifier::dump() const { dump(llvm::errs()); } LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream &OS) const { LangOptions LO; dump(OS, LO); } LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream &OS, const LangOptions &LO) const { print(OS, PrintingPolicy(LO)); } unsigned NestedNameSpecifierLoc::getLocalDataLength(NestedNameSpecifier *Qualifier) { assert(Qualifier && "Expected a non-NULL qualifier"); // Location of the trailing '::'. unsigned Length = sizeof(SourceLocation::UIntTy); switch (Qualifier->getKind()) { case NestedNameSpecifier::Global: // Nothing more to add. break; case NestedNameSpecifier::Identifier: case NestedNameSpecifier::Namespace: case NestedNameSpecifier::NamespaceAlias: case NestedNameSpecifier::Super: // The location of the identifier or namespace name. Length += sizeof(SourceLocation::UIntTy); break; case NestedNameSpecifier::TypeSpecWithTemplate: case NestedNameSpecifier::TypeSpec: // The "void*" that points at the TypeLoc data. // Note: the 'template' keyword is part of the TypeLoc. Length += sizeof(void *); break; } return Length; } unsigned NestedNameSpecifierLoc::getDataLength(NestedNameSpecifier *Qualifier) { unsigned Length = 0; for (; Qualifier; Qualifier = Qualifier->getPrefix()) Length += getLocalDataLength(Qualifier); return Length; } /// Load a (possibly unaligned) source location from a given address /// and offset. static SourceLocation LoadSourceLocation(void *Data, unsigned Offset) { 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. static void *LoadPointer(void *Data, unsigned Offset) { void *Result; memcpy(&Result, static_cast(Data) + Offset, sizeof(void*)); return Result; } SourceRange NestedNameSpecifierLoc::getSourceRange() const { if (!Qualifier) return SourceRange(); NestedNameSpecifierLoc First = *this; while (NestedNameSpecifierLoc Prefix = First.getPrefix()) First = Prefix; return SourceRange(First.getLocalSourceRange().getBegin(), getLocalSourceRange().getEnd()); } SourceRange NestedNameSpecifierLoc::getLocalSourceRange() const { if (!Qualifier) return SourceRange(); unsigned Offset = getDataLength(Qualifier->getPrefix()); switch (Qualifier->getKind()) { case NestedNameSpecifier::Global: return LoadSourceLocation(Data, Offset); case NestedNameSpecifier::Identifier: case NestedNameSpecifier::Namespace: case NestedNameSpecifier::NamespaceAlias: case NestedNameSpecifier::Super: return SourceRange( LoadSourceLocation(Data, Offset), LoadSourceLocation(Data, Offset + sizeof(SourceLocation::UIntTy))); case NestedNameSpecifier::TypeSpecWithTemplate: case NestedNameSpecifier::TypeSpec: { // The "void*" that points at the TypeLoc data. // Note: the 'template' keyword is part of the TypeLoc. void *TypeData = LoadPointer(Data, Offset); TypeLoc TL(Qualifier->getAsType(), TypeData); return SourceRange(TL.getBeginLoc(), LoadSourceLocation(Data, Offset + sizeof(void*))); } } llvm_unreachable("Invalid NNS Kind!"); } TypeLoc NestedNameSpecifierLoc::getTypeLoc() const { if (Qualifier->getKind() != NestedNameSpecifier::TypeSpec && Qualifier->getKind() != NestedNameSpecifier::TypeSpecWithTemplate) return TypeLoc(); // The "void*" that points at the TypeLoc data. unsigned Offset = getDataLength(Qualifier->getPrefix()); void *TypeData = LoadPointer(Data, Offset); return TypeLoc(Qualifier->getAsType(), TypeData); } static void Append(char *Start, char *End, char *&Buffer, unsigned &BufferSize, unsigned &BufferCapacity) { if (Start == End) return; if (BufferSize + (End - Start) > BufferCapacity) { // Reallocate the buffer. unsigned NewCapacity = std::max( (unsigned)(BufferCapacity ? BufferCapacity * 2 : sizeof(void *) * 2), (unsigned)(BufferSize + (End - Start))); if (!BufferCapacity) { char *NewBuffer = static_cast(llvm::safe_malloc(NewCapacity)); if (Buffer) memcpy(NewBuffer, Buffer, BufferSize); Buffer = NewBuffer; } else { Buffer = static_cast(llvm::safe_realloc(Buffer, NewCapacity)); } BufferCapacity = NewCapacity; } assert(Buffer && Start && End && End > Start && "Illegal memory buffer copy"); memcpy(Buffer + BufferSize, Start, End - Start); BufferSize += End - Start; } /// Save a source location to the given buffer. static void SaveSourceLocation(SourceLocation Loc, char *&Buffer, unsigned &BufferSize, unsigned &BufferCapacity) { SourceLocation::UIntTy Raw = Loc.getRawEncoding(); Append(reinterpret_cast(&Raw), reinterpret_cast(&Raw) + sizeof(Raw), Buffer, BufferSize, BufferCapacity); } /// Save a pointer to the given buffer. static void SavePointer(void *Ptr, char *&Buffer, unsigned &BufferSize, unsigned &BufferCapacity) { Append(reinterpret_cast(&Ptr), reinterpret_cast(&Ptr) + sizeof(void *), Buffer, BufferSize, BufferCapacity); } NestedNameSpecifierLocBuilder:: NestedNameSpecifierLocBuilder(const NestedNameSpecifierLocBuilder &Other) : Representation(Other.Representation) { if (!Other.Buffer) return; if (Other.BufferCapacity == 0) { // Shallow copy is okay. Buffer = Other.Buffer; BufferSize = Other.BufferSize; return; } // Deep copy Append(Other.Buffer, Other.Buffer + Other.BufferSize, Buffer, BufferSize, BufferCapacity); } NestedNameSpecifierLocBuilder & NestedNameSpecifierLocBuilder:: operator=(const NestedNameSpecifierLocBuilder &Other) { Representation = Other.Representation; if (Buffer && Other.Buffer && BufferCapacity >= Other.BufferSize) { // Re-use our storage. BufferSize = Other.BufferSize; memcpy(Buffer, Other.Buffer, BufferSize); return *this; } // Free our storage, if we have any. if (BufferCapacity) { free(Buffer); BufferCapacity = 0; } if (!Other.Buffer) { // Empty. Buffer = nullptr; BufferSize = 0; return *this; } if (Other.BufferCapacity == 0) { // Shallow copy is okay. Buffer = Other.Buffer; BufferSize = Other.BufferSize; return *this; } // Deep copy. BufferSize = 0; Append(Other.Buffer, Other.Buffer + Other.BufferSize, Buffer, BufferSize, BufferCapacity); return *this; } void NestedNameSpecifierLocBuilder::Extend(ASTContext &Context, SourceLocation TemplateKWLoc, TypeLoc TL, SourceLocation ColonColonLoc) { Representation = NestedNameSpecifier::Create(Context, Representation, TemplateKWLoc.isValid(), TL.getTypePtr()); // Push source-location info into the buffer. SavePointer(TL.getOpaqueData(), Buffer, BufferSize, BufferCapacity); SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::Extend(ASTContext &Context, IdentifierInfo *Identifier, SourceLocation IdentifierLoc, SourceLocation ColonColonLoc) { Representation = NestedNameSpecifier::Create(Context, Representation, Identifier); // Push source-location info into the buffer. SaveSourceLocation(IdentifierLoc, Buffer, BufferSize, BufferCapacity); SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::Extend(ASTContext &Context, NamespaceDecl *Namespace, SourceLocation NamespaceLoc, SourceLocation ColonColonLoc) { Representation = NestedNameSpecifier::Create(Context, Representation, Namespace); // Push source-location info into the buffer. SaveSourceLocation(NamespaceLoc, Buffer, BufferSize, BufferCapacity); SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::Extend(ASTContext &Context, NamespaceAliasDecl *Alias, SourceLocation AliasLoc, SourceLocation ColonColonLoc) { Representation = NestedNameSpecifier::Create(Context, Representation, Alias); // Push source-location info into the buffer. SaveSourceLocation(AliasLoc, Buffer, BufferSize, BufferCapacity); SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::MakeGlobal(ASTContext &Context, SourceLocation ColonColonLoc) { assert(!Representation && "Already have a nested-name-specifier!?"); Representation = NestedNameSpecifier::GlobalSpecifier(Context); // Push source-location info into the buffer. SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::MakeSuper(ASTContext &Context, CXXRecordDecl *RD, SourceLocation SuperLoc, SourceLocation ColonColonLoc) { Representation = NestedNameSpecifier::SuperSpecifier(Context, RD); // Push source-location info into the buffer. SaveSourceLocation(SuperLoc, Buffer, BufferSize, BufferCapacity); SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::MakeTrivial(ASTContext &Context, NestedNameSpecifier *Qualifier, SourceRange R) { Representation = Qualifier; // Construct bogus (but well-formed) source information for the // nested-name-specifier. BufferSize = 0; SmallVector Stack; for (NestedNameSpecifier *NNS = Qualifier; NNS; NNS = NNS->getPrefix()) Stack.push_back(NNS); while (!Stack.empty()) { NestedNameSpecifier *NNS = Stack.pop_back_val(); switch (NNS->getKind()) { case NestedNameSpecifier::Identifier: case NestedNameSpecifier::Namespace: case NestedNameSpecifier::NamespaceAlias: SaveSourceLocation(R.getBegin(), Buffer, BufferSize, BufferCapacity); break; case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::TypeSpecWithTemplate: { TypeSourceInfo *TSInfo = Context.getTrivialTypeSourceInfo(QualType(NNS->getAsType(), 0), R.getBegin()); SavePointer(TSInfo->getTypeLoc().getOpaqueData(), Buffer, BufferSize, BufferCapacity); break; } case NestedNameSpecifier::Global: case NestedNameSpecifier::Super: break; } // Save the location of the '::'. SaveSourceLocation(Stack.empty()? R.getEnd() : R.getBegin(), Buffer, BufferSize, BufferCapacity); } } void NestedNameSpecifierLocBuilder::Adopt(NestedNameSpecifierLoc Other) { if (BufferCapacity) free(Buffer); if (!Other) { Representation = nullptr; BufferSize = 0; return; } // Rather than copying the data (which is wasteful), "adopt" the // pointer (which points into the ASTContext) but set the capacity to zero to // indicate that we don't own it. Representation = Other.getNestedNameSpecifier(); Buffer = static_cast(Other.getOpaqueData()); BufferSize = Other.getDataLength(); BufferCapacity = 0; } NestedNameSpecifierLoc NestedNameSpecifierLocBuilder::getWithLocInContext(ASTContext &Context) const { if (!Representation) return NestedNameSpecifierLoc(); // If we adopted our data pointer from elsewhere in the AST context, there's // no need to copy the memory. if (BufferCapacity == 0) return NestedNameSpecifierLoc(Representation, Buffer); // FIXME: After copying the source-location information, should we free // our (temporary) buffer and adopt the ASTContext-allocated memory? // Doing so would optimize repeated calls to getWithLocInContext(). void *Mem = Context.Allocate(BufferSize, alignof(void *)); memcpy(Mem, Buffer, BufferSize); return NestedNameSpecifierLoc(Representation, Mem); }