This avoids a deep copy of the manually managed underlying Buffer. This is a follow-up to #180482.
376 lines
12 KiB
C++
376 lines
12 KiB
C++
//===- 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/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/Support/Compiler.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
using namespace clang;
|
|
|
|
const NamespaceAndPrefixStorage *
|
|
NestedNameSpecifier::MakeNamespaceAndPrefixStorage(
|
|
const ASTContext &Ctx, const NamespaceBaseDecl *Namespace,
|
|
NestedNameSpecifier Prefix) {
|
|
llvm::FoldingSetNodeID ID;
|
|
NamespaceAndPrefixStorage::Profile(ID, Namespace, Prefix);
|
|
|
|
void *InsertPos = nullptr;
|
|
NamespaceAndPrefixStorage *S =
|
|
Ctx.NamespaceAndPrefixStorages.FindNodeOrInsertPos(ID, InsertPos);
|
|
if (!S) {
|
|
S = new (Ctx, alignof(NamespaceAndPrefixStorage))
|
|
NamespaceAndPrefixStorage(Namespace, Prefix);
|
|
Ctx.NamespaceAndPrefixStorages.InsertNode(S, InsertPos);
|
|
}
|
|
return S;
|
|
}
|
|
|
|
bool NestedNameSpecifier::isFullyQualified() const {
|
|
switch (getKind()) {
|
|
case NestedNameSpecifier::Kind::Global:
|
|
return true;
|
|
case NestedNameSpecifier::Kind::Null:
|
|
case NestedNameSpecifier::Kind::MicrosoftSuper:
|
|
return false;
|
|
case NestedNameSpecifier::Kind::Namespace:
|
|
return getAsNamespaceAndPrefix().Prefix.isFullyQualified();
|
|
case NestedNameSpecifier::Kind::Type:
|
|
return getAsType()->getPrefix().isFullyQualified();
|
|
}
|
|
llvm_unreachable("Invalid NNS Kind!");
|
|
}
|
|
|
|
NestedNameSpecifierDependence NestedNameSpecifier::getDependence() const {
|
|
switch (getKind()) {
|
|
case Kind::Null:
|
|
case Kind::Global:
|
|
case Kind::Namespace:
|
|
return NestedNameSpecifierDependence::None;
|
|
case Kind::MicrosoftSuper: {
|
|
CXXRecordDecl *RD = getAsMicrosoftSuper();
|
|
return RD->isDependentContext()
|
|
? NestedNameSpecifierDependence::DependentInstantiation |
|
|
NestedNameSpecifierDependence::Dependent
|
|
: NestedNameSpecifierDependence::None;
|
|
}
|
|
case Kind::Type:
|
|
return toNestedNameSpecifierDependence(getAsType()->getDependence());
|
|
}
|
|
llvm_unreachable("Invalid NNS Kind!");
|
|
}
|
|
|
|
/// Print this nested name specifier to the given output
|
|
/// stream.
|
|
void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,
|
|
bool ResolveTemplateArguments,
|
|
bool PrintFinalScopeResOp) const {
|
|
switch (getKind()) {
|
|
case Kind::Namespace: {
|
|
auto [Namespace, Prefix] = getAsNamespaceAndPrefix();
|
|
Prefix.print(OS, Policy);
|
|
if (const auto *NS = dyn_cast<NamespaceDecl>(Namespace)) {
|
|
assert(!NS->isAnonymousNamespace());
|
|
OS << NS->getName();
|
|
} else {
|
|
OS << cast<NamespaceAliasDecl>(Namespace)->getName();
|
|
}
|
|
break;
|
|
}
|
|
case Kind::Global:
|
|
OS << "::";
|
|
return;
|
|
case Kind::MicrosoftSuper:
|
|
OS << "__super";
|
|
break;
|
|
case Kind::Type: {
|
|
PrintingPolicy InnerPolicy(Policy);
|
|
InnerPolicy.SuppressTagKeyword = true;
|
|
QualType(getAsType(), 0).print(OS, InnerPolicy);
|
|
break;
|
|
}
|
|
case Kind::Null:
|
|
return;
|
|
}
|
|
if (PrintFinalScopeResOp)
|
|
OS << "::";
|
|
}
|
|
|
|
LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream *OS,
|
|
const LangOptions *LO) const {
|
|
print(OS ? *OS : llvm::errs(), LO ? *LO : LangOptions());
|
|
}
|
|
|
|
LLVM_DUMP_METHOD void NestedNameSpecifier::dump(const LangOptions &LO) const {
|
|
dump(/*OS=*/nullptr, &LO);
|
|
}
|
|
LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream &OS) const {
|
|
dump(&OS);
|
|
}
|
|
LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream &OS,
|
|
const LangOptions &LO) const {
|
|
dump(&OS, &LO);
|
|
}
|
|
|
|
SourceLocation NestedNameSpecifierLoc::getBeginLoc() const {
|
|
if (!Qualifier)
|
|
return SourceLocation();
|
|
|
|
NestedNameSpecifierLoc First = *this;
|
|
while (NestedNameSpecifierLoc Prefix = First.getAsNamespaceAndPrefix().Prefix)
|
|
First = Prefix;
|
|
return First.getLocalSourceRange().getBegin();
|
|
}
|
|
|
|
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<char *>(llvm::safe_malloc(NewCapacity));
|
|
if (Buffer)
|
|
memcpy(NewBuffer, Buffer, BufferSize);
|
|
Buffer = NewBuffer;
|
|
} else {
|
|
Buffer = static_cast<char *>(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<char *>(&Raw),
|
|
reinterpret_cast<char *>(&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<char *>(&Ptr),
|
|
reinterpret_cast<char *>(&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(
|
|
NestedNameSpecifierLocBuilder &&Other)
|
|
: Representation(std::move(Other.Representation)),
|
|
Buffer(std::exchange(Other.Buffer, nullptr)),
|
|
BufferSize(std::exchange(Other.BufferSize, 0)),
|
|
BufferCapacity(std::exchange(Other.BufferCapacity, 0)) {}
|
|
|
|
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;
|
|
}
|
|
|
|
NestedNameSpecifierLocBuilder &NestedNameSpecifierLocBuilder::operator=(
|
|
NestedNameSpecifierLocBuilder &&Other) {
|
|
Representation = std::move(Other.Representation);
|
|
|
|
// Free our storage, if we have any.
|
|
if (BufferCapacity) {
|
|
free(Buffer);
|
|
}
|
|
Buffer = std::exchange(Other.Buffer, nullptr);
|
|
BufferSize = std::exchange(Other.BufferSize, 0);
|
|
BufferCapacity = std::exchange(Other.BufferCapacity, 0);
|
|
|
|
return *this;
|
|
}
|
|
|
|
void NestedNameSpecifierLocBuilder::Make(ASTContext &Context, TypeLoc TL,
|
|
SourceLocation ColonColonLoc) {
|
|
assert(!Representation);
|
|
Representation = NestedNameSpecifier(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,
|
|
const NamespaceBaseDecl *Namespace,
|
|
SourceLocation NamespaceLoc,
|
|
SourceLocation ColonColonLoc) {
|
|
Representation = NestedNameSpecifier(Context, Namespace, Representation);
|
|
|
|
// Push source-location info into the buffer.
|
|
SaveSourceLocation(NamespaceLoc, 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::getGlobal();
|
|
|
|
// Push source-location info into the buffer.
|
|
SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity);
|
|
}
|
|
|
|
void NestedNameSpecifierLocBuilder::MakeMicrosoftSuper(
|
|
ASTContext &Context, CXXRecordDecl *RD, SourceLocation SuperLoc,
|
|
SourceLocation ColonColonLoc) {
|
|
Representation = NestedNameSpecifier(RD);
|
|
|
|
// Push source-location info into the buffer.
|
|
SaveSourceLocation(SuperLoc, Buffer, BufferSize, BufferCapacity);
|
|
SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity);
|
|
}
|
|
|
|
void NestedNameSpecifierLocBuilder::PushTrivial(ASTContext &Context,
|
|
NestedNameSpecifier Qualifier,
|
|
SourceRange R) {
|
|
// Construct bogus (but well-formed) source information for the
|
|
// nested-name-specifier.
|
|
switch (Qualifier.getKind()) {
|
|
case NestedNameSpecifier::Kind::Null:
|
|
return;
|
|
case NestedNameSpecifier::Kind::Namespace: {
|
|
auto [_1, Prefix] = Qualifier.getAsNamespaceAndPrefix();
|
|
PushTrivial(Context, Prefix, R.getBegin());
|
|
SaveSourceLocation(R.getBegin(), Buffer, BufferSize, BufferCapacity);
|
|
break;
|
|
}
|
|
case NestedNameSpecifier::Kind::Type: {
|
|
TypeSourceInfo *TSInfo = Context.getTrivialTypeSourceInfo(
|
|
QualType(Qualifier.getAsType(), 0), R.getBegin());
|
|
SavePointer(TSInfo->getTypeLoc().getOpaqueData(), Buffer, BufferSize,
|
|
BufferCapacity);
|
|
break;
|
|
}
|
|
case NestedNameSpecifier::Kind::Global:
|
|
case NestedNameSpecifier::Kind::MicrosoftSuper:
|
|
break;
|
|
}
|
|
SaveSourceLocation(R.getEnd(), Buffer, BufferSize, BufferCapacity);
|
|
}
|
|
|
|
void NestedNameSpecifierLocBuilder::Adopt(NestedNameSpecifierLoc Other) {
|
|
if (BufferCapacity)
|
|
free(Buffer);
|
|
|
|
if (!Other) {
|
|
Representation = std::nullopt;
|
|
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<char *>(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);
|
|
}
|