//===------------------------- ItaniumDemangle.h ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Generic itanium demangler library. This file has two byte-per-byte identical // copies in the source tree, one in libcxxabi, and the other in llvm. // //===----------------------------------------------------------------------===// #ifndef LLVM_DEMANGLE_ITANIUMDEMANGLE_H #define LLVM_DEMANGLE_ITANIUMDEMANGLE_H // FIXME: (possibly) incomplete list of features that clang mangles that this // file does not yet support: // - C++ modules TS #include "DemangleConfig.h" #include "StringView.h" #include "Utility.h" #include #include #include #include #include #include #include #define FOR_EACH_NODE_KIND(X) \ X(NodeArrayNode) \ X(DotSuffix) \ X(VendorExtQualType) \ X(QualType) \ X(ConversionOperatorType) \ X(PostfixQualifiedType) \ X(ElaboratedTypeSpefType) \ X(NameType) \ X(AbiTagAttr) \ X(EnableIfAttr) \ X(ObjCProtoName) \ X(PointerType) \ X(ReferenceType) \ X(PointerToMemberType) \ X(ArrayType) \ X(FunctionType) \ X(NoexceptSpec) \ X(DynamicExceptionSpec) \ X(FunctionEncoding) \ X(LiteralOperator) \ X(SpecialName) \ X(CtorVtableSpecialName) \ X(QualifiedName) \ X(NestedName) \ X(LocalName) \ X(VectorType) \ X(PixelVectorType) \ X(SyntheticTemplateParamName) \ X(TypeTemplateParamDecl) \ X(NonTypeTemplateParamDecl) \ X(TemplateTemplateParamDecl) \ X(TemplateParamPackDecl) \ X(ParameterPack) \ X(TemplateArgumentPack) \ X(ParameterPackExpansion) \ X(TemplateArgs) \ X(ForwardTemplateReference) \ X(NameWithTemplateArgs) \ X(GlobalQualifiedName) \ X(StdQualifiedName) \ X(ExpandedSpecialSubstitution) \ X(SpecialSubstitution) \ X(CtorDtorName) \ X(DtorName) \ X(UnnamedTypeName) \ X(ClosureTypeName) \ X(StructuredBindingName) \ X(BinaryExpr) \ X(ArraySubscriptExpr) \ X(PostfixExpr) \ X(ConditionalExpr) \ X(MemberExpr) \ X(SubobjectExpr) \ X(EnclosingExpr) \ X(CastExpr) \ X(SizeofParamPackExpr) \ X(CallExpr) \ X(NewExpr) \ X(DeleteExpr) \ X(PrefixExpr) \ X(FunctionParam) \ X(ConversionExpr) \ X(PointerToMemberConversionExpr) \ X(InitListExpr) \ X(FoldExpr) \ X(ThrowExpr) \ X(BoolExpr) \ X(StringLiteral) \ X(LambdaExpr) \ X(EnumLiteral) \ X(IntegerLiteral) \ X(FloatLiteral) \ X(DoubleLiteral) \ X(LongDoubleLiteral) \ X(BracedExpr) \ X(BracedRangeExpr) DEMANGLE_NAMESPACE_BEGIN // 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 ENUMERATOR(NodeKind) K ## NodeKind, FOR_EACH_NODE_KIND(ENUMERATOR) #undef ENUMERATOR }; /// 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, }; private: Kind K; // 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; /// Track if this node is a (possibly qualified) array type. This can affect /// how we format the output string. Cache ArrayCache; /// Track if this node is a (possibly qualified) function type. This can /// affect how we format the output string. Cache FunctionCache; public: Node(Kind K_, Cache RHSComponentCache_ = Cache::No, Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No) : K(K_), RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_), FunctionCache(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(OutputStream &S) const { if (RHSComponentCache != Cache::Unknown) return RHSComponentCache == Cache::Yes; return hasRHSComponentSlow(S); } bool hasArray(OutputStream &S) const { if (ArrayCache != Cache::Unknown) return ArrayCache == Cache::Yes; return hasArraySlow(S); } bool hasFunction(OutputStream &S) const { if (FunctionCache != Cache::Unknown) return FunctionCache == Cache::Yes; return hasFunctionSlow(S); } Kind getKind() const { return K; } virtual bool hasRHSComponentSlow(OutputStream &) const { return false; } virtual bool hasArraySlow(OutputStream &) const { return false; } virtual bool hasFunctionSlow(OutputStream &) 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(OutputStream &) const { return this; } void print(OutputStream &S) const { printLeft(S); if (RHSComponentCache != Cache::No) printRight(S); } // Print the "left" side of this Node into OutputStream. virtual void printLeft(OutputStream &) 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(OutputStream &) const {} virtual StringView getBaseName() const { return StringView(); } // 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(OutputStream &S) const { bool FirstElement = true; for (size_t Idx = 0; Idx != NumElements; ++Idx) { size_t BeforeComma = S.getCurrentPosition(); if (!FirstElement) S += ", "; size_t AfterComma = S.getCurrentPosition(); Elements[Idx]->print(S); // Elements[Idx] is an empty parameter pack expansion, we should erase the // comma we just printed. if (AfterComma == S.getCurrentPosition()) { S.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(OutputStream &S) const override { Array.printWithComma(S); } }; class DotSuffix final : public Node { const Node *Prefix; const StringView Suffix; public: DotSuffix(const Node *Prefix_, StringView Suffix_) : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {} template void match(Fn F) const { F(Prefix, Suffix); } void printLeft(OutputStream &s) const override { Prefix->print(s); s += " ("; s += Suffix; s += ")"; } }; class VendorExtQualType final : public Node { const Node *Ty; StringView Ext; const Node *TA; public: VendorExtQualType(const Node *Ty_, StringView Ext_, const Node *TA_) : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_), TA(TA_) {} template void match(Fn F) const { F(Ty, Ext, TA); } void printLeft(OutputStream &S) const override { Ty->print(S); S += " "; S += Ext; if (TA != nullptr) TA->print(S); } }; 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(OutputStream &S) const { if (Quals & QualConst) S += " const"; if (Quals & QualVolatile) S += " volatile"; if (Quals & QualRestrict) S += " restrict"; } public: QualType(const Node *Child_, Qualifiers Quals_) : Node(KQualType, Child_->RHSComponentCache, Child_->ArrayCache, Child_->FunctionCache), Quals(Quals_), Child(Child_) {} template void match(Fn F) const { F(Child, Quals); } bool hasRHSComponentSlow(OutputStream &S) const override { return Child->hasRHSComponent(S); } bool hasArraySlow(OutputStream &S) const override { return Child->hasArray(S); } bool hasFunctionSlow(OutputStream &S) const override { return Child->hasFunction(S); } void printLeft(OutputStream &S) const override { Child->printLeft(S); printQuals(S); } void printRight(OutputStream &S) const override { Child->printRight(S); } }; 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(OutputStream &S) const override { S += "operator "; Ty->print(S); } }; class PostfixQualifiedType final : public Node { const Node *Ty; const StringView Postfix; public: PostfixQualifiedType(Node *Ty_, StringView Postfix_) : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {} template void match(Fn F) const { F(Ty, Postfix); } void printLeft(OutputStream &s) const override { Ty->printLeft(s); s += Postfix; } }; class NameType final : public Node { const StringView Name; public: NameType(StringView Name_) : Node(KNameType), Name(Name_) {} template void match(Fn F) const { F(Name); } StringView getName() const { return Name; } StringView getBaseName() const override { return Name; } void printLeft(OutputStream &s) const override { s += Name; } }; class ElaboratedTypeSpefType : public Node { StringView Kind; Node *Child; public: ElaboratedTypeSpefType(StringView Kind_, Node *Child_) : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {} template void match(Fn F) const { F(Kind, Child); } void printLeft(OutputStream &S) const override { S += Kind; S += ' '; Child->print(S); } }; struct AbiTagAttr : Node { Node *Base; StringView Tag; AbiTagAttr(Node* Base_, StringView Tag_) : Node(KAbiTagAttr, Base_->RHSComponentCache, Base_->ArrayCache, Base_->FunctionCache), Base(Base_), Tag(Tag_) {} template void match(Fn F) const { F(Base, Tag); } void printLeft(OutputStream &S) const override { Base->printLeft(S); S += "[abi:"; S += Tag; S += "]"; } }; class EnableIfAttr : public Node { NodeArray Conditions; public: EnableIfAttr(NodeArray Conditions_) : Node(KEnableIfAttr), Conditions(Conditions_) {} template void match(Fn F) const { F(Conditions); } void printLeft(OutputStream &S) const override { S += " [enable_if:"; Conditions.printWithComma(S); S += ']'; } }; class ObjCProtoName : public Node { const Node *Ty; StringView Protocol; friend class PointerType; public: ObjCProtoName(const Node *Ty_, StringView 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(OutputStream &S) const override { Ty->print(S); S += "<"; S += Protocol; S += ">"; } }; class PointerType final : public Node { const Node *Pointee; public: PointerType(const Node *Pointee_) : Node(KPointerType, Pointee_->RHSComponentCache), Pointee(Pointee_) {} template void match(Fn F) const { F(Pointee); } bool hasRHSComponentSlow(OutputStream &S) const override { return Pointee->hasRHSComponent(S); } void printLeft(OutputStream &s) const override { // We rewrite objc_object* into id. if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { Pointee->printLeft(s); if (Pointee->hasArray(s)) s += " "; if (Pointee->hasArray(s) || Pointee->hasFunction(s)) s += "("; s += "*"; } else { const auto *objcProto = static_cast(Pointee); s += "id<"; s += objcProto->Protocol; s += ">"; } } void printRight(OutputStream &s) const override { if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { if (Pointee->hasArray(s) || Pointee->hasFunction(s)) s += ")"; Pointee->printRight(s); } } }; 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. std::pair collapse(OutputStream &S) const { auto SoFar = std::make_pair(RK, Pointee); for (;;) { const Node *SN = SoFar.second->getSyntaxNode(S); if (SN->getKind() != KReferenceType) break; auto *RT = static_cast(SN); SoFar.second = RT->Pointee; SoFar.first = std::min(SoFar.first, RT->RK); } 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(OutputStream &S) const override { return Pointee->hasRHSComponent(S); } void printLeft(OutputStream &s) const override { if (Printing) return; SwapAndRestore SavePrinting(Printing, true); std::pair Collapsed = collapse(s); Collapsed.second->printLeft(s); if (Collapsed.second->hasArray(s)) s += " "; if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) s += "("; s += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); } void printRight(OutputStream &s) const override { if (Printing) return; SwapAndRestore SavePrinting(Printing, true); std::pair Collapsed = collapse(s); if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) s += ")"; Collapsed.second->printRight(s); } }; 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(OutputStream &S) const override { return MemberType->hasRHSComponent(S); } void printLeft(OutputStream &s) const override { MemberType->printLeft(s); if (MemberType->hasArray(s) || MemberType->hasFunction(s)) s += "("; else s += " "; ClassType->print(s); s += "::*"; } void printRight(OutputStream &s) const override { if (MemberType->hasArray(s) || MemberType->hasFunction(s)) s += ")"; MemberType->printRight(s); } }; 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(OutputStream &) const override { return true; } bool hasArraySlow(OutputStream &) const override { return true; } void printLeft(OutputStream &S) const override { Base->printLeft(S); } void printRight(OutputStream &S) const override { if (S.back() != ']') S += " "; S += "["; if (Dimension) Dimension->print(S); S += "]"; Base->printRight(S); } }; 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(OutputStream &) const override { return true; } bool hasFunctionSlow(OutputStream &) 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(OutputStream &S) const override { Ret->printLeft(S); S += " "; } void printRight(OutputStream &S) const override { S += "("; Params.printWithComma(S); S += ")"; Ret->printRight(S); if (CVQuals & QualConst) S += " const"; if (CVQuals & QualVolatile) S += " volatile"; if (CVQuals & QualRestrict) S += " restrict"; if (RefQual == FrefQualLValue) S += " &"; else if (RefQual == FrefQualRValue) S += " &&"; if (ExceptionSpec != nullptr) { S += ' '; ExceptionSpec->print(S); } } }; 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(OutputStream &S) const override { S += "noexcept("; E->print(S); S += ")"; } }; class DynamicExceptionSpec : public Node { NodeArray Types; public: DynamicExceptionSpec(NodeArray Types_) : Node(KDynamicExceptionSpec), Types(Types_) {} template void match(Fn F) const { F(Types); } void printLeft(OutputStream &S) const override { S += "throw("; Types.printWithComma(S); S += ')'; } }; 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(OutputStream &) const override { return true; } bool hasFunctionSlow(OutputStream &) const override { return true; } const Node *getName() const { return Name; } void printLeft(OutputStream &S) const override { if (Ret) { Ret->printLeft(S); if (!Ret->hasRHSComponent(S)) S += " "; } Name->print(S); } void printRight(OutputStream &S) const override { S += "("; Params.printWithComma(S); S += ")"; if (Ret) Ret->printRight(S); if (CVQuals & QualConst) S += " const"; if (CVQuals & QualVolatile) S += " volatile"; if (CVQuals & QualRestrict) S += " restrict"; if (RefQual == FrefQualLValue) S += " &"; else if (RefQual == FrefQualRValue) S += " &&"; if (Attrs != nullptr) Attrs->print(S); } }; 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(OutputStream &S) const override { S += "operator\"\" "; OpName->print(S); } }; class SpecialName final : public Node { const StringView Special; const Node *Child; public: SpecialName(StringView Special_, const Node *Child_) : Node(KSpecialName), Special(Special_), Child(Child_) {} template void match(Fn F) const { F(Special, Child); } void printLeft(OutputStream &S) const override { S += Special; Child->print(S); } }; 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(OutputStream &S) const override { S += "construction vtable for "; FirstType->print(S); S += "-in-"; SecondType->print(S); } }; 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); } StringView getBaseName() const override { return Name->getBaseName(); } void printLeft(OutputStream &S) const override { Qual->print(S); S += "::"; Name->print(S); } }; 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(OutputStream &S) const override { Encoding->print(S); S += "::"; Entity->print(S); } }; 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); } StringView getBaseName() const override { return Name->getBaseName(); } void printLeft(OutputStream &S) const override { Qualifier->print(S); S += "::"; Name->print(S); } }; class VectorType final : public Node { const Node *BaseType; const Node *Dimension; public: VectorType(const Node *BaseType_, Node *Dimension_) : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_) {} template void match(Fn F) const { F(BaseType, Dimension); } void printLeft(OutputStream &S) const override { BaseType->print(S); S += " vector["; if (Dimension) Dimension->print(S); S += "]"; } }; 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(OutputStream &S) const override { // FIXME: This should demangle as "vector pixel". S += "pixel vector["; Dimension->print(S); S += "]"; } }; 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(OutputStream &S) const override { switch (Kind) { case TemplateParamKind::Type: S += "$T"; break; case TemplateParamKind::NonType: S += "$N"; break; case TemplateParamKind::Template: S += "$TT"; break; } if (Index > 0) S << 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(OutputStream &S) const override { S += "typename "; } void printRight(OutputStream &S) const override { Name->print(S); } }; /// 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(OutputStream &S) const override { Type->printLeft(S); if (!Type->hasRHSComponent(S)) S += " "; } void printRight(OutputStream &S) const override { Name->print(S); Type->printRight(S); } }; /// 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(OutputStream &S) const override { S += "template<"; Params.printWithComma(S); S += "> typename "; } void printRight(OutputStream &S) const override { Name->print(S); } }; /// 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(OutputStream &S) const override { Param->printLeft(S); S += "..."; } void printRight(OutputStream &S) const override { Param->printRight(S); } }; /// 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 OutputStream for a pack expansion unless we're already expanding one. void initializePackExpansion(OutputStream &S) const { if (S.CurrentPackMax == std::numeric_limits::max()) { S.CurrentPackMax = static_cast(Data.size()); S.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(OutputStream &S) const override { initializePackExpansion(S); size_t Idx = S.CurrentPackIndex; return Idx < Data.size() && Data[Idx]->hasRHSComponent(S); } bool hasArraySlow(OutputStream &S) const override { initializePackExpansion(S); size_t Idx = S.CurrentPackIndex; return Idx < Data.size() && Data[Idx]->hasArray(S); } bool hasFunctionSlow(OutputStream &S) const override { initializePackExpansion(S); size_t Idx = S.CurrentPackIndex; return Idx < Data.size() && Data[Idx]->hasFunction(S); } const Node *getSyntaxNode(OutputStream &S) const override { initializePackExpansion(S); size_t Idx = S.CurrentPackIndex; return Idx < Data.size() ? Data[Idx]->getSyntaxNode(S) : this; } void printLeft(OutputStream &S) const override { initializePackExpansion(S); size_t Idx = S.CurrentPackIndex; if (Idx < Data.size()) Data[Idx]->printLeft(S); } void printRight(OutputStream &S) const override { initializePackExpansion(S); size_t Idx = S.CurrentPackIndex; if (Idx < Data.size()) Data[Idx]->printRight(S); } }; /// 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(OutputStream &S) const override { Elements.printWithComma(S); } }; /// 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(OutputStream &S) const override { constexpr unsigned Max = std::numeric_limits::max(); SwapAndRestore SavePackIdx(S.CurrentPackIndex, Max); SwapAndRestore SavePackMax(S.CurrentPackMax, Max); size_t StreamPos = S.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(S); // No ParameterPack was found in Child. This can occur if we've found a pack // expansion on a . if (S.CurrentPackMax == Max) { S += "..."; return; } // We found a ParameterPack, but it has no elements. Erase whatever we may // of printed. if (S.CurrentPackMax == 0) { S.setCurrentPosition(StreamPos); return; } // Else, iterate through the rest of the elements in the pack. for (unsigned I = 1, E = S.CurrentPackMax; I < E; ++I) { S += ", "; S.CurrentPackIndex = I; Child->print(S); } } }; 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(OutputStream &S) const override { S += "<"; Params.printWithComma(S); if (S.back() == '>') S += " "; S += ">"; } }; /// 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(OutputStream &S) const override { if (Printing) return false; SwapAndRestore SavePrinting(Printing, true); return Ref->hasRHSComponent(S); } bool hasArraySlow(OutputStream &S) const override { if (Printing) return false; SwapAndRestore SavePrinting(Printing, true); return Ref->hasArray(S); } bool hasFunctionSlow(OutputStream &S) const override { if (Printing) return false; SwapAndRestore SavePrinting(Printing, true); return Ref->hasFunction(S); } const Node *getSyntaxNode(OutputStream &S) const override { if (Printing) return this; SwapAndRestore SavePrinting(Printing, true); return Ref->getSyntaxNode(S); } void printLeft(OutputStream &S) const override { if (Printing) return; SwapAndRestore SavePrinting(Printing, true); Ref->printLeft(S); } void printRight(OutputStream &S) const override { if (Printing) return; SwapAndRestore SavePrinting(Printing, true); Ref->printRight(S); } }; 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); } StringView getBaseName() const override { return Name->getBaseName(); } void printLeft(OutputStream &S) const override { Name->print(S); TemplateArgs->print(S); } }; class GlobalQualifiedName final : public Node { Node *Child; public: GlobalQualifiedName(Node* Child_) : Node(KGlobalQualifiedName), Child(Child_) {} template void match(Fn F) const { F(Child); } StringView getBaseName() const override { return Child->getBaseName(); } void printLeft(OutputStream &S) const override { S += "::"; Child->print(S); } }; struct StdQualifiedName : Node { Node *Child; StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {} template void match(Fn F) const { F(Child); } StringView getBaseName() const override { return Child->getBaseName(); } void printLeft(OutputStream &S) const override { S += "std::"; Child->print(S); } }; enum class SpecialSubKind { allocator, basic_string, string, istream, ostream, iostream, }; class ExpandedSpecialSubstitution final : public Node { SpecialSubKind SSK; public: ExpandedSpecialSubstitution(SpecialSubKind SSK_) : Node(KExpandedSpecialSubstitution), SSK(SSK_) {} template void match(Fn F) const { F(SSK); } StringView getBaseName() const override { switch (SSK) { case SpecialSubKind::allocator: return StringView("allocator"); case SpecialSubKind::basic_string: return StringView("basic_string"); case SpecialSubKind::string: return StringView("basic_string"); case SpecialSubKind::istream: return StringView("basic_istream"); case SpecialSubKind::ostream: return StringView("basic_ostream"); case SpecialSubKind::iostream: return StringView("basic_iostream"); } DEMANGLE_UNREACHABLE; } void printLeft(OutputStream &S) const override { switch (SSK) { case SpecialSubKind::allocator: S += "std::allocator"; break; case SpecialSubKind::basic_string: S += "std::basic_string"; break; case SpecialSubKind::string: S += "std::basic_string, " "std::allocator >"; break; case SpecialSubKind::istream: S += "std::basic_istream >"; break; case SpecialSubKind::ostream: S += "std::basic_ostream >"; break; case SpecialSubKind::iostream: S += "std::basic_iostream >"; break; } } }; class SpecialSubstitution final : public Node { public: SpecialSubKind SSK; SpecialSubstitution(SpecialSubKind SSK_) : Node(KSpecialSubstitution), SSK(SSK_) {} template void match(Fn F) const { F(SSK); } StringView getBaseName() const override { switch (SSK) { case SpecialSubKind::allocator: return StringView("allocator"); case SpecialSubKind::basic_string: return StringView("basic_string"); case SpecialSubKind::string: return StringView("string"); case SpecialSubKind::istream: return StringView("istream"); case SpecialSubKind::ostream: return StringView("ostream"); case SpecialSubKind::iostream: return StringView("iostream"); } DEMANGLE_UNREACHABLE; } void printLeft(OutputStream &S) const override { switch (SSK) { case SpecialSubKind::allocator: S += "std::allocator"; break; case SpecialSubKind::basic_string: S += "std::basic_string"; break; case SpecialSubKind::string: S += "std::string"; break; case SpecialSubKind::istream: S += "std::istream"; break; case SpecialSubKind::ostream: S += "std::ostream"; break; case SpecialSubKind::iostream: S += "std::iostream"; break; } } }; 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(OutputStream &S) const override { if (IsDtor) S += "~"; S += 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(OutputStream &S) const override { S += "~"; Base->printLeft(S); } }; class UnnamedTypeName : public Node { const StringView Count; public: UnnamedTypeName(StringView Count_) : Node(KUnnamedTypeName), Count(Count_) {} template void match(Fn F) const { F(Count); } void printLeft(OutputStream &S) const override { S += "'unnamed"; S += Count; S += "\'"; } }; class ClosureTypeName : public Node { NodeArray TemplateParams; NodeArray Params; StringView Count; public: ClosureTypeName(NodeArray TemplateParams_, NodeArray Params_, StringView Count_) : Node(KClosureTypeName), TemplateParams(TemplateParams_), Params(Params_), Count(Count_) {} template void match(Fn F) const { F(TemplateParams, Params, Count); } void printDeclarator(OutputStream &S) const { if (!TemplateParams.empty()) { S += "<"; TemplateParams.printWithComma(S); S += ">"; } S += "("; Params.printWithComma(S); S += ")"; } void printLeft(OutputStream &S) const override { S += "\'lambda"; S += Count; S += "\'"; printDeclarator(S); } }; class StructuredBindingName : public Node { NodeArray Bindings; public: StructuredBindingName(NodeArray Bindings_) : Node(KStructuredBindingName), Bindings(Bindings_) {} template void match(Fn F) const { F(Bindings); } void printLeft(OutputStream &S) const override { S += '['; Bindings.printWithComma(S); S += ']'; } }; // -- Expression Nodes -- class BinaryExpr : public Node { const Node *LHS; const StringView InfixOperator; const Node *RHS; public: BinaryExpr(const Node *LHS_, StringView InfixOperator_, const Node *RHS_) : Node(KBinaryExpr), LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) { } template void match(Fn F) const { F(LHS, InfixOperator, RHS); } void printLeft(OutputStream &S) const override { // might be a template argument expression, then we need to disambiguate // with parens. if (InfixOperator == ">") S += "("; S += "("; LHS->print(S); S += ") "; S += InfixOperator; S += " ("; RHS->print(S); S += ")"; if (InfixOperator == ">") S += ")"; } }; class ArraySubscriptExpr : public Node { const Node *Op1; const Node *Op2; public: ArraySubscriptExpr(const Node *Op1_, const Node *Op2_) : Node(KArraySubscriptExpr), Op1(Op1_), Op2(Op2_) {} template void match(Fn F) const { F(Op1, Op2); } void printLeft(OutputStream &S) const override { S += "("; Op1->print(S); S += ")["; Op2->print(S); S += "]"; } }; class PostfixExpr : public Node { const Node *Child; const StringView Operator; public: PostfixExpr(const Node *Child_, StringView Operator_) : Node(KPostfixExpr), Child(Child_), Operator(Operator_) {} template void match(Fn F) const { F(Child, Operator); } void printLeft(OutputStream &S) const override { S += "("; Child->print(S); S += ")"; S += 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_) : Node(KConditionalExpr), Cond(Cond_), Then(Then_), Else(Else_) {} template void match(Fn F) const { F(Cond, Then, Else); } void printLeft(OutputStream &S) const override { S += "("; Cond->print(S); S += ") ? ("; Then->print(S); S += ") : ("; Else->print(S); S += ")"; } }; class MemberExpr : public Node { const Node *LHS; const StringView Kind; const Node *RHS; public: MemberExpr(const Node *LHS_, StringView Kind_, const Node *RHS_) : Node(KMemberExpr), LHS(LHS_), Kind(Kind_), RHS(RHS_) {} template void match(Fn F) const { F(LHS, Kind, RHS); } void printLeft(OutputStream &S) const override { LHS->print(S); S += Kind; RHS->print(S); } }; class SubobjectExpr : public Node { const Node *Type; const Node *SubExpr; StringView Offset; NodeArray UnionSelectors; bool OnePastTheEnd; public: SubobjectExpr(const Node *Type_, const Node *SubExpr_, StringView 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(OutputStream &S) const override { SubExpr->print(S); S += ".<"; Type->print(S); S += " at offset "; if (Offset.empty()) { S += "0"; } else if (Offset[0] == 'n') { S += "-"; S += Offset.dropFront(); } else { S += Offset; } S += ">"; } }; class EnclosingExpr : public Node { const StringView Prefix; const Node *Infix; const StringView Postfix; public: EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_) : Node(KEnclosingExpr), Prefix(Prefix_), Infix(Infix_), Postfix(Postfix_) {} template void match(Fn F) const { F(Prefix, Infix, Postfix); } void printLeft(OutputStream &S) const override { S += Prefix; Infix->print(S); S += Postfix; } }; class CastExpr : public Node { // cast_kind(from) const StringView CastKind; const Node *To; const Node *From; public: CastExpr(StringView CastKind_, const Node *To_, const Node *From_) : Node(KCastExpr), CastKind(CastKind_), To(To_), From(From_) {} template void match(Fn F) const { F(CastKind, To, From); } void printLeft(OutputStream &S) const override { S += CastKind; S += "<"; To->printLeft(S); S += ">("; From->printLeft(S); S += ")"; } }; 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(OutputStream &S) const override { S += "sizeof...("; ParameterPackExpansion PPE(Pack); PPE.printLeft(S); S += ")"; } }; class CallExpr : public Node { const Node *Callee; NodeArray Args; public: CallExpr(const Node *Callee_, NodeArray Args_) : Node(KCallExpr), Callee(Callee_), Args(Args_) {} template void match(Fn F) const { F(Callee, Args); } void printLeft(OutputStream &S) const override { Callee->print(S); S += "("; Args.printWithComma(S); S += ")"; } }; 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_) : Node(KNewExpr), ExprList(ExprList_), Type(Type_), InitList(InitList_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} template void match(Fn F) const { F(ExprList, Type, InitList, IsGlobal, IsArray); } void printLeft(OutputStream &S) const override { if (IsGlobal) S += "::operator "; S += "new"; if (IsArray) S += "[]"; S += ' '; if (!ExprList.empty()) { S += "("; ExprList.printWithComma(S); S += ")"; } Type->print(S); if (!InitList.empty()) { S += "("; InitList.printWithComma(S); S += ")"; } } }; class DeleteExpr : public Node { Node *Op; bool IsGlobal; bool IsArray; public: DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_) : Node(KDeleteExpr), Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} template void match(Fn F) const { F(Op, IsGlobal, IsArray); } void printLeft(OutputStream &S) const override { if (IsGlobal) S += "::"; S += "delete"; if (IsArray) S += "[] "; Op->print(S); } }; class PrefixExpr : public Node { StringView Prefix; Node *Child; public: PrefixExpr(StringView Prefix_, Node *Child_) : Node(KPrefixExpr), Prefix(Prefix_), Child(Child_) {} template void match(Fn F) const { F(Prefix, Child); } void printLeft(OutputStream &S) const override { S += Prefix; S += "("; Child->print(S); S += ")"; } }; class FunctionParam : public Node { StringView Number; public: FunctionParam(StringView Number_) : Node(KFunctionParam), Number(Number_) {} template void match(Fn F) const { F(Number); } void printLeft(OutputStream &S) const override { S += "fp"; S += Number; } }; class ConversionExpr : public Node { const Node *Type; NodeArray Expressions; public: ConversionExpr(const Node *Type_, NodeArray Expressions_) : Node(KConversionExpr), Type(Type_), Expressions(Expressions_) {} template void match(Fn F) const { F(Type, Expressions); } void printLeft(OutputStream &S) const override { S += "("; Type->print(S); S += ")("; Expressions.printWithComma(S); S += ")"; } }; class PointerToMemberConversionExpr : public Node { const Node *Type; const Node *SubExpr; StringView Offset; public: PointerToMemberConversionExpr(const Node *Type_, const Node *SubExpr_, StringView Offset_) : Node(KPointerToMemberConversionExpr), Type(Type_), SubExpr(SubExpr_), Offset(Offset_) {} template void match(Fn F) const { F(Type, SubExpr, Offset); } void printLeft(OutputStream &S) const override { S += "("; Type->print(S); S += ")("; SubExpr->print(S); S += ")"; } }; 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(OutputStream &S) const override { if (Ty) Ty->print(S); S += '{'; Inits.printWithComma(S); S += '}'; } }; 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(OutputStream &S) const override { if (IsArray) { S += '['; Elem->print(S); S += ']'; } else { S += '.'; Elem->print(S); } if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) S += " = "; Init->print(S); } }; 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(OutputStream &S) const override { S += '['; First->print(S); S += " ... "; Last->print(S); S += ']'; if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) S += " = "; Init->print(S); } }; class FoldExpr : public Node { const Node *Pack, *Init; StringView OperatorName; bool IsLeftFold; public: FoldExpr(bool IsLeftFold_, StringView 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(OutputStream &S) const override { auto PrintPack = [&] { S += '('; ParameterPackExpansion(Pack).print(S); S += ')'; }; S += '('; if (IsLeftFold) { // init op ... op pack if (Init != nullptr) { Init->print(S); S += ' '; S += OperatorName; S += ' '; } // ... op pack S += "... "; S += OperatorName; S += ' '; PrintPack(); } else { // !IsLeftFold // pack op ... PrintPack(); S += ' '; S += OperatorName; S += " ..."; // pack op ... op init if (Init != nullptr) { S += ' '; S += OperatorName; S += ' '; Init->print(S); } } S += ')'; } }; 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(OutputStream &S) const override { S += "throw "; Op->print(S); } }; class BoolExpr : public Node { bool Value; public: BoolExpr(bool Value_) : Node(KBoolExpr), Value(Value_) {} template void match(Fn F) const { F(Value); } void printLeft(OutputStream &S) const override { S += Value ? StringView("true") : StringView("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(OutputStream &S) const override { S += "\"<"; Type->print(S); S += ">\""; } }; 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(OutputStream &S) const override { S += "[]"; if (Type->getKind() == KClosureTypeName) static_cast(Type)->printDeclarator(S); S += "{...}"; } }; class EnumLiteral : public Node { // ty(integer) const Node *Ty; StringView Integer; public: EnumLiteral(const Node *Ty_, StringView Integer_) : Node(KEnumLiteral), Ty(Ty_), Integer(Integer_) {} template void match(Fn F) const { F(Ty, Integer); } void printLeft(OutputStream &S) const override { S << "("; Ty->print(S); S << ")"; if (Integer[0] == 'n') S << "-" << Integer.dropFront(1); else S << Integer; } }; class IntegerLiteral : public Node { StringView Type; StringView Value; public: IntegerLiteral(StringView Type_, StringView Value_) : Node(KIntegerLiteral), Type(Type_), Value(Value_) {} template void match(Fn F) const { F(Type, Value); } void printLeft(OutputStream &S) const override { if (Type.size() > 3) { S += "("; S += Type; S += ")"; } if (Value[0] == 'n') { S += "-"; S += Value.dropFront(1); } else S += Value; if (Type.size() <= 3) S += 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; } } template class FloatLiteralImpl : public Node { const StringView Contents; static constexpr Kind KindForClass = float_literal_impl::getFloatLiteralKind((Float *)nullptr); public: FloatLiteralImpl(StringView Contents_) : Node(KindForClass), Contents(Contents_) {} template void match(Fn F) const { F(Contents); } void printLeft(OutputStream &s) const override { const char *first = Contents.begin(); const char *last = Contents.end() + 1; const size_t N = FloatData::mangled_size; if (static_cast(last - first) > N) { last = first + N; union { Float value; char buf[sizeof(Float)]; }; const char *t = first; 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); s += StringView(num, 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 CASE(X) case K ## X: return F(static_cast(this)); FOR_EACH_NODE_KIND(CASE) #undef CASE } assert(0 && "unknown mangling node kind"); } /// Determine the kind of a node from its type. template struct NodeKind; #define SPECIALIZATION(X) \ template<> struct NodeKind { \ static constexpr Node::Kind Kind = Node::K##X; \ static constexpr const char *name() { return #X; } \ }; FOR_EACH_NODE_KIND(SPECIALIZATION) #undef SPECIALIZATION #undef FOR_EACH_NODE_KIND 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; } void push_back(const T& Elem) { if (Last == Cap) reserve(size() * 2); *Last++ = Elem; } 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); } }; 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(StringView S) { if (StringView(First, Last).startsWith(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) { if (static_cast(Last - First) <= Lookahead) return '\0'; return First[Lookahead]; } size_t numLeft() const { return static_cast(Last - First); } StringView parseNumber(bool AllowNegative = false); Qualifiers parseCVQualifiers(); bool parsePositiveInteger(size_t *Out); StringView 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(StringView Kind); Node *parseBinaryExpr(StringView Kind); Node *parseIntegerLiteral(StringView Lit); Node *parseExprPrimary(); template Node *parseFloatingLiteral(); Node *parseFunctionParam(); Node *parseNewExpr(); Node *parseConversionExpr(); Node *parseBracedExpr(); Node *parseFoldExpr(); Node *parsePointerToMemberConversionExpr(); 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); Node *parseUnqualifiedName(NameState *State); Node *parseUnnamedTypeName(NameState *State); Node *parseSourceName(NameState *State); Node *parseUnscopedName(NameState *State); Node *parseNestedName(NameState *State); Node *parseCtorDtorName(Node *&SoFar, NameState *State); Node *parseAbiTags(Node *N); /// Parse the production. Node *parseUnresolvedName(); 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) { consumeIf('L'); // extension if (look() == 'N') return getDerived().parseNestedName(State); if (look() == 'Z') return getDerived().parseLocalName(State); // ::= if (look() == 'S' && look(1) != 't') { Node *S = getDerived().parseSubstitution(); if (S == nullptr) return nullptr; if (look() != 'I') return nullptr; Node *TA = getDerived().parseTemplateArgs(State != nullptr); if (TA == nullptr) return nullptr; if (State) State->EndsWithTemplateArgs = true; return make(S, TA); } Node *N = getDerived().parseUnscopedName(State); if (N == nullptr) return nullptr; // ::= if (look() == 'I') { Subs.push_back(N); Node *TA = getDerived().parseTemplateArgs(State != nullptr); if (TA == nullptr) return nullptr; if (State) State->EndsWithTemplateArgs = true; return make(N, TA); } // ::= return N; } // := 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 ::= StL template Node * AbstractManglingParser::parseUnscopedName(NameState *State) { if (consumeIf("StL") || consumeIf("St")) { Node *R = getDerived().parseUnqualifiedName(State); if (R == nullptr) return nullptr; return make(R); } return getDerived().parseUnqualifiedName(State); } // ::= [abi-tags] // ::= // ::= // ::= // ::= DC + E # structured binding declaration template Node * AbstractManglingParser::parseUnqualifiedName(NameState *State) { // s are special-cased in parseNestedName(). Node *Result; if (look() == 'U') Result = getDerived().parseUnnamedTypeName(State); else if (look() >= '1' && look() <= '9') Result = getDerived().parseSourceName(State); else if (consumeIf("DC")) { 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 Result = getDerived().parseOperatorName(State); if (Result != nullptr) Result = getDerived().parseAbiTags(Result); return Result; } // ::= 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")) { StringView Count = parseNumber(); if (!consumeIf('_')) return nullptr; return make(Count); } if (consumeIf("Ul")) { SwapAndRestore SwapParams(ParsingLambdaParamsAtLevel, TemplateParams.size()); ScopedTemplateParamList LambdaTemplateParams(this); size_t ParamsBegin = Names.size(); while (look() == 'T' && StringView("yptn").find(look(1)) != StringView::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); StringView 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; StringView Name(First, First + Length); First += Length; if (Name.startsWith("_GLOBAL__N")) return make("(anonymous namespace)"); return make(Name); } // ::= aa # && // ::= ad # & (unary) // ::= an # & // ::= aN # &= // ::= aS # = // ::= cl # () // ::= cm # , // ::= co # ~ // ::= cv # (cast) // ::= da # delete[] // ::= de # * (unary) // ::= dl # delete // ::= dv # / // ::= dV # /= // ::= eo # ^ // ::= eO # ^= // ::= eq # == // ::= ge # >= // ::= gt # > // ::= ix # [] // ::= le # <= // ::= li # operator "" // ::= ls # << // ::= lS # <<= // ::= lt # < // ::= mi # - // ::= mI # -= // ::= ml # * // ::= mL # *= // ::= mm # -- (postfix in context) // ::= na # new[] // ::= ne # != // ::= ng # - (unary) // ::= nt # ! // ::= nw # new // ::= oo # || // ::= or # | // ::= oR # |= // ::= pm # ->* // ::= pl # + // ::= pL # += // ::= pp # ++ (postfix in context) // ::= ps # + (unary) // ::= pt # -> // ::= qu # ? // ::= rm # % // ::= rM # %= // ::= rs # >> // ::= rS # >>= // ::= ss # <=> C++2a // ::= v # vendor extended operator template Node * AbstractManglingParser::parseOperatorName(NameState *State) { switch (look()) { case 'a': switch (look(1)) { case 'a': First += 2; return make("operator&&"); case 'd': case 'n': First += 2; return make("operator&"); case 'N': First += 2; return make("operator&="); case 'S': First += 2; return make("operator="); } return nullptr; case 'c': switch (look(1)) { case 'l': First += 2; return make("operator()"); case 'm': First += 2; return make("operator,"); case 'o': First += 2; return make("operator~"); // ::= cv # (cast) case 'v': { First += 2; SwapAndRestore 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. SwapAndRestore SavePermit(PermitForwardTemplateReferences, PermitForwardTemplateReferences || State != nullptr); Node *Ty = getDerived().parseType(); if (Ty == nullptr) return nullptr; if (State) State->CtorDtorConversion = true; return make(Ty); } } return nullptr; case 'd': switch (look(1)) { case 'a': First += 2; return make("operator delete[]"); case 'e': First += 2; return make("operator*"); case 'l': First += 2; return make("operator delete"); case 'v': First += 2; return make("operator/"); case 'V': First += 2; return make("operator/="); } return nullptr; case 'e': switch (look(1)) { case 'o': First += 2; return make("operator^"); case 'O': First += 2; return make("operator^="); case 'q': First += 2; return make("operator=="); } return nullptr; case 'g': switch (look(1)) { case 'e': First += 2; return make("operator>="); case 't': First += 2; return make("operator>"); } return nullptr; case 'i': if (look(1) == 'x') { First += 2; return make("operator[]"); } return nullptr; case 'l': switch (look(1)) { case 'e': First += 2; return make("operator<="); // ::= li # operator "" case 'i': { First += 2; Node *SN = getDerived().parseSourceName(State); if (SN == nullptr) return nullptr; return make(SN); } case 's': First += 2; return make("operator<<"); case 'S': First += 2; return make("operator<<="); case 't': First += 2; return make("operator<"); } return nullptr; case 'm': switch (look(1)) { case 'i': First += 2; return make("operator-"); case 'I': First += 2; return make("operator-="); case 'l': First += 2; return make("operator*"); case 'L': First += 2; return make("operator*="); case 'm': First += 2; return make("operator--"); } return nullptr; case 'n': switch (look(1)) { case 'a': First += 2; return make("operator new[]"); case 'e': First += 2; return make("operator!="); case 'g': First += 2; return make("operator-"); case 't': First += 2; return make("operator!"); case 'w': First += 2; return make("operator new"); } return nullptr; case 'o': switch (look(1)) { case 'o': First += 2; return make("operator||"); case 'r': First += 2; return make("operator|"); case 'R': First += 2; return make("operator|="); } return nullptr; case 'p': switch (look(1)) { case 'm': First += 2; return make("operator->*"); case 'l': First += 2; return make("operator+"); case 'L': First += 2; return make("operator+="); case 'p': First += 2; return make("operator++"); case 's': First += 2; return make("operator+"); case 't': First += 2; return make("operator->"); } return nullptr; case 'q': if (look(1) == 'u') { First += 2; return make("operator?"); } return nullptr; case 'r': switch (look(1)) { case 'm': First += 2; return make("operator%"); case 'M': First += 2; return make("operator%="); case 's': First += 2; return make("operator>>"); case 'S': First += 2; return make("operator>>="); } return nullptr; case 's': if (look(1) == 's') { First += 2; return make("operator<=>"); } return nullptr; // ::= v # vendor extended operator case 'v': if (std::isdigit(look(1))) { First += 2; 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) { auto SSK = static_cast(SoFar)->SSK; switch (SSK) { case SpecialSubKind::string: case SpecialSubKind::istream: case SpecialSubKind::ostream: case SpecialSubKind::iostream: SoFar = make(SSK); if (!SoFar) return nullptr; break; default: break; } } 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 ::= L // // := [] M // // ::=