diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4a24aec --- /dev/null +++ b/.clang-format @@ -0,0 +1,46 @@ +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Left +AlignConsecutiveDeclarations: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowAllArgumentsOnNextLine: false +AlignOperands: AlignAfterOperator +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AllowShortLambdasOnASingleLine: All +AllowShortBlocksOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakTemplateDeclarations: 'Yes' +BinPackArguments: false +BinPackParameters: false +BreakBeforeBraces: Custom +BreakBeforeBinaryOperators: NonAssignment +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerIndentWidth: 0 +IndentWidth: 4 +Language: Cpp +MaxEmptyLinesToKeep: 2 +PackConstructorInitializers: CurrentLine +PointerAlignment: Left +TabWidth: 4 +UseTab: Never +SortIncludes: CaseSensitive diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..a6d90db --- /dev/null +++ b/.clangd @@ -0,0 +1,4 @@ +Diagnostics: + Suppress: ["-Wmicrosoft-enum-forward-reference", "-Wc++11-narrowing"] +CompileFlags: + Add: ["-ferror-limit=0", "-D__FUNCTION__=\"dummy\"", "-D_CRT_USE_BUILTIN_OFFSETOF"] diff --git a/include/demangler/Demangle.h b/include/demangler/Demangle.h index 2c44b0b..e8ceb7d 100644 --- a/include/demangler/Demangle.h +++ b/include/demangler/Demangle.h @@ -11,6 +11,7 @@ #include #include +#include namespace demangler { /// This is a llvm local version of __cxa_demangle. Other than the name and @@ -28,8 +29,10 @@ enum : int { demangle_success = 0, }; -char* itaniumDemangle(const char* mangled_name, char* buf, size_t* n, - int* status); +/// Returns a non-NULL pointer to a NUL-terminated C style string +/// that should be explicitly freed, if successful. Otherwise, may return +/// nullptr if mangled_name is not a valid mangling or is nullptr. +char* itaniumDemangle(std::string_view mangled_name); enum MSDemangleFlags { MSDF_None = 0, @@ -46,31 +49,24 @@ enum MSDemangleFlags { /// success, or nullptr on error. /// If n_read is non-null and demangling was successful, it receives how many /// bytes of the input string were consumed. -/// buf can point to a *n_buf bytes large buffer where the demangled name is -/// stored. If the buffer is too small, it is grown with realloc(). If buf is -/// nullptr, then this malloc()s memory for the result. -/// *n_buf stores the size of buf on input if buf is non-nullptr, and it -/// receives the size of the demangled string on output if n_buf is not nullptr. /// status receives one of the demangle_ enum entries above if it's not nullptr. /// Flags controls various details of the demangled representation. -char* microsoftDemangle(const char* mangled_name, size_t* n_read, char* buf, - size_t* n_buf, int* status, - MSDemangleFlags Flags = MSDF_None); +char* microsoftDemangle(std::string_view mangled_name, size_t* n_read, int* status, MSDemangleFlags Flags = MSDF_None); // Demangles a Rust v0 mangled symbol. -char* rustDemangle(const char* MangledName); +char* rustDemangle(std::string_view MangledName); // Demangles a D mangled symbol. -char* dlangDemangle(const char* MangledName); +char* dlangDemangle(std::string_view MangledName); /// Attempt to demangle a string using different demangling schemes. /// The function uses heuristics to determine which demangling scheme to use. /// \param MangledName - reference to string to demangle. /// \returns - the demangled string, or a copy of the input string if no /// demangling occurred. -std::string demangle(const std::string& MangledName); +std::string demangle(std::string_view MangledName); -bool nonMicrosoftDemangle(const char* MangledName, std::string& Result); +bool nonMicrosoftDemangle(std::string_view MangledName, std::string& Result); /// "Partial" demangler. This supports demangling a string into an AST /// (typically an intermediate stage in itaniumDemangle) and querying certain @@ -87,7 +83,7 @@ struct ItaniumPartialDemangler { bool partialDemangle(const char* MangledName); /// Just print the entire mangled name into Buf. Buf and N behave like the - /// second and third parameters to itaniumDemangle. + /// second and third parameters to __cxa_demangle. char* finishDemangle(char* Buf, size_t* N) const; /// Get the base name of a function. This doesn't include trailing template @@ -128,6 +124,6 @@ struct ItaniumPartialDemangler { void* RootNode; void* Context; }; -} // namespace llvm +} // namespace demangler #endif diff --git a/include/demangler/DemangleConfig.h b/include/demangler/DemangleConfig.h index 5de1b3f..67bdf35 100644 --- a/include/demangler/DemangleConfig.h +++ b/include/demangler/DemangleConfig.h @@ -33,11 +33,10 @@ #ifndef DEMANGLE_GNUC_PREREQ #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) -#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \ - ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ - ((maj) << 20) + ((min) << 10) + (patch)) +#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= ((maj) << 20) + ((min) << 10) + (patch)) #elif defined(__GNUC__) && defined(__GNUC_MINOR__) -#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \ +#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \ ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) #else #define DEMANGLE_GNUC_PREREQ(maj, min, patch) 0 @@ -86,11 +85,11 @@ #define DEMANGLE_FALLTHROUGH #endif -#define DEMANGLE_NAMESPACE_BEGIN \ - namespace demangler { \ +#define DEMANGLE_NAMESPACE_BEGIN \ + namespace demangler { \ namespace itanium_demangle { -#define DEMANGLE_NAMESPACE_END \ - } \ +#define DEMANGLE_NAMESPACE_END \ + } \ } #endif diff --git a/include/demangler/ItaniumDemangle.h b/include/demangler/ItaniumDemangle.h new file mode 100644 index 0000000..e326bf4 --- /dev/null +++ b/include/demangler/ItaniumDemangle.h @@ -0,0 +1,5316 @@ +//===--- ItaniumDemangle.h -----------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. +// 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 +// +//===----------------------------------------------------------------------===// +// +// Generic itanium demangler library. +// There are two copies of this file in the source tree. The one under +// libcxxabi is the original and the one under llvm is the copy. Use +// cp-to-llvm.sh to update the copy. See README.txt for more details. +// +//===----------------------------------------------------------------------===// + +#ifndef DEMANGLE_ITANIUMDEMANGLE_H +#define DEMANGLE_ITANIUMDEMANGLE_H + +#include "DemangleConfig.h" +#include "StringViewExtras.h" +#include "Utility.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DEMANGLE_NAMESPACE_BEGIN + +template +class PODSmallVector { + static_assert(std::is_pod::value, "T is required to be a plain old data type"); + + T* First = nullptr; + T* Last = nullptr; + T* Cap = nullptr; + T Inline[N] = {0}; + + bool isInline() const { return First == Inline; } + + void clearInline() { + First = Inline; + Last = Inline; + Cap = Inline + N; + } + + void reserve(size_t NewCap) { + size_t S = size(); + if (isInline()) { + auto* Tmp = static_cast(std::malloc(NewCap * sizeof(T))); + if (Tmp == nullptr) std::terminate(); + std::copy(First, Last, Tmp); + First = Tmp; + } else { + First = static_cast(std::realloc(First, NewCap * sizeof(T))); + if (First == nullptr) std::terminate(); + } + Last = First + S; + Cap = First + NewCap; + } + +public: + PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} + + PODSmallVector(const PODSmallVector&) = delete; + PODSmallVector& operator=(const PODSmallVector&) = delete; + + PODSmallVector(PODSmallVector&& Other) : PODSmallVector() { + if (Other.isInline()) { + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return; + } + + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + } + + PODSmallVector& operator=(PODSmallVector&& Other) { + if (Other.isInline()) { + if (!isInline()) { + std::free(First); + clearInline(); + } + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return *this; + } + + if (isInline()) { + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + return *this; + } + + std::swap(First, Other.First); + std::swap(Last, Other.Last); + std::swap(Cap, Other.Cap); + Other.clear(); + return *this; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void push_back(const T& Elem) { + if (Last == Cap) reserve(size() * 2); + *Last++ = Elem; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void pop_back() { + assert(Last != First && "Popping empty vector!"); + --Last; + } + + void dropBack(size_t Index) { + assert(Index <= size() && "dropBack() can't expand!"); + Last = First + Index; + } + + T* begin() { return First; } + T* end() { return Last; } + + bool empty() const { return First == Last; } + size_t size() const { return static_cast(Last - First); } + T& back() { + assert(Last != First && "Calling back() on empty vector!"); + return *(Last - 1); + } + T& operator[](size_t Index) { + assert(Index < size() && "Invalid access!"); + return *(begin() + Index); + } + void clear() { Last = First; } + + ~PODSmallVector() { + if (!isInline()) std::free(First); + } +}; + +// Base class of all AST nodes. The AST is built by the parser, then is +// traversed by the printLeft/Right functions to produce a demangled string. +class Node { +public: + enum Kind : unsigned char { +#define NODE(NodeKind) K##NodeKind, +#include "ItaniumNodes.def" + }; + + /// Three-way bool to track a cached value. Unknown is possible if this node + /// has an unexpanded parameter pack below it that may affect this cache. + enum class Cache : unsigned char { + Yes, + No, + Unknown, + }; + + /// Operator precedence for expression nodes. Used to determine required + /// parens in expression emission. + enum class Prec { + Primary, + Postfix, + Unary, + Cast, + PtrMem, + Multiplicative, + Additive, + Shift, + Spaceship, + Relational, + Equality, + And, + Xor, + Ior, + AndIf, + OrIf, + Conditional, + Assign, + Comma, + Default, + }; + +private: + Kind K; + + Prec Precedence : 6; + + // FIXME: Make these protected. +public: + /// Tracks if this node has a component on its right side, in which case we + /// need to call printRight. + Cache RHSComponentCache : 2; + + /// Track if this node is a (possibly qualified) array type. This can affect + /// how we format the output string. + Cache ArrayCache : 2; + + /// Track if this node is a (possibly qualified) function type. This can + /// affect how we format the output string. + Cache FunctionCache : 2; + +public: + Node( + Kind K_, + Prec Precedence_ = Prec::Primary, + Cache RHSComponentCache_ = Cache::No, + Cache ArrayCache_ = Cache::No, + Cache FunctionCache_ = Cache::No + ) + : K(K_), + Precedence(Precedence_), + RHSComponentCache(RHSComponentCache_), + ArrayCache(ArrayCache_), + FunctionCache(FunctionCache_) {} + Node(Kind K_, Cache RHSComponentCache_, Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No) + : Node(K_, Prec::Primary, RHSComponentCache_, ArrayCache_, FunctionCache_) {} + + /// Visit the most-derived object corresponding to this object. + template + void visit(Fn F) const; + + // The following function is provided by all derived classes: + // + // Call F with arguments that, when passed to the constructor of this node, + // would construct an equivalent node. + // template void match(Fn F) const; + + bool hasRHSComponent(OutputBuffer& OB) const { + if (RHSComponentCache != Cache::Unknown) return RHSComponentCache == Cache::Yes; + return hasRHSComponentSlow(OB); + } + + bool hasArray(OutputBuffer& OB) const { + if (ArrayCache != Cache::Unknown) return ArrayCache == Cache::Yes; + return hasArraySlow(OB); + } + + bool hasFunction(OutputBuffer& OB) const { + if (FunctionCache != Cache::Unknown) return FunctionCache == Cache::Yes; + return hasFunctionSlow(OB); + } + + Kind getKind() const { return K; } + + Prec getPrecedence() const { return Precedence; } + + virtual bool hasRHSComponentSlow(OutputBuffer&) const { return false; } + virtual bool hasArraySlow(OutputBuffer&) const { return false; } + virtual bool hasFunctionSlow(OutputBuffer&) const { return false; } + + // Dig through "glue" nodes like ParameterPack and ForwardTemplateReference to + // get at a node that actually represents some concrete syntax. + virtual const Node* getSyntaxNode(OutputBuffer&) const { return this; } + + // Print this node as an expression operand, surrounding it in parentheses if + // its precedence is [Strictly] weaker than P. + void printAsOperand(OutputBuffer& OB, Prec P = Prec::Default, bool StrictlyWorse = false) const { + bool Paren = unsigned(getPrecedence()) >= unsigned(P) + unsigned(StrictlyWorse); + if (Paren) OB.printOpen(); + print(OB); + if (Paren) OB.printClose(); + } + + void print(OutputBuffer& OB) const { + printLeft(OB); + if (RHSComponentCache != Cache::No) printRight(OB); + } + + // Print the "left" side of this Node into OutputBuffer. + virtual void printLeft(OutputBuffer&) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implementation. + virtual void printRight(OutputBuffer&) const {} + + virtual std::string_view getBaseName() const { return {}; } + + // Silence compiler warnings, this dtor will never be called. + virtual ~Node() = default; + +#ifndef NDEBUG + DEMANGLE_DUMP_METHOD void dump() const; +#endif +}; + +class NodeArray { + Node** Elements; + size_t NumElements; + +public: + NodeArray() : Elements(nullptr), NumElements(0) {} + NodeArray(Node** Elements_, size_t NumElements_) : Elements(Elements_), NumElements(NumElements_) {} + + bool empty() const { return NumElements == 0; } + size_t size() const { return NumElements; } + + Node** begin() const { return Elements; } + Node** end() const { return Elements + NumElements; } + + Node* operator[](size_t Idx) const { return Elements[Idx]; } + + void printWithComma(OutputBuffer& OB) const { + bool FirstElement = true; + for (size_t Idx = 0; Idx != NumElements; ++Idx) { + size_t BeforeComma = OB.getCurrentPosition(); + if (!FirstElement) OB += ", "; + size_t AfterComma = OB.getCurrentPosition(); + Elements[Idx]->printAsOperand(OB, Node::Prec::Comma); + + // Elements[Idx] is an empty parameter pack expansion, we should erase the + // comma we just printed. + if (AfterComma == OB.getCurrentPosition()) { + OB.setCurrentPosition(BeforeComma); + continue; + } + + FirstElement = false; + } + } +}; + +struct NodeArrayNode : Node { + NodeArray Array; + NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {} + + template + void match(Fn F) const { + F(Array); + } + + void printLeft(OutputBuffer& OB) const override { Array.printWithComma(OB); } +}; + +class DotSuffix final : public Node { + const Node* Prefix; + const std::string_view Suffix; + +public: + DotSuffix(const Node* Prefix_, std::string_view Suffix_) : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {} + + template + void match(Fn F) const { + F(Prefix, Suffix); + } + + void printLeft(OutputBuffer& OB) const override { + Prefix->print(OB); + OB += " ("; + OB += Suffix; + OB += ")"; + } +}; + +class VendorExtQualType final : public Node { + const Node* Ty; + std::string_view Ext; + const Node* TA; + +public: + VendorExtQualType(const Node* Ty_, std::string_view Ext_, const Node* TA_) + : Node(KVendorExtQualType), + Ty(Ty_), + Ext(Ext_), + TA(TA_) {} + + const Node* getTy() const { return Ty; } + std::string_view getExt() const { return Ext; } + const Node* getTA() const { return TA; } + + template + void match(Fn F) const { + F(Ty, Ext, TA); + } + + void printLeft(OutputBuffer& OB) const override { + Ty->print(OB); + OB += " "; + OB += Ext; + if (TA != nullptr) TA->print(OB); + } +}; + +enum FunctionRefQual : unsigned char { + FrefQualNone, + FrefQualLValue, + FrefQualRValue, +}; + +enum Qualifiers { + QualNone = 0, + QualConst = 0x1, + QualVolatile = 0x2, + QualRestrict = 0x4, +}; + +inline Qualifiers operator|=(Qualifiers& Q1, Qualifiers Q2) { return Q1 = static_cast(Q1 | Q2); } + +class QualType final : public Node { +protected: + const Qualifiers Quals; + const Node* Child; + + void printQuals(OutputBuffer& OB) const { + if (Quals & QualConst) OB += " const"; + if (Quals & QualVolatile) OB += " volatile"; + if (Quals & QualRestrict) OB += " restrict"; + } + +public: + QualType(const Node* Child_, Qualifiers Quals_) + : Node(KQualType, Child_->RHSComponentCache, Child_->ArrayCache, Child_->FunctionCache), + Quals(Quals_), + Child(Child_) {} + + Qualifiers getQuals() const { return Quals; } + const Node* getChild() const { return Child; } + + template + void match(Fn F) const { + F(Child, Quals); + } + + bool hasRHSComponentSlow(OutputBuffer& OB) const override { return Child->hasRHSComponent(OB); } + bool hasArraySlow(OutputBuffer& OB) const override { return Child->hasArray(OB); } + bool hasFunctionSlow(OutputBuffer& OB) const override { return Child->hasFunction(OB); } + + void printLeft(OutputBuffer& OB) const override { + Child->printLeft(OB); + printQuals(OB); + } + + void printRight(OutputBuffer& OB) const override { Child->printRight(OB); } +}; + +class ConversionOperatorType final : public Node { + const Node* Ty; + +public: + ConversionOperatorType(const Node* Ty_) : Node(KConversionOperatorType), Ty(Ty_) {} + + template + void match(Fn F) const { + F(Ty); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "operator "; + Ty->print(OB); + } +}; + +class PostfixQualifiedType final : public Node { + const Node* Ty; + const std::string_view Postfix; + +public: + PostfixQualifiedType(const Node* Ty_, std::string_view Postfix_) + : Node(KPostfixQualifiedType), + Ty(Ty_), + Postfix(Postfix_) {} + + template + void match(Fn F) const { + F(Ty, Postfix); + } + + void printLeft(OutputBuffer& OB) const override { + Ty->printLeft(OB); + OB += Postfix; + } +}; + +class NameType final : public Node { + const std::string_view Name; + +public: + NameType(std::string_view Name_) : Node(KNameType), Name(Name_) {} + + template + void match(Fn F) const { + F(Name); + } + + std::string_view getName() const { return Name; } + std::string_view getBaseName() const override { return Name; } + + void printLeft(OutputBuffer& OB) const override { OB += Name; } +}; + +class BitIntType final : public Node { + const Node* Size; + bool Signed; + +public: + BitIntType(const Node* Size_, bool Signed_) : Node(KBitIntType), Size(Size_), Signed(Signed_) {} + + template + void match(Fn F) const { + F(Size, Signed); + } + + void printLeft(OutputBuffer& OB) const override { + if (!Signed) OB += "unsigned "; + OB += "_BitInt"; + OB.printOpen(); + Size->printAsOperand(OB); + OB.printClose(); + } +}; + +class ElaboratedTypeSpefType : public Node { + std::string_view Kind; + Node* Child; + +public: + ElaboratedTypeSpefType(std::string_view Kind_, Node* Child_) + : Node(KElaboratedTypeSpefType), + Kind(Kind_), + Child(Child_) {} + + template + void match(Fn F) const { + F(Kind, Child); + } + + void printLeft(OutputBuffer& OB) const override { + OB += Kind; + OB += ' '; + Child->print(OB); + } +}; + +struct AbiTagAttr : Node { + Node* Base; + std::string_view Tag; + + AbiTagAttr(Node* Base_, std::string_view Tag_) + : Node(KAbiTagAttr, Base_->RHSComponentCache, Base_->ArrayCache, Base_->FunctionCache), + Base(Base_), + Tag(Tag_) {} + + template + void match(Fn F) const { + F(Base, Tag); + } + + std::string_view getBaseName() const override { return Base->getBaseName(); } + + void printLeft(OutputBuffer& OB) const override { + Base->printLeft(OB); + OB += "[abi:"; + OB += Tag; + OB += "]"; + } +}; + +class EnableIfAttr : public Node { + NodeArray Conditions; + +public: + EnableIfAttr(NodeArray Conditions_) : Node(KEnableIfAttr), Conditions(Conditions_) {} + + template + void match(Fn F) const { + F(Conditions); + } + + void printLeft(OutputBuffer& OB) const override { + OB += " [enable_if:"; + Conditions.printWithComma(OB); + OB += ']'; + } +}; + +class ObjCProtoName : public Node { + const Node* Ty; + std::string_view Protocol; + + friend class PointerType; + +public: + ObjCProtoName(const Node* Ty_, std::string_view Protocol_) : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} + + template + void match(Fn F) const { + F(Ty, Protocol); + } + + bool isObjCObject() const { + return Ty->getKind() == KNameType && static_cast(Ty)->getName() == "objc_object"; + } + + void printLeft(OutputBuffer& OB) const override { + Ty->print(OB); + OB += "<"; + OB += Protocol; + OB += ">"; + } +}; + +class PointerType final : public Node { + const Node* Pointee; + +public: + PointerType(const Node* Pointee_) : Node(KPointerType, Pointee_->RHSComponentCache), Pointee(Pointee_) {} + + const Node* getPointee() const { return Pointee; } + + template + void match(Fn F) const { + F(Pointee); + } + + bool hasRHSComponentSlow(OutputBuffer& OB) const override { return Pointee->hasRHSComponent(OB); } + + void printLeft(OutputBuffer& OB) const override { + // We rewrite objc_object* into id. + if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { + Pointee->printLeft(OB); + if (Pointee->hasArray(OB)) OB += " "; + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) OB += "("; + OB += "*"; + } else { + const auto* objcProto = static_cast(Pointee); + OB += "id<"; + OB += objcProto->Protocol; + OB += ">"; + } + } + + void printRight(OutputBuffer& OB) const override { + if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) OB += ")"; + Pointee->printRight(OB); + } + } +}; + +enum class ReferenceKind { + LValue, + RValue, +}; + +// Represents either a LValue or an RValue reference type. +class ReferenceType : public Node { + const Node* Pointee; + ReferenceKind RK; + + mutable bool Printing = false; + + // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The + // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any + // other combination collapses to a lvalue ref. + // + // A combination of a TemplateForwardReference and a back-ref Substitution + // from an ill-formed string may have created a cycle; use cycle detection to + // avoid looping forever. + std::pair collapse(OutputBuffer& OB) const { + auto SoFar = std::make_pair(RK, Pointee); + // Track the chain of nodes for the Floyd's 'tortoise and hare' + // cycle-detection algorithm, since getSyntaxNode(S) is impure + PODSmallVector Prev; + for (;;) { + const Node* SN = SoFar.second->getSyntaxNode(OB); + if (SN->getKind() != KReferenceType) break; + auto* RT = static_cast(SN); + SoFar.second = RT->Pointee; + SoFar.first = std::min(SoFar.first, RT->RK); + + // The middle of Prev is the 'slow' pointer moving at half speed + Prev.push_back(SoFar.second); + if (Prev.size() > 1 && SoFar.second == Prev[(Prev.size() - 1) / 2]) { + // Cycle detected + SoFar.second = nullptr; + break; + } + } + return SoFar; + } + +public: + ReferenceType(const Node* Pointee_, ReferenceKind RK_) + : Node(KReferenceType, Pointee_->RHSComponentCache), + Pointee(Pointee_), + RK(RK_) {} + + template + void match(Fn F) const { + F(Pointee, RK); + } + + bool hasRHSComponentSlow(OutputBuffer& OB) const override { return Pointee->hasRHSComponent(OB); } + + void printLeft(OutputBuffer& OB) const override { + if (Printing) return; + ScopedOverride SavePrinting(Printing, true); + std::pair Collapsed = collapse(OB); + if (!Collapsed.second) return; + Collapsed.second->printLeft(OB); + if (Collapsed.second->hasArray(OB)) OB += " "; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) OB += "("; + + OB += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); + } + void printRight(OutputBuffer& OB) const override { + if (Printing) return; + ScopedOverride SavePrinting(Printing, true); + std::pair Collapsed = collapse(OB); + if (!Collapsed.second) return; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) OB += ")"; + Collapsed.second->printRight(OB); + } +}; + +class PointerToMemberType final : public Node { + const Node* ClassType; + const Node* MemberType; + +public: + PointerToMemberType(const Node* ClassType_, const Node* MemberType_) + : Node(KPointerToMemberType, MemberType_->RHSComponentCache), + ClassType(ClassType_), + MemberType(MemberType_) {} + + template + void match(Fn F) const { + F(ClassType, MemberType); + } + + bool hasRHSComponentSlow(OutputBuffer& OB) const override { return MemberType->hasRHSComponent(OB); } + + void printLeft(OutputBuffer& OB) const override { + MemberType->printLeft(OB); + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += "("; + else OB += " "; + ClassType->print(OB); + OB += "::*"; + } + + void printRight(OutputBuffer& OB) const override { + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += ")"; + MemberType->printRight(OB); + } +}; + +class ArrayType final : public Node { + const Node* Base; + Node* Dimension; + +public: + ArrayType(const Node* Base_, Node* Dimension_) + : Node( + KArrayType, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::Yes + ), + Base(Base_), + Dimension(Dimension_) {} + + template + void match(Fn F) const { + F(Base, Dimension); + } + + bool hasRHSComponentSlow(OutputBuffer&) const override { return true; } + bool hasArraySlow(OutputBuffer&) const override { return true; } + + void printLeft(OutputBuffer& OB) const override { Base->printLeft(OB); } + + void printRight(OutputBuffer& OB) const override { + if (OB.back() != ']') OB += " "; + OB += "["; + if (Dimension) Dimension->print(OB); + OB += "]"; + Base->printRight(OB); + } +}; + +class FunctionType final : public Node { + const Node* Ret; + NodeArray Params; + Qualifiers CVQuals; + FunctionRefQual RefQual; + const Node* ExceptionSpec; + +public: + FunctionType( + const Node* Ret_, + NodeArray Params_, + Qualifiers CVQuals_, + FunctionRefQual RefQual_, + const Node* ExceptionSpec_ + ) + : Node( + KFunctionType, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes + ), + Ret(Ret_), + Params(Params_), + CVQuals(CVQuals_), + RefQual(RefQual_), + ExceptionSpec(ExceptionSpec_) {} + + template + void match(Fn F) const { + F(Ret, Params, CVQuals, RefQual, ExceptionSpec); + } + + bool hasRHSComponentSlow(OutputBuffer&) const override { return true; } + bool hasFunctionSlow(OutputBuffer&) const override { return true; } + + // Handle C++'s ... quirky decl grammar by using the left & right + // distinction. Consider: + // int (*f(float))(char) {} + // f is a function that takes a float and returns a pointer to a function + // that takes a char and returns an int. If we're trying to print f, start + // by printing out the return types's left, then print our parameters, then + // finally print right of the return type. + void printLeft(OutputBuffer& OB) const override { + Ret->printLeft(OB); + OB += " "; + } + + void printRight(OutputBuffer& OB) const override { + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + Ret->printRight(OB); + + if (CVQuals & QualConst) OB += " const"; + if (CVQuals & QualVolatile) OB += " volatile"; + if (CVQuals & QualRestrict) OB += " restrict"; + + if (RefQual == FrefQualLValue) OB += " &"; + else if (RefQual == FrefQualRValue) OB += " &&"; + + if (ExceptionSpec != nullptr) { + OB += ' '; + ExceptionSpec->print(OB); + } + } +}; + +class NoexceptSpec : public Node { + const Node* E; + +public: + NoexceptSpec(const Node* E_) : Node(KNoexceptSpec), E(E_) {} + + template + void match(Fn F) const { + F(E); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "noexcept"; + OB.printOpen(); + E->printAsOperand(OB); + OB.printClose(); + } +}; + +class DynamicExceptionSpec : public Node { + NodeArray Types; + +public: + DynamicExceptionSpec(NodeArray Types_) : Node(KDynamicExceptionSpec), Types(Types_) {} + + template + void match(Fn F) const { + F(Types); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "throw"; + OB.printOpen(); + Types.printWithComma(OB); + OB.printClose(); + } +}; + +class FunctionEncoding final : public Node { + const Node* Ret; + const Node* Name; + NodeArray Params; + const Node* Attrs; + Qualifiers CVQuals; + FunctionRefQual RefQual; + +public: + FunctionEncoding( + const Node* Ret_, + const Node* Name_, + NodeArray Params_, + const Node* Attrs_, + Qualifiers CVQuals_, + FunctionRefQual RefQual_ + ) + : Node( + KFunctionEncoding, + /*RHSComponentCache=*/Cache::Yes, + /*ArrayCache=*/Cache::No, + /*FunctionCache=*/Cache::Yes + ), + Ret(Ret_), + Name(Name_), + Params(Params_), + Attrs(Attrs_), + CVQuals(CVQuals_), + RefQual(RefQual_) {} + + template + void match(Fn F) const { + F(Ret, Name, Params, Attrs, CVQuals, RefQual); + } + + Qualifiers getCVQuals() const { return CVQuals; } + FunctionRefQual getRefQual() const { return RefQual; } + NodeArray getParams() const { return Params; } + const Node* getReturnType() const { return Ret; } + + bool hasRHSComponentSlow(OutputBuffer&) const override { return true; } + bool hasFunctionSlow(OutputBuffer&) const override { return true; } + + const Node* getName() const { return Name; } + + void printLeft(OutputBuffer& OB) const override { + if (Ret) { + Ret->printLeft(OB); + if (!Ret->hasRHSComponent(OB)) OB += " "; + } + Name->print(OB); + } + + void printRight(OutputBuffer& OB) const override { + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + if (Ret) Ret->printRight(OB); + + if (CVQuals & QualConst) OB += " const"; + if (CVQuals & QualVolatile) OB += " volatile"; + if (CVQuals & QualRestrict) OB += " restrict"; + + if (RefQual == FrefQualLValue) OB += " &"; + else if (RefQual == FrefQualRValue) OB += " &&"; + + if (Attrs != nullptr) Attrs->print(OB); + } +}; + +class LiteralOperator : public Node { + const Node* OpName; + +public: + LiteralOperator(const Node* OpName_) : Node(KLiteralOperator), OpName(OpName_) {} + + template + void match(Fn F) const { + F(OpName); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "operator\"\" "; + OpName->print(OB); + } +}; + +class SpecialName final : public Node { + const std::string_view Special; + const Node* Child; + +public: + SpecialName(std::string_view Special_, const Node* Child_) : Node(KSpecialName), Special(Special_), Child(Child_) {} + + template + void match(Fn F) const { + F(Special, Child); + } + + void printLeft(OutputBuffer& OB) const override { + OB += Special; + Child->print(OB); + } +}; + +class CtorVtableSpecialName final : public Node { + const Node* FirstType; + const Node* SecondType; + +public: + CtorVtableSpecialName(const Node* FirstType_, const Node* SecondType_) + : Node(KCtorVtableSpecialName), + FirstType(FirstType_), + SecondType(SecondType_) {} + + template + void match(Fn F) const { + F(FirstType, SecondType); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "construction vtable for "; + FirstType->print(OB); + OB += "-in-"; + SecondType->print(OB); + } +}; + +struct NestedName : Node { + Node* Qual; + Node* Name; + + NestedName(Node* Qual_, Node* Name_) : Node(KNestedName), Qual(Qual_), Name(Name_) {} + + template + void match(Fn F) const { + F(Qual, Name); + } + + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer& OB) const override { + Qual->print(OB); + OB += "::"; + Name->print(OB); + } +}; + +struct ModuleName : Node { + ModuleName* Parent; + Node* Name; + bool IsPartition; + + ModuleName(ModuleName* Parent_, Node* Name_, bool IsPartition_ = false) + : Node(KModuleName), + Parent(Parent_), + Name(Name_), + IsPartition(IsPartition_) {} + + template + void match(Fn F) const { + F(Parent, Name, IsPartition); + } + + void printLeft(OutputBuffer& OB) const override { + if (Parent) Parent->print(OB); + if (Parent || IsPartition) OB += IsPartition ? ':' : '.'; + Name->print(OB); + } +}; + +struct ModuleEntity : Node { + ModuleName* Module; + Node* Name; + + ModuleEntity(ModuleName* Module_, Node* Name_) : Node(KModuleEntity), Module(Module_), Name(Name_) {} + + template + void match(Fn F) const { + F(Module, Name); + } + + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer& OB) const override { + Name->print(OB); + OB += '@'; + Module->print(OB); + } +}; + +struct LocalName : Node { + Node* Encoding; + Node* Entity; + + LocalName(Node* Encoding_, Node* Entity_) : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {} + + template + void match(Fn F) const { + F(Encoding, Entity); + } + + void printLeft(OutputBuffer& OB) const override { + Encoding->print(OB); + OB += "::"; + Entity->print(OB); + } +}; + +class QualifiedName final : public Node { + // qualifier::name + const Node* Qualifier; + const Node* Name; + +public: + QualifiedName(const Node* Qualifier_, const Node* Name_) + : Node(KQualifiedName), + Qualifier(Qualifier_), + Name(Name_) {} + + template + void match(Fn F) const { + F(Qualifier, Name); + } + + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer& OB) const override { + Qualifier->print(OB); + OB += "::"; + Name->print(OB); + } +}; + +class VectorType final : public Node { + const Node* BaseType; + const Node* Dimension; + +public: + VectorType(const Node* BaseType_, const Node* Dimension_) + : Node(KVectorType), + BaseType(BaseType_), + Dimension(Dimension_) {} + + const Node* getBaseType() const { return BaseType; } + const Node* getDimension() const { return Dimension; } + + template + void match(Fn F) const { + F(BaseType, Dimension); + } + + void printLeft(OutputBuffer& OB) const override { + BaseType->print(OB); + OB += " vector["; + if (Dimension) Dimension->print(OB); + OB += "]"; + } +}; + +class PixelVectorType final : public Node { + const Node* Dimension; + +public: + PixelVectorType(const Node* Dimension_) : Node(KPixelVectorType), Dimension(Dimension_) {} + + template + void match(Fn F) const { + F(Dimension); + } + + void printLeft(OutputBuffer& OB) const override { + // FIXME: This should demangle as "vector pixel". + OB += "pixel vector["; + Dimension->print(OB); + OB += "]"; + } +}; + +class BinaryFPType final : public Node { + const Node* Dimension; + +public: + BinaryFPType(const Node* Dimension_) : Node(KBinaryFPType), Dimension(Dimension_) {} + + template + void match(Fn F) const { + F(Dimension); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "_Float"; + Dimension->print(OB); + } +}; + +enum class TemplateParamKind { Type, NonType, Template }; + +/// An invented name for a template parameter for which we don't have a +/// corresponding template argument. +/// +/// This node is created when parsing the for a lambda with +/// explicit template arguments, which might be referenced in the parameter +/// types appearing later in the . +class SyntheticTemplateParamName final : public Node { + TemplateParamKind Kind; + unsigned Index; + +public: + SyntheticTemplateParamName(TemplateParamKind Kind_, unsigned Index_) + : Node(KSyntheticTemplateParamName), + Kind(Kind_), + Index(Index_) {} + + template + void match(Fn F) const { + F(Kind, Index); + } + + void printLeft(OutputBuffer& OB) const override { + switch (Kind) { + case TemplateParamKind::Type: + OB += "$T"; + break; + case TemplateParamKind::NonType: + OB += "$N"; + break; + case TemplateParamKind::Template: + OB += "$TT"; + break; + } + if (Index > 0) OB << Index - 1; + } +}; + +/// A template type parameter declaration, 'typename T'. +class TypeTemplateParamDecl final : public Node { + Node* Name; + +public: + TypeTemplateParamDecl(Node* Name_) : Node(KTypeTemplateParamDecl, Cache::Yes), Name(Name_) {} + + template + void match(Fn F) const { + F(Name); + } + + void printLeft(OutputBuffer& OB) const override { OB += "typename "; } + + void printRight(OutputBuffer& OB) const override { Name->print(OB); } +}; + +/// A non-type template parameter declaration, 'int N'. +class NonTypeTemplateParamDecl final : public Node { + Node* Name; + Node* Type; + +public: + NonTypeTemplateParamDecl(Node* Name_, Node* Type_) + : Node(KNonTypeTemplateParamDecl, Cache::Yes), + Name(Name_), + Type(Type_) {} + + template + void match(Fn F) const { + F(Name, Type); + } + + void printLeft(OutputBuffer& OB) const override { + Type->printLeft(OB); + if (!Type->hasRHSComponent(OB)) OB += " "; + } + + void printRight(OutputBuffer& OB) const override { + Name->print(OB); + Type->printRight(OB); + } +}; + +/// A template template parameter declaration, +/// 'template typename N'. +class TemplateTemplateParamDecl final : public Node { + Node* Name; + NodeArray Params; + +public: + TemplateTemplateParamDecl(Node* Name_, NodeArray Params_) + : Node(KTemplateTemplateParamDecl, Cache::Yes), + Name(Name_), + Params(Params_) {} + + template + void match(Fn F) const { + F(Name, Params); + } + + void printLeft(OutputBuffer& OB) const override { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "template<"; + Params.printWithComma(OB); + OB += "> typename "; + } + + void printRight(OutputBuffer& OB) const override { Name->print(OB); } +}; + +/// A template parameter pack declaration, 'typename ...T'. +class TemplateParamPackDecl final : public Node { + Node* Param; + +public: + TemplateParamPackDecl(Node* Param_) : Node(KTemplateParamPackDecl, Cache::Yes), Param(Param_) {} + + template + void match(Fn F) const { + F(Param); + } + + void printLeft(OutputBuffer& OB) const override { + Param->printLeft(OB); + OB += "..."; + } + + void printRight(OutputBuffer& OB) const override { Param->printRight(OB); } +}; + +/// An unexpanded parameter pack (either in the expression or type context). If +/// this AST is correct, this node will have a ParameterPackExpansion node above +/// it. +/// +/// This node is created when some are found that apply to an +/// , and is stored in the TemplateParams table. In order for this to +/// appear in the final AST, it has to referenced via a (ie, +/// T_). +class ParameterPack final : public Node { + NodeArray Data; + + // Setup OutputBuffer for a pack expansion, unless we're already expanding + // one. + void initializePackExpansion(OutputBuffer& OB) const { + if (OB.CurrentPackMax == std::numeric_limits::max()) { + OB.CurrentPackMax = static_cast(Data.size()); + OB.CurrentPackIndex = 0; + } + } + +public: + ParameterPack(NodeArray Data_) : Node(KParameterPack), Data(Data_) { + ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { return P->ArrayCache == Cache::No; })) + ArrayCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { return P->FunctionCache == Cache::No; })) + FunctionCache = Cache::No; + if (std::all_of(Data.begin(), Data.end(), [](Node* P) { return P->RHSComponentCache == Cache::No; })) + RHSComponentCache = Cache::No; + } + + template + void match(Fn F) const { + F(Data); + } + + bool hasRHSComponentSlow(OutputBuffer& OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(OB); + } + bool hasArraySlow(OutputBuffer& OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(OB); + } + bool hasFunctionSlow(OutputBuffer& OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(OB); + } + const Node* getSyntaxNode(OutputBuffer& OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() ? Data[Idx]->getSyntaxNode(OB) : this; + } + + void printLeft(OutputBuffer& OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + if (Idx < Data.size()) Data[Idx]->printLeft(OB); + } + void printRight(OutputBuffer& OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + if (Idx < Data.size()) Data[Idx]->printRight(OB); + } +}; + +/// A variadic template argument. This node represents an occurrence of +/// JE in some . It isn't itself unexpanded, unless +/// one of it's Elements is. The parser inserts a ParameterPack into the +/// TemplateParams table if the this pack belongs to apply to an +/// . +class TemplateArgumentPack final : public Node { + NodeArray Elements; + +public: + TemplateArgumentPack(NodeArray Elements_) : Node(KTemplateArgumentPack), Elements(Elements_) {} + + template + void match(Fn F) const { + F(Elements); + } + + NodeArray getElements() const { return Elements; } + + void printLeft(OutputBuffer& OB) const override { Elements.printWithComma(OB); } +}; + +/// A pack expansion. Below this node, there are some unexpanded ParameterPacks +/// which each have Child->ParameterPackSize elements. +class ParameterPackExpansion final : public Node { + const Node* Child; + +public: + ParameterPackExpansion(const Node* Child_) : Node(KParameterPackExpansion), Child(Child_) {} + + template + void match(Fn F) const { + F(Child); + } + + const Node* getChild() const { return Child; } + + void printLeft(OutputBuffer& OB) const override { + constexpr unsigned Max = std::numeric_limits::max(); + ScopedOverride SavePackIdx(OB.CurrentPackIndex, Max); + ScopedOverride SavePackMax(OB.CurrentPackMax, Max); + size_t StreamPos = OB.getCurrentPosition(); + + // Print the first element in the pack. If Child contains a ParameterPack, + // it will set up S.CurrentPackMax and print the first element. + Child->print(OB); + + // No ParameterPack was found in Child. This can occur if we've found a pack + // expansion on a . + if (OB.CurrentPackMax == Max) { + OB += "..."; + return; + } + + // We found a ParameterPack, but it has no elements. Erase whatever we may + // of printed. + if (OB.CurrentPackMax == 0) { + OB.setCurrentPosition(StreamPos); + return; + } + + // Else, iterate through the rest of the elements in the pack. + for (unsigned I = 1, E = OB.CurrentPackMax; I < E; ++I) { + OB += ", "; + OB.CurrentPackIndex = I; + Child->print(OB); + } + } +}; + +class TemplateArgs final : public Node { + NodeArray Params; + +public: + TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {} + + template + void match(Fn F) const { + F(Params); + } + + NodeArray getParams() { return Params; } + + void printLeft(OutputBuffer& OB) const override { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "<"; + Params.printWithComma(OB); + OB += ">"; + } +}; + +/// A forward-reference to a template argument that was not known at the point +/// where the template parameter name was parsed in a mangling. +/// +/// This is created when demangling the name of a specialization of a +/// conversion function template: +/// +/// \code +/// struct A { +/// template operator T*(); +/// }; +/// \endcode +/// +/// When demangling a specialization of the conversion function template, we +/// encounter the name of the template (including the \c T) before we reach +/// the template argument list, so we cannot substitute the parameter name +/// for the corresponding argument while parsing. Instead, we create a +/// \c ForwardTemplateReference node that is resolved after we parse the +/// template arguments. +struct ForwardTemplateReference : Node { + size_t Index; + Node* Ref = nullptr; + + // If we're currently printing this node. It is possible (though invalid) for + // a forward template reference to refer to itself via a substitution. This + // creates a cyclic AST, which will stack overflow printing. To fix this, bail + // out if more than one print* function is active. + mutable bool Printing = false; + + ForwardTemplateReference(size_t Index_) + : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown, Cache::Unknown), + Index(Index_) {} + + // We don't provide a matcher for these, because the value of the node is + // not determined by its construction parameters, and it generally needs + // special handling. + template + void match(Fn F) const = delete; + + bool hasRHSComponentSlow(OutputBuffer& OB) const override { + if (Printing) return false; + ScopedOverride SavePrinting(Printing, true); + return Ref->hasRHSComponent(OB); + } + bool hasArraySlow(OutputBuffer& OB) const override { + if (Printing) return false; + ScopedOverride SavePrinting(Printing, true); + return Ref->hasArray(OB); + } + bool hasFunctionSlow(OutputBuffer& OB) const override { + if (Printing) return false; + ScopedOverride SavePrinting(Printing, true); + return Ref->hasFunction(OB); + } + const Node* getSyntaxNode(OutputBuffer& OB) const override { + if (Printing) return this; + ScopedOverride SavePrinting(Printing, true); + return Ref->getSyntaxNode(OB); + } + + void printLeft(OutputBuffer& OB) const override { + if (Printing) return; + ScopedOverride SavePrinting(Printing, true); + Ref->printLeft(OB); + } + void printRight(OutputBuffer& OB) const override { + if (Printing) return; + ScopedOverride SavePrinting(Printing, true); + Ref->printRight(OB); + } +}; + +struct NameWithTemplateArgs : Node { + // name + Node* Name; + Node* TemplateArgs; + + NameWithTemplateArgs(Node* Name_, Node* TemplateArgs_) + : Node(KNameWithTemplateArgs), + Name(Name_), + TemplateArgs(TemplateArgs_) {} + + template + void match(Fn F) const { + F(Name, TemplateArgs); + } + + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer& OB) const override { + Name->print(OB); + TemplateArgs->print(OB); + } +}; + +class GlobalQualifiedName final : public Node { + Node* Child; + +public: + GlobalQualifiedName(Node* Child_) : Node(KGlobalQualifiedName), Child(Child_) {} + + template + void match(Fn F) const { + F(Child); + } + + std::string_view getBaseName() const override { return Child->getBaseName(); } + + void printLeft(OutputBuffer& OB) const override { + OB += "::"; + Child->print(OB); + } +}; + +enum class SpecialSubKind { + allocator, + basic_string, + string, + istream, + ostream, + iostream, +}; + +class SpecialSubstitution; +class ExpandedSpecialSubstitution : public Node { +protected: + SpecialSubKind SSK; + + ExpandedSpecialSubstitution(SpecialSubKind SSK_, Kind K_) : Node(K_), SSK(SSK_) {} + +public: + ExpandedSpecialSubstitution(SpecialSubKind SSK_) + : ExpandedSpecialSubstitution(SSK_, KExpandedSpecialSubstitution) {} + inline ExpandedSpecialSubstitution(SpecialSubstitution const*); + + template + void match(Fn F) const { + F(SSK); + } + +protected: + bool isInstantiation() const { return unsigned(SSK) >= unsigned(SpecialSubKind::string); } + + std::string_view getBaseName() const override { + switch (SSK) { + case SpecialSubKind::allocator: + return {"allocator"}; + case SpecialSubKind::basic_string: + return {"basic_string"}; + case SpecialSubKind::string: + return {"basic_string"}; + case SpecialSubKind::istream: + return {"basic_istream"}; + case SpecialSubKind::ostream: + return {"basic_ostream"}; + case SpecialSubKind::iostream: + return {"basic_iostream"}; + } + DEMANGLE_UNREACHABLE; + } + +private: + void printLeft(OutputBuffer& OB) const override { + OB << "std::" << getBaseName(); + if (isInstantiation()) { + OB << ""; + if (SSK == SpecialSubKind::string) OB << ", std::allocator"; + OB << ">"; + } + } +}; + +class SpecialSubstitution final : public ExpandedSpecialSubstitution { +public: + SpecialSubstitution(SpecialSubKind SSK_) : ExpandedSpecialSubstitution(SSK_, KSpecialSubstitution) {} + + template + void match(Fn F) const { + F(SSK); + } + + std::string_view getBaseName() const override { + std::string_view SV = ExpandedSpecialSubstitution::getBaseName(); + if (isInstantiation()) { + // The instantiations are typedefs that drop the "basic_" prefix. + assert(demangler::itanium_demangle::starts_with(SV, "basic_")); + SV.remove_prefix(sizeof("basic_") - 1); + } + return SV; + } + + void printLeft(OutputBuffer& OB) const override { OB << "std::" << getBaseName(); } +}; + +inline ExpandedSpecialSubstitution::ExpandedSpecialSubstitution(SpecialSubstitution const* SS) +: ExpandedSpecialSubstitution(SS->SSK) {} + +class CtorDtorName final : public Node { + const Node* Basename; + const bool IsDtor; + const int Variant; + +public: + CtorDtorName(const Node* Basename_, bool IsDtor_, int Variant_) + : Node(KCtorDtorName), + Basename(Basename_), + IsDtor(IsDtor_), + Variant(Variant_) {} + + template + void match(Fn F) const { + F(Basename, IsDtor, Variant); + } + + void printLeft(OutputBuffer& OB) const override { + if (IsDtor) OB += "~"; + OB += Basename->getBaseName(); + } +}; + +class DtorName : public Node { + const Node* Base; + +public: + DtorName(const Node* Base_) : Node(KDtorName), Base(Base_) {} + + template + void match(Fn F) const { + F(Base); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "~"; + Base->printLeft(OB); + } +}; + +class UnnamedTypeName : public Node { + const std::string_view Count; + +public: + UnnamedTypeName(std::string_view Count_) : Node(KUnnamedTypeName), Count(Count_) {} + + template + void match(Fn F) const { + F(Count); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "'unnamed"; + OB += Count; + OB += "\'"; + } +}; + +class ClosureTypeName : public Node { + NodeArray TemplateParams; + NodeArray Params; + std::string_view Count; + +public: + ClosureTypeName(NodeArray TemplateParams_, NodeArray Params_, std::string_view Count_) + : Node(KClosureTypeName), + TemplateParams(TemplateParams_), + Params(Params_), + Count(Count_) {} + + template + void match(Fn F) const { + F(TemplateParams, Params, Count); + } + + void printDeclarator(OutputBuffer& OB) const { + if (!TemplateParams.empty()) { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "<"; + TemplateParams.printWithComma(OB); + OB += ">"; + } + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "\'lambda"; + OB += Count; + OB += "\'"; + printDeclarator(OB); + } +}; + +class StructuredBindingName : public Node { + NodeArray Bindings; + +public: + StructuredBindingName(NodeArray Bindings_) : Node(KStructuredBindingName), Bindings(Bindings_) {} + + template + void match(Fn F) const { + F(Bindings); + } + + void printLeft(OutputBuffer& OB) const override { + OB.printOpen('['); + Bindings.printWithComma(OB); + OB.printClose(']'); + } +}; + +// -- Expression Nodes -- + +class BinaryExpr : public Node { + const Node* LHS; + const std::string_view InfixOperator; + const Node* RHS; + +public: + BinaryExpr(const Node* LHS_, std::string_view InfixOperator_, const Node* RHS_, Prec Prec_) + : Node(KBinaryExpr, Prec_), + LHS(LHS_), + InfixOperator(InfixOperator_), + RHS(RHS_) {} + + template + void match(Fn F) const { + F(LHS, InfixOperator, RHS, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + bool ParenAll = OB.isGtInsideTemplateArgs() && (InfixOperator == ">" || InfixOperator == ">>"); + if (ParenAll) OB.printOpen(); + // Assignment is right associative, with special LHS precedence. + bool IsAssign = getPrecedence() == Prec::Assign; + LHS->printAsOperand(OB, IsAssign ? Prec::OrIf : getPrecedence(), !IsAssign); + // No space before comma operator + if (!(InfixOperator == ",")) OB += " "; + OB += InfixOperator; + OB += " "; + RHS->printAsOperand(OB, getPrecedence(), IsAssign); + if (ParenAll) OB.printClose(); + } +}; + +class ArraySubscriptExpr : public Node { + const Node* Op1; + const Node* Op2; + +public: + ArraySubscriptExpr(const Node* Op1_, const Node* Op2_, Prec Prec_) + : Node(KArraySubscriptExpr, Prec_), + Op1(Op1_), + Op2(Op2_) {} + + template + void match(Fn F) const { + F(Op1, Op2, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + Op1->printAsOperand(OB, getPrecedence()); + OB.printOpen('['); + Op2->printAsOperand(OB); + OB.printClose(']'); + } +}; + +class PostfixExpr : public Node { + const Node* Child; + const std::string_view Operator; + +public: + PostfixExpr(const Node* Child_, std::string_view Operator_, Prec Prec_) + : Node(KPostfixExpr, Prec_), + Child(Child_), + Operator(Operator_) {} + + template + void match(Fn F) const { + F(Child, Operator, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + Child->printAsOperand(OB, getPrecedence(), true); + OB += Operator; + } +}; + +class ConditionalExpr : public Node { + const Node* Cond; + const Node* Then; + const Node* Else; + +public: + ConditionalExpr(const Node* Cond_, const Node* Then_, const Node* Else_, Prec Prec_) + : Node(KConditionalExpr, Prec_), + Cond(Cond_), + Then(Then_), + Else(Else_) {} + + template + void match(Fn F) const { + F(Cond, Then, Else, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + Cond->printAsOperand(OB, getPrecedence()); + OB += " ? "; + Then->printAsOperand(OB); + OB += " : "; + Else->printAsOperand(OB, Prec::Assign, true); + } +}; + +class MemberExpr : public Node { + const Node* LHS; + const std::string_view Kind; + const Node* RHS; + +public: + MemberExpr(const Node* LHS_, std::string_view Kind_, const Node* RHS_, Prec Prec_) + : Node(KMemberExpr, Prec_), + LHS(LHS_), + Kind(Kind_), + RHS(RHS_) {} + + template + void match(Fn F) const { + F(LHS, Kind, RHS, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + LHS->printAsOperand(OB, getPrecedence(), true); + OB += Kind; + RHS->printAsOperand(OB, getPrecedence(), false); + } +}; + +class SubobjectExpr : public Node { + const Node* Type; + const Node* SubExpr; + std::string_view Offset; + NodeArray UnionSelectors; + bool OnePastTheEnd; + +public: + SubobjectExpr( + const Node* Type_, + const Node* SubExpr_, + std::string_view Offset_, + NodeArray UnionSelectors_, + bool OnePastTheEnd_ + ) + : Node(KSubobjectExpr), + Type(Type_), + SubExpr(SubExpr_), + Offset(Offset_), + UnionSelectors(UnionSelectors_), + OnePastTheEnd(OnePastTheEnd_) {} + + template + void match(Fn F) const { + F(Type, SubExpr, Offset, UnionSelectors, OnePastTheEnd); + } + + void printLeft(OutputBuffer& OB) const override { + SubExpr->print(OB); + OB += ".<"; + Type->print(OB); + OB += " at offset "; + if (Offset.empty()) { + OB += "0"; + } else if (Offset[0] == 'n') { + OB += "-"; + OB += std::string_view(Offset.data() + 1, Offset.size() - 1); + } else { + OB += Offset; + } + OB += ">"; + } +}; + +class EnclosingExpr : public Node { + const std::string_view Prefix; + const Node* Infix; + const std::string_view Postfix; + +public: + EnclosingExpr(std::string_view Prefix_, const Node* Infix_, Prec Prec_ = Prec::Primary) + : Node(KEnclosingExpr, Prec_), + Prefix(Prefix_), + Infix(Infix_) {} + + template + void match(Fn F) const { + F(Prefix, Infix, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + OB += Prefix; + OB.printOpen(); + Infix->print(OB); + OB.printClose(); + OB += Postfix; + } +}; + +class CastExpr : public Node { + // cast_kind(from) + const std::string_view CastKind; + const Node* To; + const Node* From; + +public: + CastExpr(std::string_view CastKind_, const Node* To_, const Node* From_, Prec Prec_) + : Node(KCastExpr, Prec_), + CastKind(CastKind_), + To(To_), + From(From_) {} + + template + void match(Fn F) const { + F(CastKind, To, From, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + OB += CastKind; + { + ScopedOverride LT(OB.GtIsGt, 0); + OB += "<"; + To->printLeft(OB); + OB += ">"; + } + OB.printOpen(); + From->printAsOperand(OB); + OB.printClose(); + } +}; + +class SizeofParamPackExpr : public Node { + const Node* Pack; + +public: + SizeofParamPackExpr(const Node* Pack_) : Node(KSizeofParamPackExpr), Pack(Pack_) {} + + template + void match(Fn F) const { + F(Pack); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "sizeof..."; + OB.printOpen(); + ParameterPackExpansion PPE(Pack); + PPE.printLeft(OB); + OB.printClose(); + } +}; + +class CallExpr : public Node { + const Node* Callee; + NodeArray Args; + +public: + CallExpr(const Node* Callee_, NodeArray Args_, Prec Prec_) : Node(KCallExpr, Prec_), Callee(Callee_), Args(Args_) {} + + template + void match(Fn F) const { + F(Callee, Args, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + Callee->print(OB); + OB.printOpen(); + Args.printWithComma(OB); + OB.printClose(); + } +}; + +class NewExpr : public Node { + // new (expr_list) type(init_list) + NodeArray ExprList; + Node* Type; + NodeArray InitList; + bool IsGlobal; // ::operator new ? + bool IsArray; // new[] ? +public: + NewExpr(NodeArray ExprList_, Node* Type_, NodeArray InitList_, bool IsGlobal_, bool IsArray_, Prec Prec_) + : Node(KNewExpr, Prec_), + ExprList(ExprList_), + Type(Type_), + InitList(InitList_), + IsGlobal(IsGlobal_), + IsArray(IsArray_) {} + + template + void match(Fn F) const { + F(ExprList, Type, InitList, IsGlobal, IsArray, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + if (IsGlobal) OB += "::"; + OB += "new"; + if (IsArray) OB += "[]"; + if (!ExprList.empty()) { + OB.printOpen(); + ExprList.printWithComma(OB); + OB.printClose(); + } + OB += " "; + Type->print(OB); + if (!InitList.empty()) { + OB.printOpen(); + InitList.printWithComma(OB); + OB.printClose(); + } + } +}; + +class DeleteExpr : public Node { + Node* Op; + bool IsGlobal; + bool IsArray; + +public: + DeleteExpr(Node* Op_, bool IsGlobal_, bool IsArray_, Prec Prec_) + : Node(KDeleteExpr, Prec_), + Op(Op_), + IsGlobal(IsGlobal_), + IsArray(IsArray_) {} + + template + void match(Fn F) const { + F(Op, IsGlobal, IsArray, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + if (IsGlobal) OB += "::"; + OB += "delete"; + if (IsArray) OB += "[]"; + OB += ' '; + Op->print(OB); + } +}; + +class PrefixExpr : public Node { + std::string_view Prefix; + Node* Child; + +public: + PrefixExpr(std::string_view Prefix_, Node* Child_, Prec Prec_) + : Node(KPrefixExpr, Prec_), + Prefix(Prefix_), + Child(Child_) {} + + template + void match(Fn F) const { + F(Prefix, Child, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + OB += Prefix; + Child->printAsOperand(OB, getPrecedence()); + } +}; + +class FunctionParam : public Node { + std::string_view Number; + +public: + FunctionParam(std::string_view Number_) : Node(KFunctionParam), Number(Number_) {} + + template + void match(Fn F) const { + F(Number); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "fp"; + OB += Number; + } +}; + +class ConversionExpr : public Node { + const Node* Type; + NodeArray Expressions; + +public: + ConversionExpr(const Node* Type_, NodeArray Expressions_, Prec Prec_) + : Node(KConversionExpr, Prec_), + Type(Type_), + Expressions(Expressions_) {} + + template + void match(Fn F) const { + F(Type, Expressions, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + OB.printOpen(); + Type->print(OB); + OB.printClose(); + OB.printOpen(); + Expressions.printWithComma(OB); + OB.printClose(); + } +}; + +class PointerToMemberConversionExpr : public Node { + const Node* Type; + const Node* SubExpr; + std::string_view Offset; + +public: + PointerToMemberConversionExpr(const Node* Type_, const Node* SubExpr_, std::string_view Offset_, Prec Prec_) + : Node(KPointerToMemberConversionExpr, Prec_), + Type(Type_), + SubExpr(SubExpr_), + Offset(Offset_) {} + + template + void match(Fn F) const { + F(Type, SubExpr, Offset, getPrecedence()); + } + + void printLeft(OutputBuffer& OB) const override { + OB.printOpen(); + Type->print(OB); + OB.printClose(); + OB.printOpen(); + SubExpr->print(OB); + OB.printClose(); + } +}; + +class InitListExpr : public Node { + const Node* Ty; + NodeArray Inits; + +public: + InitListExpr(const Node* Ty_, NodeArray Inits_) : Node(KInitListExpr), Ty(Ty_), Inits(Inits_) {} + + template + void match(Fn F) const { + F(Ty, Inits); + } + + void printLeft(OutputBuffer& OB) const override { + if (Ty) Ty->print(OB); + OB += '{'; + Inits.printWithComma(OB); + OB += '}'; + } +}; + +class BracedExpr : public Node { + const Node* Elem; + const Node* Init; + bool IsArray; + +public: + BracedExpr(const Node* Elem_, const Node* Init_, bool IsArray_) + : Node(KBracedExpr), + Elem(Elem_), + Init(Init_), + IsArray(IsArray_) {} + + template + void match(Fn F) const { + F(Elem, Init, IsArray); + } + + void printLeft(OutputBuffer& OB) const override { + if (IsArray) { + OB += '['; + Elem->print(OB); + OB += ']'; + } else { + OB += '.'; + Elem->print(OB); + } + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) OB += " = "; + Init->print(OB); + } +}; + +class BracedRangeExpr : public Node { + const Node* First; + const Node* Last; + const Node* Init; + +public: + BracedRangeExpr(const Node* First_, const Node* Last_, const Node* Init_) + : Node(KBracedRangeExpr), + First(First_), + Last(Last_), + Init(Init_) {} + + template + void match(Fn F) const { + F(First, Last, Init); + } + + void printLeft(OutputBuffer& OB) const override { + OB += '['; + First->print(OB); + OB += " ... "; + Last->print(OB); + OB += ']'; + if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) OB += " = "; + Init->print(OB); + } +}; + +class FoldExpr : public Node { + const Node * Pack, *Init; + std::string_view OperatorName; + bool IsLeftFold; + +public: + FoldExpr(bool IsLeftFold_, std::string_view OperatorName_, const Node* Pack_, const Node* Init_) + : Node(KFoldExpr), + Pack(Pack_), + Init(Init_), + OperatorName(OperatorName_), + IsLeftFold(IsLeftFold_) {} + + template + void match(Fn F) const { + F(IsLeftFold, OperatorName, Pack, Init); + } + + void printLeft(OutputBuffer& OB) const override { + auto PrintPack = [&] { + OB.printOpen(); + ParameterPackExpansion(Pack).print(OB); + OB.printClose(); + }; + + OB.printOpen(); + // Either '[init op ]... op pack' or 'pack op ...[ op init]' + // Refactored to '[(init|pack) op ]...[ op (pack|init)]' + // Fold expr operands are cast-expressions + if (!IsLeftFold || Init != nullptr) { + // '(init|pack) op ' + if (IsLeftFold) Init->printAsOperand(OB, Prec::Cast, true); + else PrintPack(); + OB << " " << OperatorName << " "; + } + OB << "..."; + if (IsLeftFold || Init != nullptr) { + // ' op (init|pack)' + OB << " " << OperatorName << " "; + if (IsLeftFold) PrintPack(); + else Init->printAsOperand(OB, Prec::Cast, true); + } + OB.printClose(); + } +}; + +class ThrowExpr : public Node { + const Node* Op; + +public: + ThrowExpr(const Node* Op_) : Node(KThrowExpr), Op(Op_) {} + + template + void match(Fn F) const { + F(Op); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "throw "; + Op->print(OB); + } +}; + +class BoolExpr : public Node { + bool Value; + +public: + BoolExpr(bool Value_) : Node(KBoolExpr), Value(Value_) {} + + template + void match(Fn F) const { + F(Value); + } + + void printLeft(OutputBuffer& OB) const override { + OB += Value ? std::string_view("true") : std::string_view("false"); + } +}; + +class StringLiteral : public Node { + const Node* Type; + +public: + StringLiteral(const Node* Type_) : Node(KStringLiteral), Type(Type_) {} + + template + void match(Fn F) const { + F(Type); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "\"<"; + Type->print(OB); + OB += ">\""; + } +}; + +class LambdaExpr : public Node { + const Node* Type; + +public: + LambdaExpr(const Node* Type_) : Node(KLambdaExpr), Type(Type_) {} + + template + void match(Fn F) const { + F(Type); + } + + void printLeft(OutputBuffer& OB) const override { + OB += "[]"; + if (Type->getKind() == KClosureTypeName) static_cast(Type)->printDeclarator(OB); + OB += "{...}"; + } +}; + +class EnumLiteral : public Node { + // ty(integer) + const Node* Ty; + std::string_view Integer; + +public: + EnumLiteral(const Node* Ty_, std::string_view Integer_) : Node(KEnumLiteral), Ty(Ty_), Integer(Integer_) {} + + template + void match(Fn F) const { + F(Ty, Integer); + } + + void printLeft(OutputBuffer& OB) const override { + OB.printOpen(); + Ty->print(OB); + OB.printClose(); + + if (Integer[0] == 'n') OB << '-' << std::string_view(Integer.data() + 1, Integer.size() - 1); + else OB << Integer; + } +}; + +class IntegerLiteral : public Node { + std::string_view Type; + std::string_view Value; + +public: + IntegerLiteral(std::string_view Type_, std::string_view Value_) + : Node(KIntegerLiteral), + Type(Type_), + Value(Value_) {} + + template + void match(Fn F) const { + F(Type, Value); + } + + void printLeft(OutputBuffer& OB) const override { + if (Type.size() > 3) { + OB.printOpen(); + OB += Type; + OB.printClose(); + } + + if (Value[0] == 'n') OB << '-' << std::string_view(Value.data() + 1, Value.size() - 1); + else OB += Value; + + if (Type.size() <= 3) OB += Type; + } +}; + +template +struct FloatData; + +namespace float_literal_impl { +constexpr Node::Kind getFloatLiteralKind(float*) { return Node::KFloatLiteral; } +constexpr Node::Kind getFloatLiteralKind(double*) { return Node::KDoubleLiteral; } +constexpr Node::Kind getFloatLiteralKind(long double*) { return Node::KLongDoubleLiteral; } +} // namespace float_literal_impl + +template +class FloatLiteralImpl : public Node { + const std::string_view Contents; + + static constexpr Kind KindForClass = float_literal_impl::getFloatLiteralKind((Float*)nullptr); + +public: + FloatLiteralImpl(std::string_view Contents_) : Node(KindForClass), Contents(Contents_) {} + + template + void match(Fn F) const { + F(Contents); + } + + void printLeft(OutputBuffer& OB) const override { + const size_t N = FloatData::mangled_size; + if (Contents.size() >= N) { + union { + Float value; + char buf[sizeof(Float)]; + }; + const char* t = Contents.data(); + const char* last = t + N; + char* e = buf; + for (; t != last; ++t, ++e) { + unsigned d1 = isdigit(*t) ? static_cast(*t - '0') : static_cast(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast(*t - '0') : static_cast(*t - 'a' + 10); + *e = static_cast((d1 << 4) + d0); + } +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[FloatData::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), FloatData::spec, value); + OB += std::string_view(num, n); + } + } +}; + +using FloatLiteral = FloatLiteralImpl; +using DoubleLiteral = FloatLiteralImpl; +using LongDoubleLiteral = FloatLiteralImpl; + +/// Visit the node. Calls \c F(P), where \c P is the node cast to the +/// appropriate derived class. +template +void Node::visit(Fn F) const { + switch (K) { +#define NODE(X) \ + case K##X: \ + return F(static_cast(this)); +#include "ItaniumNodes.def" + } + assert(0 && "unknown mangling node kind"); +} + +/// Determine the kind of a node from its type. +template +struct NodeKind; +#define NODE(X) \ + template <> \ + struct NodeKind { \ + static constexpr Node::Kind Kind = Node::K##X; \ + static constexpr const char* name() { return #X; } \ + }; +#include "ItaniumNodes.def" + +template +struct AbstractManglingParser { + const char* First; + const char* Last; + + // Name stack, this is used by the parser to hold temporary names that were + // parsed. The parser collapses multiple names into new nodes to construct + // the AST. Once the parser is finished, names.size() == 1. + PODSmallVector Names; + + // Substitution table. Itanium supports name substitutions as a means of + // compression. The string "S42_" refers to the 44nd entry (base-36) in this + // table. + PODSmallVector Subs; + + using TemplateParamList = PODSmallVector; + + class ScopedTemplateParamList { + AbstractManglingParser* Parser; + size_t OldNumTemplateParamLists; + TemplateParamList Params; + + public: + ScopedTemplateParamList(AbstractManglingParser* TheParser) + : Parser(TheParser), + OldNumTemplateParamLists(TheParser->TemplateParams.size()) { + Parser->TemplateParams.push_back(&Params); + } + ~ScopedTemplateParamList() { + assert(Parser->TemplateParams.size() >= OldNumTemplateParamLists); + Parser->TemplateParams.dropBack(OldNumTemplateParamLists); + } + }; + + // Template parameter table. Like the above, but referenced like "T42_". + // This has a smaller size compared to Subs and Names because it can be + // stored on the stack. + TemplateParamList OuterTemplateParams; + + // Lists of template parameters indexed by template parameter depth, + // referenced like "TL2_4_". If nonempty, element 0 is always + // OuterTemplateParams; inner elements are always template parameter lists of + // lambda expressions. For a generic lambda with no explicit template + // parameter list, the corresponding parameter list pointer will be null. + PODSmallVector TemplateParams; + + // Set of unresolved forward references. These can occur in a + // conversion operator's type, and are resolved in the enclosing . + PODSmallVector ForwardTemplateRefs; + + bool TryToParseTemplateArgs = true; + bool PermitForwardTemplateReferences = false; + size_t ParsingLambdaParamsAtLevel = (size_t)-1; + + unsigned NumSyntheticTemplateParameters[3] = {}; + + Alloc ASTAllocator; + + AbstractManglingParser(const char* First_, const char* Last_) : First(First_), Last(Last_) {} + + Derived& getDerived() { return static_cast(*this); } + + void reset(const char* First_, const char* Last_) { + First = First_; + Last = Last_; + Names.clear(); + Subs.clear(); + TemplateParams.clear(); + ParsingLambdaParamsAtLevel = (size_t)-1; + TryToParseTemplateArgs = true; + PermitForwardTemplateReferences = false; + for (int I = 0; I != 3; ++I) NumSyntheticTemplateParameters[I] = 0; + ASTAllocator.reset(); + } + + template + Node* make(Args&&... args) { + return ASTAllocator.template makeNode(std::forward(args)...); + } + + template + NodeArray makeNodeArray(It begin, It end) { + size_t sz = static_cast(end - begin); + void* mem = ASTAllocator.allocateNodeArray(sz); + Node** data = new (mem) Node*[sz]; + std::copy(begin, end, data); + return NodeArray(data, sz); + } + + NodeArray popTrailingNodeArray(size_t FromPosition) { + assert(FromPosition <= Names.size()); + NodeArray res = makeNodeArray(Names.begin() + (long)FromPosition, Names.end()); + Names.dropBack(FromPosition); + return res; + } + + bool consumeIf(std::string_view S) { + if (demangler::itanium_demangle::starts_with(std::string_view(First, Last - First), S)) { + First += S.size(); + return true; + } + return false; + } + + bool consumeIf(char C) { + if (First != Last && *First == C) { + ++First; + return true; + } + return false; + } + + char consume() { return First != Last ? *First++ : '\0'; } + + char look(unsigned Lookahead = 0) const { + if (static_cast(Last - First) <= Lookahead) return '\0'; + return First[Lookahead]; + } + + size_t numLeft() const { return static_cast(Last - First); } + + std::string_view parseNumber(bool AllowNegative = false); + Qualifiers parseCVQualifiers(); + bool parsePositiveInteger(size_t* Out); + std::string_view parseBareSourceName(); + + bool parseSeqId(size_t* Out); + Node* parseSubstitution(); + Node* parseTemplateParam(); + Node* parseTemplateParamDecl(); + Node* parseTemplateArgs(bool TagTemplates = false); + Node* parseTemplateArg(); + + /// Parse the production. + Node* parseExpr(); + Node* parsePrefixExpr(std::string_view Kind, Node::Prec Prec); + Node* parseBinaryExpr(std::string_view Kind, Node::Prec Prec); + Node* parseIntegerLiteral(std::string_view Lit); + Node* parseExprPrimary(); + template + Node* parseFloatingLiteral(); + Node* parseFunctionParam(); + Node* parseConversionExpr(); + Node* parseBracedExpr(); + Node* parseFoldExpr(); + Node* parsePointerToMemberConversionExpr(Node::Prec Prec); + Node* parseSubobjectExpr(); + + /// Parse the production. + Node* parseType(); + Node* parseFunctionType(); + Node* parseVectorType(); + Node* parseDecltype(); + Node* parseArrayType(); + Node* parsePointerToMemberType(); + Node* parseClassEnumType(); + Node* parseQualifiedType(); + + Node* parseEncoding(); + bool parseCallOffset(); + Node* parseSpecialName(); + + /// Holds some extra information about a that is being parsed. This + /// information is only pertinent if the refers to an . + struct NameState { + bool CtorDtorConversion = false; + bool EndsWithTemplateArgs = false; + Qualifiers CVQualifiers = QualNone; + FunctionRefQual ReferenceQualifier = FrefQualNone; + size_t ForwardTemplateRefsBegin; + + NameState(AbstractManglingParser* Enclosing) + : ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {} + }; + + bool resolveForwardTemplateRefs(NameState& State) { + size_t I = State.ForwardTemplateRefsBegin; + size_t E = ForwardTemplateRefs.size(); + for (; I < E; ++I) { + size_t Idx = ForwardTemplateRefs[I]->Index; + if (TemplateParams.empty() || !TemplateParams[0] || Idx >= TemplateParams[0]->size()) return true; + ForwardTemplateRefs[I]->Ref = (*TemplateParams[0])[Idx]; + } + ForwardTemplateRefs.dropBack(State.ForwardTemplateRefsBegin); + return false; + } + + /// Parse the production> + Node* parseName(NameState* State = nullptr); + Node* parseLocalName(NameState* State); + Node* parseOperatorName(NameState* State); + bool parseModuleNameOpt(ModuleName*& Module); + Node* parseUnqualifiedName(NameState* State, Node* Scope, ModuleName* Module); + Node* parseUnnamedTypeName(NameState* State); + Node* parseSourceName(NameState* State); + Node* parseUnscopedName(NameState* State, bool* isSubstName); + Node* parseNestedName(NameState* State); + Node* parseCtorDtorName(Node*& SoFar, NameState* State); + + Node* parseAbiTags(Node* N); + + struct OperatorInfo { + enum OIKind : unsigned char { + Prefix, // Prefix unary: @ expr + Postfix, // Postfix unary: expr @ + Binary, // Binary: lhs @ rhs + Array, // Array index: lhs [ rhs ] + Member, // Member access: lhs @ rhs + New, // New + Del, // Delete + Call, // Function call: expr (expr*) + CCast, // C cast: (type)expr + Conditional, // Conditional: expr ? expr : expr + NameOnly, // Overload only, not allowed in expression. + // Below do not have operator names + NamedCast, // Named cast, @(expr) + OfIdOp, // alignof, sizeof, typeid + + Unnameable = NamedCast, + }; + char Enc[2]; // Encoding + OIKind Kind; // Kind of operator + bool Flag : 1; // Entry-specific flag + Node::Prec Prec : 7; // Precedence + const char* Name; // Spelling + + public: + constexpr OperatorInfo(const char (&E)[3], OIKind K, bool F, Node::Prec P, const char* N) + : Enc{E[0], E[1]}, + Kind{K}, + Flag{F}, + Prec{P}, + Name{N} {} + + public: + bool operator<(const OperatorInfo& Other) const { return *this < Other.Enc; } + bool operator<(const char* Peek) const { return Enc[0] < Peek[0] || (Enc[0] == Peek[0] && Enc[1] < Peek[1]); } + bool operator==(const char* Peek) const { return Enc[0] == Peek[0] && Enc[1] == Peek[1]; } + bool operator!=(const char* Peek) const { return !this->operator==(Peek); } + + public: + std::string_view getSymbol() const { + std::string_view Res = Name; + if (Kind < Unnameable) { + assert( + demangler::itanium_demangle::starts_with(Res, "operator") + && "operator name does not start with 'operator'" + ); + Res.remove_prefix(sizeof("operator") - 1); + if (demangler::itanium_demangle::starts_with(Res, ' ')) Res.remove_prefix(1); + } + return Res; + } + std::string_view getName() const { return Name; } + OIKind getKind() const { return Kind; } + bool getFlag() const { return Flag; } + Node::Prec getPrecedence() const { return Prec; } + }; + static const OperatorInfo Ops[]; + static const size_t NumOps; + const OperatorInfo* parseOperatorEncoding(); + + /// Parse the production. + Node* parseUnresolvedName(bool Global); + Node* parseSimpleId(); + Node* parseBaseUnresolvedName(); + Node* parseUnresolvedType(); + Node* parseDestructorName(); + + /// Top-level entry point into the parser. + Node* parse(); +}; + +const char* parse_discriminator(const char* first, const char* last); + +// ::= // N +// ::= # See Scope Encoding below // Z +// ::= +// ::= +// +// ::= +// ::= +template +Node* AbstractManglingParser::parseName(NameState* State) { + if (look() == 'N') return getDerived().parseNestedName(State); + if (look() == 'Z') return getDerived().parseLocalName(State); + + Node* Result = nullptr; + bool IsSubst = false; + + Result = getDerived().parseUnscopedName(State, &IsSubst); + if (!Result) return nullptr; + + if (look() == 'I') { + // ::= + if (!IsSubst) + // An unscoped-template-name is substitutable. + Subs.push_back(Result); + Node* TA = getDerived().parseTemplateArgs(State != nullptr); + if (TA == nullptr) return nullptr; + if (State) State->EndsWithTemplateArgs = true; + Result = make(Result, TA); + } else if (IsSubst) { + // The substitution case must be followed by . + return nullptr; + } + + return Result; +} + +// := Z E [] +// := Z E s [] +// := Z Ed [ ] _ +template +Node* AbstractManglingParser::parseLocalName(NameState* State) { + if (!consumeIf('Z')) return nullptr; + Node* Encoding = getDerived().parseEncoding(); + if (Encoding == nullptr || !consumeIf('E')) return nullptr; + + if (consumeIf('s')) { + First = parse_discriminator(First, Last); + auto* StringLitName = make("string literal"); + if (!StringLitName) return nullptr; + return make(Encoding, StringLitName); + } + + if (consumeIf('d')) { + parseNumber(true); + if (!consumeIf('_')) return nullptr; + Node* N = getDerived().parseName(State); + if (N == nullptr) return nullptr; + return make(Encoding, N); + } + + Node* Entity = getDerived().parseName(State); + if (Entity == nullptr) return nullptr; + First = parse_discriminator(First, Last); + return make(Encoding, Entity); +} + +// ::= +// ::= St # ::std:: +// [*] extension +template +Node* AbstractManglingParser::parseUnscopedName(NameState* State, bool* IsSubst) { + + Node* Std = nullptr; + if (consumeIf("St")) { + Std = make("std"); + if (Std == nullptr) return nullptr; + } + + Node* Res = nullptr; + ModuleName* Module = nullptr; + if (look() == 'S') { + Node* S = getDerived().parseSubstitution(); + if (!S) return nullptr; + if (S->getKind() == Node::KModuleName) Module = static_cast(S); + else if (IsSubst && Std == nullptr) { + Res = S; + *IsSubst = true; + } else { + return nullptr; + } + } + + if (Res == nullptr || Std != nullptr) { + Res = getDerived().parseUnqualifiedName(State, Std, Module); + } + + return Res; +} + +// ::= [] L? [] +// ::= [] [] +// ::= [] L? [] +// ::= [] L? [] +// # structured binding declaration +// ::= [] L? DC + E +template +Node* AbstractManglingParser::parseUnqualifiedName(NameState* State, Node* Scope, ModuleName* Module) { + if (getDerived().parseModuleNameOpt(Module)) return nullptr; + + consumeIf('L'); + + Node* Result; + if (look() >= '1' && look() <= '9') { + Result = getDerived().parseSourceName(State); + } else if (look() == 'U') { + Result = getDerived().parseUnnamedTypeName(State); + } else if (consumeIf("DC")) { + // Structured binding + size_t BindingsBegin = Names.size(); + do { + Node* Binding = getDerived().parseSourceName(State); + if (Binding == nullptr) return nullptr; + Names.push_back(Binding); + } while (!consumeIf('E')); + Result = make(popTrailingNodeArray(BindingsBegin)); + } else if (look() == 'C' || look() == 'D') { + // A . + if (Scope == nullptr || Module != nullptr) return nullptr; + Result = getDerived().parseCtorDtorName(Scope, State); + } else { + Result = getDerived().parseOperatorName(State); + } + + if (Result != nullptr && Module != nullptr) Result = make(Module, Result); + if (Result != nullptr) Result = getDerived().parseAbiTags(Result); + if (Result != nullptr && Scope != nullptr) Result = make(Scope, Result); + + return Result; +} + +// ::= +// ::= +// ::= # passed in by caller +// ::= W +// ::= W P +template +bool AbstractManglingParser::parseModuleNameOpt(ModuleName*& Module) { + while (consumeIf('W')) { + bool IsPartition = consumeIf('P'); + Node* Sub = getDerived().parseSourceName(nullptr); + if (!Sub) return true; + Module = static_cast(make(Module, Sub, IsPartition)); + Subs.push_back(Module); + } + + return false; +} + +// ::= Ut [] _ +// ::= +// +// ::= Ul E [ ] _ +// +// ::= + # Parameter types or "v" if the lambda has no parameters +template +Node* AbstractManglingParser::parseUnnamedTypeName(NameState* State) { + // refer to the innermost . Clear out any + // outer args that we may have inserted into TemplateParams. + if (State != nullptr) TemplateParams.clear(); + + if (consumeIf("Ut")) { + std::string_view Count = parseNumber(); + if (!consumeIf('_')) return nullptr; + return make(Count); + } + if (consumeIf("Ul")) { + ScopedOverride SwapParams(ParsingLambdaParamsAtLevel, TemplateParams.size()); + ScopedTemplateParamList LambdaTemplateParams(this); + + size_t ParamsBegin = Names.size(); + while (look() == 'T' && std::string_view("yptn").find(look(1)) != std::string_view::npos) { + Node* T = parseTemplateParamDecl(); + if (!T) return nullptr; + Names.push_back(T); + } + NodeArray TempParams = popTrailingNodeArray(ParamsBegin); + + // FIXME: If TempParams is empty and none of the function parameters + // includes 'auto', we should remove LambdaTemplateParams from the + // TemplateParams list. Unfortunately, we don't find out whether there are + // any 'auto' parameters until too late in an example such as: + // + // template void f( + // decltype([](decltype([](T v) {}), + // auto) {})) {} + // template void f( + // decltype([](decltype([](T w) {}), + // int) {})) {} + // + // Here, the type of v is at level 2 but the type of w is at level 1. We + // don't find this out until we encounter the type of the next parameter. + // + // However, compilers can't actually cope with the former example in + // practice, and it's likely to be made ill-formed in future, so we don't + // need to support it here. + // + // If we encounter an 'auto' in the function parameter types, we will + // recreate a template parameter scope for it, but any intervening lambdas + // will be parsed in the 'wrong' template parameter depth. + if (TempParams.empty()) TemplateParams.pop_back(); + + if (!consumeIf("vE")) { + do { + Node* P = getDerived().parseType(); + if (P == nullptr) return nullptr; + Names.push_back(P); + } while (!consumeIf('E')); + } + NodeArray Params = popTrailingNodeArray(ParamsBegin); + + std::string_view Count = parseNumber(); + if (!consumeIf('_')) return nullptr; + return make(TempParams, Params, Count); + } + if (consumeIf("Ub")) { + (void)parseNumber(); + if (!consumeIf('_')) return nullptr; + return make("'block-literal'"); + } + return nullptr; +} + +// ::= +template +Node* AbstractManglingParser::parseSourceName(NameState*) { + size_t Length = 0; + if (parsePositiveInteger(&Length)) return nullptr; + if (numLeft() < Length || Length == 0) return nullptr; + std::string_view Name(First, Length); + First += Length; + if (demangler::itanium_demangle::starts_with(Name, "_GLOBAL__N")) return make("(anonymous namespace)"); + return make(Name); +} + +// Operator encodings +template +const typename AbstractManglingParser::OperatorInfo AbstractManglingParser::Ops[] = { + // Keep ordered by encoding + {"aN", OperatorInfo::Binary, false, Node::Prec::Assign, "operator&=" }, + {"aS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator=" }, + {"aa", OperatorInfo::Binary, false, Node::Prec::AndIf, "operator&&" }, + {"ad", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator&" }, + {"an", OperatorInfo::Binary, false, Node::Prec::And, "operator&" }, + {"at", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, "alignof " }, + {"aw", OperatorInfo::NameOnly, false, Node::Prec::Primary, "operator co_await"}, + {"az", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, "alignof " }, + {"cc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "const_cast" }, + {"cl", OperatorInfo::Call, false, Node::Prec::Postfix, "operator()" }, + {"cm", OperatorInfo::Binary, false, Node::Prec::Comma, "operator," }, + {"co", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator~" }, + {"cv", OperatorInfo::CCast, false, Node::Prec::Cast, "operator" }, // C Cast + {"dV", OperatorInfo::Binary, false, Node::Prec::Assign, "operator/=" }, + {"da", OperatorInfo::Del, /*Ary*/ true, Node::Prec::Unary, "operator delete[]"}, + {"dc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "dynamic_cast" }, + {"de", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator*" }, + {"dl", OperatorInfo::Del, /*Ary*/ false, Node::Prec::Unary, "operator delete" }, + {"ds", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem, "operator.*" }, + {"dt", OperatorInfo::Member, /*Named*/ false, Node::Prec::Postfix, "operator." }, + {"dv", OperatorInfo::Binary, false, Node::Prec::Assign, "operator/" }, + {"eO", OperatorInfo::Binary, false, Node::Prec::Assign, "operator^=" }, + {"eo", OperatorInfo::Binary, false, Node::Prec::Xor, "operator^" }, + {"eq", OperatorInfo::Binary, false, Node::Prec::Equality, "operator==" }, + {"ge", OperatorInfo::Binary, false, Node::Prec::Relational, "operator>=" }, + {"gt", OperatorInfo::Binary, false, Node::Prec::Relational, "operator>" }, + {"ix", OperatorInfo::Array, false, Node::Prec::Postfix, "operator[]" }, + {"lS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator<<=" }, + {"le", OperatorInfo::Binary, false, Node::Prec::Relational, "operator<=" }, + {"ls", OperatorInfo::Binary, false, Node::Prec::Shift, "operator<<" }, + {"lt", OperatorInfo::Binary, false, Node::Prec::Relational, "operator<" }, + {"mI", OperatorInfo::Binary, false, Node::Prec::Assign, "operator-=" }, + {"mL", OperatorInfo::Binary, false, Node::Prec::Assign, "operator*=" }, + {"mi", OperatorInfo::Binary, false, Node::Prec::Additive, "operator-" }, + {"ml", OperatorInfo::Binary, false, Node::Prec::Multiplicative, "operator*" }, + {"mm", OperatorInfo::Postfix, false, Node::Prec::Postfix, "operator--" }, + {"na", OperatorInfo::New, /*Ary*/ true, Node::Prec::Unary, "operator new[]" }, + {"ne", OperatorInfo::Binary, false, Node::Prec::Equality, "operator!=" }, + {"ng", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator-" }, + {"nt", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator!" }, + {"nw", OperatorInfo::New, /*Ary*/ false, Node::Prec::Unary, "operator new" }, + {"oR", OperatorInfo::Binary, false, Node::Prec::Assign, "operator|=" }, + {"oo", OperatorInfo::Binary, false, Node::Prec::OrIf, "operator||" }, + {"or", OperatorInfo::Binary, false, Node::Prec::Ior, "operator|" }, + {"pL", OperatorInfo::Binary, false, Node::Prec::Assign, "operator+=" }, + {"pl", OperatorInfo::Binary, false, Node::Prec::Additive, "operator+" }, + {"pm", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem, "operator->*" }, + {"pp", OperatorInfo::Postfix, false, Node::Prec::Postfix, "operator++" }, + {"ps", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator+" }, + {"pt", OperatorInfo::Member, /*Named*/ true, Node::Prec::Postfix, "operator->" }, + {"qu", OperatorInfo::Conditional, false, Node::Prec::Conditional, "operator?" }, + {"rM", OperatorInfo::Binary, false, Node::Prec::Assign, "operator%=" }, + {"rS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator>>=" }, + {"rc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "reinterpret_cast" }, + {"rm", OperatorInfo::Binary, false, Node::Prec::Multiplicative, "operator%" }, + {"rs", OperatorInfo::Binary, false, Node::Prec::Shift, "operator>>" }, + {"sc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "static_cast" }, + {"ss", OperatorInfo::Binary, false, Node::Prec::Spaceship, "operator<=>" }, + {"st", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, "sizeof " }, + {"sz", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, "sizeof " }, + {"te", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Postfix, "typeid " }, + {"ti", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Postfix, "typeid " }, +}; +template +const size_t AbstractManglingParser::NumOps = sizeof(Ops) / sizeof(Ops[0]); + +// If the next 2 chars are an operator encoding, consume them and return their +// OperatorInfo. Otherwise return nullptr. +template +const typename AbstractManglingParser::OperatorInfo* +AbstractManglingParser::parseOperatorEncoding() { + if (numLeft() < 2) return nullptr; + + // We can't use lower_bound as that can link to symbols in the C++ library, + // and this must remain independant of that. + size_t lower = 0u, upper = NumOps - 1; // Inclusive bounds. + while (upper != lower) { + size_t middle = (upper + lower) / 2; + if (Ops[middle] < First) lower = middle + 1; + else upper = middle; + } + if (Ops[lower] != First) return nullptr; + + First += 2; + return &Ops[lower]; +} + +// ::= See parseOperatorEncoding() +// ::= li # operator "" +// ::= v # vendor extended operator +template +Node* AbstractManglingParser::parseOperatorName(NameState* State) { + if (const auto* Op = parseOperatorEncoding()) { + if (Op->getKind() == OperatorInfo::CCast) { + // ::= cv # (cast) + ScopedOverride SaveTemplate(TryToParseTemplateArgs, false); + // If we're parsing an encoding, State != nullptr and the conversion + // operators' could have a that refers to some + // s further ahead in the mangled name. + ScopedOverride SavePermit( + PermitForwardTemplateReferences, + PermitForwardTemplateReferences || State != nullptr + ); + Node* Ty = getDerived().parseType(); + if (Ty == nullptr) return nullptr; + if (State) State->CtorDtorConversion = true; + return make(Ty); + } + + if (Op->getKind() >= OperatorInfo::Unnameable) /* Not a nameable operator. */ + return nullptr; + if (Op->getKind() == OperatorInfo::Member && !Op->getFlag()) /* Not a nameable MemberExpr */ + return nullptr; + + return make(Op->getName()); + } + + if (consumeIf("li")) { + // ::= li # operator "" + Node* SN = getDerived().parseSourceName(State); + if (SN == nullptr) return nullptr; + return make(SN); + } + + if (consumeIf('v')) { + // ::= v # vendor extended operator + if (look() >= '0' && look() <= '9') { + First++; + Node* SN = getDerived().parseSourceName(State); + if (SN == nullptr) return nullptr; + return make(SN); + } + return nullptr; + } + + return nullptr; +} + +// ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// extension ::= C4 # gcc old-style "[unified]" constructor +// extension ::= C5 # the COMDAT used for ctors +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor +// extension ::= D4 # gcc old-style "[unified]" destructor +// extension ::= D5 # the COMDAT used for dtors +template +Node* AbstractManglingParser::parseCtorDtorName(Node*& SoFar, NameState* State) { + if (SoFar->getKind() == Node::KSpecialSubstitution) { + // Expand the special substitution. + SoFar = make(static_cast(SoFar)); + if (!SoFar) return nullptr; + } + + if (consumeIf('C')) { + bool IsInherited = consumeIf('I'); + if (look() != '1' && look() != '2' && look() != '3' && look() != '4' && look() != '5') return nullptr; + int Variant = look() - '0'; + ++First; + if (State) State->CtorDtorConversion = true; + if (IsInherited) { + if (getDerived().parseName(State) == nullptr) return nullptr; + } + return make(SoFar, /*IsDtor=*/false, Variant); + } + + if (look() == 'D' && (look(1) == '0' || look(1) == '1' || look(1) == '2' || look(1) == '4' || look(1) == '5')) { + int Variant = look(1) - '0'; + First += 2; + if (State) State->CtorDtorConversion = true; + return make(SoFar, /*IsDtor=*/true, Variant); + } + + return nullptr; +} + +// ::= N [] [] +// E +// ::= N [] [] +// E +// +// ::= +// ::= +// ::= +// ::= +// ::= # empty +// ::= +// ::= +// [*] extension +// +// := [] M +// +// ::=