[libcxxabi] r309340 - [demangler] Use an AST to represent demangled names

Erik Pilkington via cfe-commits cfe-commits at lists.llvm.org
Sun Aug 6 13:50:24 PDT 2017


Fixed in r310226, thanks!


On 8/4/17 12:08 PM, Kostya Serebryany wrote:
> and one more:
> https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2837
> Crash Type: Heap-buffer-overflow READ 8
> Crash Address: 0x615000000078
> Crash State:
>    __cxxabiv1::parse_new_expr
>    __cxxabiv1::parse_expression
>    __cxxabiv1::parse_array_type
>
> On Mon, Jul 31, 2017 at 9:56 AM, Kostya Serebryany <kcc at google.com 
> <mailto:kcc at google.com>> wrote:
>
>     Yep, confirmed. Thanks!
>     In the meantime, two new bugs popped up (probably, got un-hidden
>     after your fixes):
>     https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2804
>     <https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2804>
>     Crash Type: Heap-buffer-overflow READ 8
>     Crash Address: 0x619000000078
>     Crash State:
>       __cxxabiv1::parse_encoding
>       __cxxabiv1::demangle
>       __cxa_demangle
>
>     https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2800
>     <https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2800>
>     Crash Type: Out-of-memory (exceeds 2048 MB)
>
>     Two older stack overflow bugs also remain.
>
>     --kcc
>
>     On Sun, Jul 30, 2017 at 1:12 PM, Erik Pilkington
>     <erik.pilkington at gmail.com <mailto:erik.pilkington at gmail.com>> wrote:
>
>         r309520 should fix the new failures. I'll keep an eye on
>         oss-fuzz in case anything new comes up.
>         Thanks for pinging me,
>         Erik
>
>         On 7/28/17 10:06 AM, Kostya Serebryany wrote:
>>         Erik,
>>         A bunch of old bugs reported in this code by OSS-Fuzz got
>>         auto-closed today, thanks!
>>
>>         Also a few new bugs got opened tonight, most likely caused by
>>         this patch. You are auto-CC-ed.
>>         I would start from
>>         https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2767
>>         <https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2767>
>>         Crash Type: Null-dereference READ Crash Address:
>>         0x000000000008 Crash State: __cxxabiv1::parse_nested_name
>>         __cxxabiv1::parse_name __cxxabiv1::parse_local_name
>>         Also, you have the access to all inputs generated by the demangler fuzzer in a few months of fuzzing.
>>         You can use that corpus for local testing, and I would also encourage you to add the corpus to the public LLVM bots.
>>         --kcc
>>
>>         On Thu, Jul 27, 2017 at 5:43 PM, Erik Pilkington via
>>         cfe-commits <cfe-commits at lists.llvm.org
>>         <mailto:cfe-commits at lists.llvm.org>> wrote:
>>
>>             Author: epilk
>>             Date: Thu Jul 27 17:43:49 2017
>>             New Revision: 309340
>>
>>             URL:
>>             http://llvm.org/viewvc/llvm-project?rev=309340&view=rev
>>             <http://llvm.org/viewvc/llvm-project?rev=309340&view=rev>
>>             Log:
>>             [demangler] Use an AST to represent demangled names
>>
>>             The demangler now demangles by producing an AST, then
>>             traverses that
>>             AST to produce a demangled name. This is done for
>>             performance reasons,
>>             now the demangler doesn't manuiplate std::strings, which hurt
>>             performance and caused string operations to be inlined
>>             into the
>>             parser, leading to large code size and stack usage.
>>
>>             Differential revision: https://reviews.llvm.org/D35159
>>             <https://reviews.llvm.org/D35159>
>>
>>             Modified:
>>                 libcxxabi/trunk/src/cxa_demangle.cpp
>>                 libcxxabi/trunk/test/test_demangle.pass.cpp
>>
>>             Modified: libcxxabi/trunk/src/cxa_demangle.cpp
>>             URL:
>>             http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp?rev=309340&r1=309339&r2=309340&view=diff
>>             <http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp?rev=309340&r1=309339&r2=309340&view=diff>
>>             ==============================================================================
>>             --- libcxxabi/trunk/src/cxa_demangle.cpp (original)
>>             +++ libcxxabi/trunk/src/cxa_demangle.cpp Thu Jul 27
>>             17:43:49 2017
>>             @@ -13,7 +13,6 @@
>>
>>              #include <vector>
>>              #include <algorithm>
>>             -#include <string>
>>              #include <numeric>
>>              #include <cstdlib>
>>              #include <cstring>
>>             @@ -41,6 +40,1370 @@ enum
>>                  success
>>              };
>>
>>             +class StringView {
>>             +  const char *First;
>>             +  const char *Last;
>>             +
>>             +public:
>>             +  template <size_t N>
>>             +  StringView(const char (&Str)[N]) : First(Str),
>>             Last(Str + N - 1) {}
>>             +  StringView(const char *First, const char *Last) :
>>             First(First), Last(Last) {}
>>             +  StringView() : First(nullptr), Last(nullptr) {}
>>             +
>>             +  StringView substr(size_t From, size_t To) {
>>             +    if (To >= size())
>>             +      To = size() - 1;
>>             +    if (From >= size())
>>             +      From = size() - 1;
>>             +    return StringView(First + From, First + To);
>>             +  }
>>             +
>>             +  StringView dropFront(size_t N) const {
>>             +    if (N >= size())
>>             +      N = size() - 1;
>>             +    return StringView(First + N, Last);
>>             +  }
>>             +
>>             +  bool startsWith(StringView Str) const {
>>             +    if (Str.size() > size())
>>             +      return false;
>>             +    return std::equal(Str.begin(), Str.end(), begin());
>>             +  }
>>             +
>>             +  const char &operator[](size_t Idx) const { return
>>             *(begin() + Idx); }
>>             +
>>             +  const char *begin() const { return First; }
>>             +  const char *end() const { return Last; }
>>             +  size_t size() const { return static_cast<size_t>(Last
>>             - First); }
>>             +};
>>             +
>>             +bool operator==(const StringView &LHS, const StringView
>>             &RHS) {
>>             +  return LHS.size() == RHS.size() &&
>>             +         std::equal(LHS.begin(), LHS.end(), RHS.begin());
>>             +}
>>             +
>>             +// Stream that AST nodes write their string
>>             representation into after the AST
>>             +// has been parsed.
>>             +class OutputStream {
>>             +  char *Buffer;
>>             +  size_t CurrentPosition;
>>             +  size_t BufferCapacity;
>>             +
>>             +  // Ensure there is at least n more positions in buffer.
>>             +  void grow(size_t N) {
>>             +    if (N + CurrentPosition >= BufferCapacity) {
>>             +      BufferCapacity *= 2;
>>             +      if (BufferCapacity < N + CurrentPosition)
>>             +        BufferCapacity = N + CurrentPosition;
>>             +      Buffer = static_cast<char *>(std::realloc(Buffer,
>>             BufferCapacity));
>>             +    }
>>             +  }
>>             +
>>             +public:
>>             +  OutputStream(char *StartBuf, size_t Size)
>>             +      : Buffer(StartBuf), CurrentPosition(0),
>>             BufferCapacity(Size) {}
>>             +
>>             +  OutputStream &operator+=(StringView R) {
>>             +    size_t Size = R.size();
>>             +    if (Size == 0)
>>             +      return *this;
>>             +    grow(Size);
>>             +    memmove(Buffer + CurrentPosition, R.begin(), Size);
>>             +    CurrentPosition += Size;
>>             +    return *this;
>>             +  }
>>             +
>>             +  OutputStream &operator+=(char C) {
>>             +    grow(1);
>>             +    Buffer[CurrentPosition++] = C;
>>             +    return *this;
>>             +  }
>>             +
>>             +  // Offset of position in buffer, used for building
>>             stream_string_view.
>>             +  typedef unsigned StreamPosition;
>>             +
>>             +  // StringView into a stream, used for caching the ast
>>             nodes.
>>             +  class StreamStringView {
>>             +    StreamPosition First, Last;
>>             +
>>             +    friend class OutputStream;
>>             +
>>             +  public:
>>             +    StreamStringView() : First(0), Last(0) {}
>>             +
>>             +    StreamStringView(StreamPosition First,
>>             StreamPosition Last)
>>             +        : First(First), Last(Last) {}
>>             +
>>             +    bool empty() const { return First == Last; }
>>             +  };
>>             +
>>             +  OutputStream &operator+=(StreamStringView &s) {
>>             +    size_t Sz = static_cast<size_t>(s.Last - s.First);
>>             +    if (Sz == 0)
>>             +      return *this;
>>             +    grow(Sz);
>>             +    memmove(Buffer + CurrentPosition, Buffer + s.First, Sz);
>>             +    CurrentPosition += Sz;
>>             +    return *this;
>>             +  }
>>             +
>>             +  StreamPosition getCurrentPosition() const {
>>             +    return static_cast<StreamPosition>(CurrentPosition);
>>             +  }
>>             +
>>             +  StreamStringView
>>             makeStringViewFromPastPosition(StreamPosition Pos) {
>>             +    return StreamStringView(Pos, getCurrentPosition());
>>             +  }
>>             +
>>             +  char back() const {
>>             +    return CurrentPosition ? Buffer[CurrentPosition - 1]
>>             : '\0';
>>             +  }
>>             +
>>             +  bool empty() const { return CurrentPosition == 0; }
>>             +
>>             +  char *getBuffer() { return Buffer; }
>>             +  char *getBufferEnd() { return Buffer + CurrentPosition
>>             - 1; }
>>             +  size_t getBufferCapacity() { return BufferCapacity; }
>>             +};
>>             +
>>             +// 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 {
>>             +    KDotSuffix,
>>             +    KVendorExtQualType,
>>             +    KQualType,
>>             +    KConversionOperatorType,
>>             +    KPostfixQualifiedType,
>>             +    KNameType,
>>             +    KObjCProtoName,
>>             +    KPointerType,
>>             +    KLValueReferenceType,
>>             +    KRValueReferenceType,
>>             +    KPointerToMemberType,
>>             +    KArrayType,
>>             +    KFunctionType,
>>             +    KTopLevelFunctionDecl,
>>             +    KFunctionQualType,
>>             +    KFunctionRefQualType,
>>             +    KLiteralOperator,
>>             +    KSpecialName,
>>             +    KCtorVtableSpecialName,
>>             +    KQualifiedName,
>>             +    KEmptyName,
>>             +    KVectorType,
>>             +    KTemplateParams,
>>             +    KNameWithTemplateArgs,
>>             +    KGlobalQualifiedName,
>>             +    KStdQualifiedName,
>>             +    KExpandedSpecialSubstitution,
>>             +    KSpecialSubstitution,
>>             +    KCtorDtorName,
>>             +    KDtorName,
>>             +    KUnnamedTypeName,
>>             +    KLambdaTypeName,
>>             +    KExpr,
>>             +  };
>>             +
>>             +  const Kind K;
>>             +
>>             +private:
>>             +  // If this Node has any RHS part, potentally many
>>             Nodes further down.
>>             +  const unsigned HasRHSComponent : 1;
>>             +  const unsigned HasFunction : 1;
>>             +  const unsigned HasArray : 1;
>>             +
>>             +public:
>>             +  Node(Kind K, bool HasRHS = false, bool HasFunction =
>>             false,
>>             +       bool HasArray = false)
>>             +      : K(K), HasRHSComponent(HasRHS),
>>             HasFunction(HasFunction),
>>             +        HasArray(HasArray) {}
>>             +
>>             +  bool hasRHSComponent() const { return HasRHSComponent; }
>>             +  bool hasArray() const { return HasArray; }
>>             +  bool hasFunction() const { return HasFunction; }
>>             +
>>             +  void print(OutputStream &s) const {
>>             +    printLeft(s);
>>             +    if (hasRHSComponent())
>>             +      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
>>             +  // implemenation.
>>             +  virtual void printRight(OutputStream &) const {}
>>             +
>>             +  virtual StringView getBaseName() const { return
>>             StringView(); }
>>             +
>>             +  // Silence compiler warnings, this dtor will never be
>>             called.
>>             +  virtual ~Node() = default;
>>             +};
>>             +
>>             +class NodeArray {
>>             +  Node **Elements;
>>             +  size_t NumElements;
>>             +
>>             +public:
>>             +  NodeArray() : NumElements(0) {}
>>             +  NodeArray(Node **Elements, size_t NumElements)
>>             +      : Elements(Elements), NumElements(NumElements) {}
>>             +
>>             +  bool empty() const { return NumElements == 0; }
>>             +  size_t size() const { return NumElements; }
>>             +
>>             +  void printWithSeperator(OutputStream &S, StringView
>>             Seperator) const {
>>             +    for (size_t Idx = 0; Idx != NumElements; ++Idx) {
>>             +      if (Idx)
>>             +        S += Seperator;
>>             +      Elements[Idx]->print(S);
>>             +    }
>>             +  }
>>             +};
>>             +
>>             +class DotSuffix final : public Node {
>>             +  const Node *Prefix;
>>             +  const StringView Suffix;
>>             +
>>             +public:
>>             +  DotSuffix(Node *Prefix, StringView Suffix)
>>             +      : Node(KDotSuffix), Prefix(Prefix), Suffix(Suffix) {}
>>             +
>>             +  void printLeft(OutputStream &s) const override {
>>             +    Prefix->print(s);
>>             +    s += " (";
>>             +    s += Suffix;
>>             +    s += ")";
>>             +  }
>>             +};
>>             +
>>             +class VendorExtQualType final : public Node {
>>             +  const Node *Ext;
>>             +  const Node *Ty;
>>             +
>>             +public:
>>             +  VendorExtQualType(Node *Ext, Node *Ty)
>>             +      : Node(KVendorExtQualType), Ext(Ext), Ty(Ty) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    Ext->print(S);
>>             +    S += " ";
>>             +    Ty->printLeft(S);
>>             +  }
>>             +
>>             +  void printRight(OutputStream &S) const override {
>>             Ty->printRight(S); }
>>             +};
>>             +
>>             +enum Qualifiers {
>>             +  QualNone = 0,
>>             +  QualConst = 0x1,
>>             +  QualVolatile = 0x2,
>>             +  QualRestrict = 0x4,
>>             +};
>>             +
>>             +void addQualifiers(Qualifiers &Q1, Qualifiers Q2) {
>>             +  Q1 = static_cast<Qualifiers>(Q1 | Q2);
>>             +}
>>             +
>>             +class QualType : 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(Node *Child, Qualifiers Quals)
>>             +      : Node(KQualType, Child->hasRHSComponent(),
>>             Child->hasFunction(),
>>             +             Child->hasArray()),
>>             +        Quals(Quals), Child(Child) {}
>>             +
>>             +  QualType(Node::Kind ChildKind, Node *Child, Qualifiers
>>             Quals)
>>             +      : Node(ChildKind, Child->hasRHSComponent(),
>>             Child->hasFunction(),
>>             +             Child->hasArray()),
>>             +        Quals(Quals), Child(Child) {}
>>             +
>>             +  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(Node *Ty) :
>>             Node(KConversionOperatorType), Ty(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) {}
>>             +
>>             +  void printLeft(OutputStream &s) const override {
>>             +    Ty->printLeft(s);
>>             +    s += Postfix;
>>             +  }
>>             +
>>             +  void printRight(OutputStream &S) const override {
>>             Ty->printRight(S); }
>>             +};
>>             +
>>             +class NameType final : public Node {
>>             +  const StringView Name;
>>             +
>>             +public:
>>             +  NameType(StringView Name) : Node(KNameType), Name(Name) {}
>>             +
>>             +  StringView getName() const { return Name; }
>>             +  StringView getBaseName() const override { return Name; }
>>             +
>>             +  void printLeft(OutputStream &s) const override { s +=
>>             Name; }
>>             +};
>>             +
>>             +class ObjCProtoName : public Node {
>>             +  Node *Ty;
>>             +  Node *Protocol;
>>             +
>>             +  friend class PointerType;
>>             +
>>             +public:
>>             +  ObjCProtoName(Node *Ty, Node *Protocol)
>>             +      : Node(KObjCProtoName), Ty(Ty), Protocol(Protocol) {}
>>             +
>>             +  bool isObjCObject() const {
>>             +    return Ty->K == KNameType &&
>>             +           static_cast<NameType *>(Ty)->getName() ==
>>             "objc_object";
>>             +  }
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    Ty->printLeft(S);
>>             +    S += "<";
>>             +    Protocol->printLeft(S);
>>             +    S += ">";
>>             +  }
>>             +};
>>             +
>>             +class PointerType final : public Node {
>>             +  const Node *Pointee;
>>             +
>>             +public:
>>             +  PointerType(Node *Pointee)
>>             +      : Node(KPointerType, Pointee->hasRHSComponent()),
>>             Pointee(Pointee) {}
>>             +
>>             +  void printLeft(OutputStream &s) const override {
>>             +    // We rewrite objc_object<SomeProtocol>* into
>>             id<SomeProtocol>.
>>             +    if (Pointee->K != KObjCProtoName ||
>>             +        !static_cast<const ObjCProtoName
>>             *>(Pointee)->isObjCObject()) {
>>             +      Pointee->printLeft(s);
>>             +      if (Pointee->hasArray())
>>             +        s += " ";
>>             +      if (Pointee->hasArray() || Pointee->hasFunction())
>>             +        s += "(";
>>             +      s += "*";
>>             +    } else {
>>             +      const auto *objcProto = static_cast<const
>>             ObjCProtoName *>(Pointee);
>>             +      s += "id<";
>>             +      objcProto->Protocol->print(s);
>>             +      s += ">";
>>             +    }
>>             +  }
>>             +
>>             +  void printRight(OutputStream &s) const override {
>>             +    if (Pointee->K != KObjCProtoName ||
>>             +        !static_cast<const ObjCProtoName
>>             *>(Pointee)->isObjCObject()) {
>>             +      if (Pointee->hasArray() || Pointee->hasFunction())
>>             +        s += ")";
>>             +      Pointee->printRight(s);
>>             +    }
>>             +  }
>>             +};
>>             +
>>             +class LValueReferenceType final : public Node {
>>             +  const Node *Pointee;
>>             +
>>             +public:
>>             +  LValueReferenceType(Node *Pointee)
>>             +      : Node(KLValueReferenceType,
>>             Pointee->hasRHSComponent()),
>>             +        Pointee(Pointee) {}
>>             +
>>             +  void printLeft(OutputStream &s) const override {
>>             +    Pointee->printLeft(s);
>>             +    if (Pointee->hasArray())
>>             +      s += " ";
>>             +    if (Pointee->hasArray() || Pointee->hasFunction())
>>             +      s += "(&";
>>             +    else
>>             +      s += "&";
>>             +  }
>>             +  void printRight(OutputStream &s) const override {
>>             +    if (Pointee->hasArray() || Pointee->hasFunction())
>>             +      s += ")";
>>             +    Pointee->printRight(s);
>>             +  }
>>             +};
>>             +
>>             +class RValueReferenceType final : public Node {
>>             +  const Node *Pointee;
>>             +
>>             +public:
>>             +  RValueReferenceType(Node *Pointee)
>>             +      : Node(KRValueReferenceType,
>>             Pointee->hasRHSComponent()),
>>             +        Pointee(Pointee) {}
>>             +
>>             +  void printLeft(OutputStream &s) const override {
>>             +    Pointee->printLeft(s);
>>             +    if (Pointee->hasArray())
>>             +      s += " ";
>>             +    if (Pointee->hasArray() || Pointee->hasFunction())
>>             +      s += "(&&";
>>             +    else
>>             +      s += "&&";
>>             +  }
>>             +
>>             +  void printRight(OutputStream &s) const override {
>>             +    if (Pointee->hasArray() || Pointee->hasFunction())
>>             +      s += ")";
>>             +    Pointee->printRight(s);
>>             +  }
>>             +};
>>             +
>>             +class PointerToMemberType final : public Node {
>>             +  const Node *ClassType;
>>             +  const Node *MemberType;
>>             +
>>             +public:
>>             +  PointerToMemberType(Node *ClassType, Node *MemberType)
>>             +      : Node(KPointerToMemberType,
>>             MemberType->hasRHSComponent()),
>>             +        ClassType(ClassType), MemberType(MemberType) {}
>>             +
>>             +  void printLeft(OutputStream &s) const override {
>>             +    MemberType->printLeft(s);
>>             +    if (MemberType->hasArray() || MemberType->hasFunction())
>>             +      s += "(";
>>             +    else
>>             +      s += " ";
>>             +    ClassType->print(s);
>>             +    s += "::*";
>>             +  }
>>             +
>>             +  void printRight(OutputStream &s) const override {
>>             +    if (MemberType->hasArray() || MemberType->hasFunction())
>>             +      s += ")";
>>             +    MemberType->printRight(s);
>>             +  }
>>             +};
>>             +
>>             +class NodeOrString {
>>             +  const void *First;
>>             +  const void *Second;
>>             +
>>             +public:
>>             +  /* implicit */ NodeOrString(StringView Str) {
>>             +    const char *FirstChar = Str.begin();
>>             +    const char *SecondChar = Str.end();
>>             +    if (SecondChar == nullptr) {
>>             +      assert(FirstChar == SecondChar);
>>             +      ++FirstChar, ++SecondChar;
>>             +    }
>>             +    First = static_cast<const void *>(FirstChar);
>>             +    Second = static_cast<const void *>(SecondChar);
>>             +  }
>>             +
>>             +  /* implicit */ NodeOrString(Node *N)
>>             +      : First(static_cast<const void *>(N)),
>>             Second(nullptr) {}
>>             +  NodeOrString() : First(nullptr), Second(nullptr) {}
>>             +
>>             +  bool isString() const { return Second && First; }
>>             +  bool isNode() const { return First && !Second; }
>>             +  bool isEmpty() const { return !First && !Second; }
>>             +
>>             +  StringView asString() const {
>>             +    assert(isString());
>>             +    return StringView(static_cast<const char *>(First),
>>             +                      static_cast<const char *>(Second));
>>             +  }
>>             +
>>             +  const Node *asNode() const {
>>             +    assert(isNode());
>>             +    return static_cast<const Node *>(First);
>>             +  }
>>             +};
>>             +
>>             +class ArrayType final : public Node {
>>             +  Node *Base;
>>             +  NodeOrString Dimension;
>>             +
>>             +public:
>>             +  ArrayType(Node *Base, NodeOrString Dimension)
>>             +      : Node(KArrayType, true, false, true), Base(Base),
>>             Dimension(Dimension) {}
>>             +
>>             +  // Incomplete array type.
>>             +  ArrayType(Node *Base) : Node(KArrayType, true, false,
>>             true), Base(Base) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             Base->printLeft(S); }
>>             +
>>             +  void printRight(OutputStream &S) const override {
>>             +    if (S.back() != ']')
>>             +      S += " ";
>>             +    S += "[";
>>             +    if (Dimension.isString())
>>             +      S += Dimension.asString();
>>             +    else if (Dimension.isNode())
>>             +      Dimension.asNode()->print(S);
>>             +    S += "]";
>>             +    Base->printRight(S);
>>             +  }
>>             +};
>>             +
>>             +class FunctionType final : public Node {
>>             +  Node *Ret;
>>             +  NodeArray Params;
>>             +
>>             +public:
>>             +  FunctionType(Node *Ret, NodeArray Params)
>>             +      : Node(KFunctionType, true, true), Ret(Ret),
>>             Params(Params) {}
>>             +
>>             +  // Handle C++'s ... quirky decl grammer 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.printWithSeperator(S, ", ");
>>             +    S += ")";
>>             +    Ret->printRight(S);
>>             +  }
>>             +};
>>             +
>>             +class TopLevelFunctionDecl final : public Node {
>>             +  const Node *Ret;
>>             +  const Node *Name;
>>             +  NodeArray Params;
>>             +
>>             +public:
>>             +  TopLevelFunctionDecl(Node *Ret, Node *Name, NodeArray
>>             Params)
>>             +      : Node(KTopLevelFunctionDecl, true, true),
>>             Ret(Ret), Name(Name),
>>             +        Params(Params) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    if (Ret) {
>>             +      Ret->printLeft(S);
>>             +      if (!Ret->hasRHSComponent())
>>             +        S += " ";
>>             +    }
>>             +    Name->print(S);
>>             +  }
>>             +
>>             +  void printRight(OutputStream &S) const override {
>>             +    S += "(";
>>             +    Params.printWithSeperator(S, ", ");
>>             +    S += ")";
>>             +    if (Ret)
>>             +      Ret->printRight(S);
>>             +  }
>>             +};
>>             +
>>             +enum FunctionRefQual : unsigned char {
>>             +  FrefQualNone,
>>             +  FrefQualLValue,
>>             +  FrefQualRValue,
>>             +};
>>             +
>>             +class FunctionRefQualType : public Node {
>>             +  Node *Fn;
>>             +  FunctionRefQual Quals;
>>             +
>>             +  friend class FunctionQualType;
>>             +
>>             +public:
>>             +  FunctionRefQualType(Node *Fn, FunctionRefQual Quals)
>>             +      : Node(KFunctionRefQualType, true, true), Fn(Fn),
>>             Quals(Quals) {}
>>             +
>>             +  void printQuals(OutputStream &S) const {
>>             +    if (Quals == FrefQualLValue)
>>             +      S += " &";
>>             +    else
>>             +      S += " &&";
>>             +  }
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             Fn->printLeft(S); }
>>             +
>>             +  void printRight(OutputStream &S) const override {
>>             +    Fn->printRight(S);
>>             +    printQuals(S);
>>             +  }
>>             +};
>>             +
>>             +class FunctionQualType final : public QualType {
>>             +public:
>>             +  FunctionQualType(Node *Child, Qualifiers Quals)
>>             +      : QualType(KFunctionQualType, Child, Quals) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             Child->printLeft(S); }
>>             +
>>             +  void printRight(OutputStream &S) const override {
>>             +    if (Child->K == KFunctionRefQualType) {
>>             +      auto *RefQuals = static_cast<const
>>             FunctionRefQualType *>(Child);
>>             +      RefQuals->Fn->printRight(S);
>>             +      printQuals(S);
>>             +      RefQuals->printQuals(S);
>>             +    } else {
>>             +      Child->printRight(S);
>>             +      printQuals(S);
>>             +    }
>>             +  }
>>             +};
>>             +
>>             +class LiteralOperator : public Node {
>>             +  const Node *OpName;
>>             +
>>             +public:
>>             +  LiteralOperator(Node *OpName) :
>>             Node(KLiteralOperator), OpName(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, Node *Child)
>>             +      : Node(KSpecialName), Special(Special),
>>             Child(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(Node *FirstType, Node *SecondType)
>>             +      : Node(KCtorVtableSpecialName), FirstType(FirstType),
>>             +        SecondType(SecondType) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "construction vtable for ";
>>             +    FirstType->print(S);
>>             +    S += "-in-";
>>             +    SecondType->print(S);
>>             +  }
>>             +};
>>             +
>>             +class QualifiedName final : public Node {
>>             +  // qualifier::name
>>             +  const Node *Qualifier;
>>             +  const Node *Name;
>>             +
>>             +  mutable OutputStream::StreamStringView Cache;
>>             +
>>             +public:
>>             +  QualifiedName(Node *Qualifier, Node *Name)
>>             +      : Node(KQualifiedName), Qualifier(Qualifier),
>>             Name(Name) {}
>>             +
>>             +  StringView getBaseName() const override { return
>>             Name->getBaseName(); }
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    if (!Cache.empty()) {
>>             +      S += Cache;
>>             +      return;
>>             +    }
>>             +
>>             +    OutputStream::StreamPosition Start =
>>             S.getCurrentPosition();
>>             +    if (Qualifier->K != KEmptyName) {
>>             +      Qualifier->print(S);
>>             +      S += "::";
>>             +    }
>>             +    Name->print(S);
>>             +    Cache = S.makeStringViewFromPastPosition(Start);
>>             +  }
>>             +};
>>             +
>>             +class EmptyName : public Node {
>>             +public:
>>             +  EmptyName() : Node(KEmptyName) {}
>>             +  void printLeft(OutputStream &) const override {}
>>             +};
>>             +
>>             +class VectorType final : public Node {
>>             +  const Node *BaseType;
>>             +  const NodeOrString Dimension;
>>             +  const bool IsPixel;
>>             +
>>             +public:
>>             +  VectorType(NodeOrString Dimension)
>>             +      : Node(KVectorType), BaseType(nullptr),
>>             Dimension(Dimension),
>>             +        IsPixel(true) {}
>>             +  VectorType(Node *BaseType, NodeOrString Dimension)
>>             +      : Node(KVectorType), BaseType(BaseType),
>>             Dimension(Dimension),
>>             +        IsPixel(false) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    if (IsPixel) {
>>             +      S += "pixel vector[";
>>             +      S += Dimension.asString();
>>             +      S += "]";
>>             +    } else {
>>             +      BaseType->print(S);
>>             +      S += " vector[";
>>             +      if (Dimension.isNode())
>>             +        Dimension.asNode()->print(S);
>>             +      else if (Dimension.isString())
>>             +        S += Dimension.asString();
>>             +      S += "]";
>>             +    }
>>             +  }
>>             +};
>>             +
>>             +class TemplateParams final : public Node {
>>             +  NodeArray Params;
>>             +
>>             +  mutable OutputStream::StreamStringView Cache;
>>             +
>>             +public:
>>             +  TemplateParams(NodeArray Params) :
>>             Node(KTemplateParams), Params(Params) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    if (!Cache.empty()) {
>>             +      S += Cache;
>>             +      return;
>>             +    }
>>             +
>>             +    OutputStream::StreamPosition Start =
>>             S.getCurrentPosition();
>>             +
>>             +    S += "<";
>>             +    Params.printWithSeperator(S, ", ");
>>             +    if (S.back() == '>')
>>             +      S += " ";
>>             +    S += ">";
>>             +
>>             +    Cache = S.makeStringViewFromPastPosition(Start);
>>             +  }
>>             +};
>>             +
>>             +class NameWithTemplateArgs final : public Node {
>>             +  // name<template_args>
>>             +  Node *Name;
>>             +  Node *TemplateArgs;
>>             +
>>             +public:
>>             +  NameWithTemplateArgs(Node *Name, Node *TemplateArgs)
>>             +      : Node(KNameWithTemplateArgs), Name(Name),
>>             TemplateArgs(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) {}
>>             +
>>             +  StringView getBaseName() const override { return
>>             Child->getBaseName(); }
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "::";
>>             +    Child->print(S);
>>             +  }
>>             +};
>>             +
>>             +class StdQualifiedName final : public Node {
>>             +  Node *Child;
>>             +
>>             +public:
>>             +  StdQualifiedName(Node *Child) :
>>             Node(KStdQualifiedName), Child(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) {}
>>             +
>>             +  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");
>>             +    }
>>             +  }
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    switch (SSK) {
>>             +    case SpecialSubKind::allocator:
>>             +      S += "std::basic_string<char,
>>             std::char_traits<char>, "
>>             +           "std::allocator<char> >";
>>             +      break;
>>             +    case SpecialSubKind::basic_string:
>>             +    case SpecialSubKind::string:
>>             +      S += "std::basic_string<char,
>>             std::char_traits<char>, "
>>             +           "std::allocator<char> >";
>>             +      break;
>>             +    case SpecialSubKind::istream:
>>             +      S += "std::basic_istream<char,
>>             std::char_traits<char> >";
>>             +      break;
>>             +    case SpecialSubKind::ostream:
>>             +      S += "std::basic_ostream<char,
>>             std::char_traits<char> >";
>>             +      break;
>>             +    case SpecialSubKind::iostream:
>>             +      S += "std::basic_iostream<char,
>>             std::char_traits<char> >";
>>             +      break;
>>             +    }
>>             +  }
>>             +};
>>             +
>>             +class SpecialSubstitution final : public Node {
>>             +public:
>>             +  SpecialSubKind SSK;
>>             +
>>             +  SpecialSubstitution(SpecialSubKind SSK)
>>             +      : Node(KSpecialSubstitution), SSK(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");
>>             +    }
>>             +  }
>>             +
>>             +  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;
>>             +
>>             +public:
>>             +  CtorDtorName(Node *Basename, bool IsDtor)
>>             +      : Node(KCtorDtorName), Basename(Basename),
>>             IsDtor(IsDtor) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    if (IsDtor)
>>             +      S += "~";
>>             +    S += Basename->getBaseName();
>>             +  }
>>             +};
>>             +
>>             +class DtorName : public Node {
>>             +  const Node *Base;
>>             +
>>             +public:
>>             +  DtorName(Node *Base) : Node(KDtorName), Base(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) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "'unnamed";
>>             +    S += Count;
>>             +    S += "\'";
>>             +  }
>>             +};
>>             +
>>             +class LambdaTypeName : public Node {
>>             +  NodeArray Params;
>>             +  StringView Count;
>>             +
>>             +public:
>>             +  LambdaTypeName(NodeArray Params, StringView Count)
>>             +      : Node(KLambdaTypeName), Params(Params),
>>             Count(Count) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "\'lambda";
>>             +    S += Count;
>>             +    S += "\'(";
>>             +    Params.printWithSeperator(S, ", ");
>>             +    S += ")";
>>             +  }
>>             +};
>>             +
>>             +// -- Expression Nodes --
>>             +
>>             +struct Expr : public Node {
>>             +  Expr() : Node(KExpr) {}
>>             +};
>>             +
>>             +class BinaryExpr : public Expr {
>>             +  const Node *LHS;
>>             +  const StringView InfixOperator;
>>             +  const Node *RHS;
>>             +
>>             +public:
>>             +  BinaryExpr(Node *LHS, StringView InfixOperator, Node *RHS)
>>             +      : LHS(LHS), InfixOperator(InfixOperator), RHS(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 Expr {
>>             +  const Node *Op1;
>>             +  const Node *Op2;
>>             +
>>             +public:
>>             +  ArraySubscriptExpr(Node *Op1, Node *Op2) : Op1(Op1),
>>             Op2(Op2) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "(";
>>             +    Op1->print(S);
>>             +    S += ")[";
>>             +    Op2->print(S);
>>             +    S += "]";
>>             +  }
>>             +};
>>             +
>>             +class PostfixExpr : public Expr {
>>             +  const Node *Child;
>>             +  const StringView Operand;
>>             +
>>             +public:
>>             +  PostfixExpr(Node *Child, StringView Operand)
>>             +      : Child(Child), Operand(Operand) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "(";
>>             +    Child->print(S);
>>             +    S += ")";
>>             +    S += Operand;
>>             +  }
>>             +};
>>             +
>>             +class ConditionalExpr : public Expr {
>>             +  const Node *Cond;
>>             +  const Node *Then;
>>             +  const Node *Else;
>>             +
>>             +public:
>>             +  ConditionalExpr(Node *Cond, Node *Then, Node *Else)
>>             +      : Cond(Cond), Then(Then), Else(Else) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "(";
>>             +    Cond->print(S);
>>             +    S += ") ? (";
>>             +    Then->print(S);
>>             +    S += ") : (";
>>             +    Else->print(S);
>>             +    S += ")";
>>             +  }
>>             +};
>>             +
>>             +class MemberExpr : public Expr {
>>             +  const Node *LHS;
>>             +  const StringView Kind;
>>             +  const Node *RHS;
>>             +
>>             +public:
>>             +  MemberExpr(Node *LHS, StringView Kind, Node *RHS)
>>             +      : LHS(LHS), Kind(Kind), RHS(RHS) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    LHS->print(S);
>>             +    S += Kind;
>>             +    RHS->print(S);
>>             +  }
>>             +};
>>             +
>>             +class EnclosingExpr : public Expr {
>>             +  const StringView Prefix;
>>             +  const Node *Infix;
>>             +  const StringView Postfix;
>>             +
>>             +public:
>>             +  EnclosingExpr(StringView Prefix, Node *Infix,
>>             StringView Postfix)
>>             +      : Prefix(Prefix), Infix(Infix), Postfix(Postfix) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += Prefix;
>>             +    Infix->print(S);
>>             +    S += Postfix;
>>             +  }
>>             +};
>>             +
>>             +class CastExpr : public Expr {
>>             +  // cast_kind<to>(from)
>>             +  const StringView CastKind;
>>             +  const Node *To;
>>             +  const Node *From;
>>             +
>>             +public:
>>             +  CastExpr(StringView CastKind, Node *To, Node *From)
>>             +      : CastKind(CastKind), To(To), From(From) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += CastKind;
>>             +    S += "<";
>>             +    To->printLeft(S);
>>             +    S += ">(";
>>             +    From->printLeft(S);
>>             +    S += ")";
>>             +  }
>>             +};
>>             +
>>             +class SizeofParamPackExpr : public Expr {
>>             +  NodeArray Args;
>>             +
>>             +public:
>>             +  SizeofParamPackExpr(NodeArray Args) : Args(Args) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "sizeof...(";
>>             +    Args.printWithSeperator(S, ", ");
>>             +    S += ")";
>>             +  }
>>             +};
>>             +
>>             +class CallExpr : public Expr {
>>             +  const Node *Callee;
>>             +  NodeArray Args;
>>             +
>>             +public:
>>             +  CallExpr(Node *Callee, NodeArray Args) :
>>             Callee(Callee), Args(Args) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    Callee->print(S);
>>             +    S += "(";
>>             +    Args.printWithSeperator(S, ", ");
>>             +    S += ")";
>>             +  }
>>             +};
>>             +
>>             +class NewExpr : public Expr {
>>             +  // 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)
>>             +      : ExprList(ExprList), Type(Type),
>>             InitList(InitList), IsGlobal(IsGlobal),
>>             +        IsArray(IsArray) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    if (IsGlobal)
>>             +      S += "::operator ";
>>             +    S += "new";
>>             +    if (IsArray)
>>             +      S += "[]";
>>             +    if (!ExprList.empty()) {
>>             +      S += "(";
>>             +      ExprList.printWithSeperator(S, ", ");
>>             +      S += ")";
>>             +    }
>>             +    Type->print(S);
>>             +    if (!InitList.empty()) {
>>             +      S += "(";
>>             +      InitList.printWithSeperator(S, ", ");
>>             +      S += ")";
>>             +    }
>>             +  }
>>             +};
>>             +
>>             +class DeleteExpr : public Expr {
>>             +  Node *Op;
>>             +  bool IsGlobal;
>>             +  bool IsArray;
>>             +
>>             +public:
>>             +  DeleteExpr(Node *Op, bool IsGlobal, bool IsArray)
>>             +      : Op(Op), IsGlobal(IsGlobal), IsArray(IsArray) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    if (IsGlobal)
>>             +      S += "::";
>>             +    S += "delete";
>>             +    if (IsArray)
>>             +      S += "[] ";
>>             +    Op->print(S);
>>             +  }
>>             +};
>>             +
>>             +class PrefixExpr : public Expr {
>>             +  StringView Prefix;
>>             +  Node *Child;
>>             +
>>             +public:
>>             +  PrefixExpr(StringView Prefix, Node *Child) :
>>             Prefix(Prefix), Child(Child) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += Prefix;
>>             +    S += "(";
>>             +    Child->print(S);
>>             +    S += ")";
>>             +  }
>>             +};
>>             +
>>             +class FunctionParam : public Expr {
>>             +  StringView Number;
>>             +
>>             +public:
>>             +  FunctionParam(StringView Number) : Number(Number) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "fp";
>>             +    S += Number;
>>             +  }
>>             +};
>>             +
>>             +class ExprList : public Expr {
>>             +  NodeArray SubExprs;
>>             +
>>             +public:
>>             +  ExprList(NodeArray SubExprs) : SubExprs(SubExprs) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "(";
>>             +    SubExprs.printWithSeperator(S, ", ");
>>             +    S += ")";
>>             +  }
>>             +};
>>             +
>>             +class ConversionExpr : public Expr {
>>             +  NodeArray Expressions;
>>             +  NodeArray Types;
>>             +
>>             +public:
>>             +  ConversionExpr(NodeArray Expressions, NodeArray Types)
>>             +      : Expressions(Expressions), Types(Types) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "(";
>>             +    Expressions.printWithSeperator(S, ", ");
>>             +    S += ")(";
>>             +    Types.printWithSeperator(S, ", ");
>>             +    S += ")";
>>             +  }
>>             +};
>>             +
>>             +class ThrowExpr : public Expr {
>>             +  const Node *Op;
>>             +
>>             +public:
>>             +  ThrowExpr(Node *Op) : Op(Op) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "throw ";
>>             +    Op->print(S);
>>             +  }
>>             +};
>>             +
>>             +class BoolExpr : public Expr {
>>             +  bool Value;
>>             +
>>             +public:
>>             +  BoolExpr(bool Value) : Value(Value) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += Value ? StringView("true") : StringView("false");
>>             +  }
>>             +};
>>             +
>>             +class IntegerCastExpr : public Expr {
>>             +  // ty(integer)
>>             +  Node *Ty;
>>             +  StringView Integer;
>>             +
>>             +public:
>>             +  IntegerCastExpr(Node *Ty, StringView Integer) :
>>             Ty(Ty), Integer(Integer) {}
>>             +
>>             +  void printLeft(OutputStream &S) const override {
>>             +    S += "(";
>>             +    Ty->print(S);
>>             +    S += ")";
>>             +    S += Integer;
>>             +  }
>>             +};
>>             +
>>             +class IntegerExpr : public Expr {
>>             +  StringView Type;
>>             +  StringView Value;
>>             +
>>             +public:
>>             +  IntegerExpr(StringView Type, StringView Value) :
>>             Type(Type), Value(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 <class Float> struct FloatData;
>>             +
>>             +template <class Float> class FloatExpr : public Expr {
>>             +  const StringView Contents;
>>             +
>>             +public:
>>             +  FloatExpr(StringView Contents) : Contents(Contents) {}
>>             +
>>             +  void printLeft(OutputStream &s) const override {
>>             +    const char *first = Contents.begin();
>>             +    const char *last = Contents.end() + 1;
>>             +
>>             +    const size_t N = FloatData<Float>::mangled_size;
>>             +    if (static_cast<std::size_t>(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<unsigned>(*t - '0')
>>             +                                  :
>>             static_cast<unsigned>(*t - 'a' + 10);
>>             +        ++t;
>>             +        unsigned d0 = isdigit(*t) ?
>>             static_cast<unsigned>(*t - '0')
>>             +                                  :
>>             static_cast<unsigned>(*t - 'a' + 10);
>>             +        *e = static_cast<char>((d1 << 4) + d0);
>>             +      }
>>             +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
>>             +      std::reverse(buf, e);
>>             +#endif
>>             +      char num[FloatData<Float>::max_demangled_size] = {0};
>>             +      int n = snprintf(num, sizeof(num),
>>             FloatData<Float>::spec, value);
>>             +      s += StringView(num, num + n);
>>             +    }
>>             +  }
>>             +};
>>             +
>>              template <std::size_t N>
>>              class arena
>>              {
>>             @@ -148,168 +1511,118 @@ operator!=(const short_alloc<T,
>>             N>& x, c
>>                  return !(x == y);
>>              }
>>
>>             -template <class T>
>>             -class malloc_alloc
>>             -{
>>             -public:
>>             -    typedef T value_type;
>>             -    typedef T& reference;
>>             -    typedef const T& const_reference;
>>             -    typedef T* pointer;
>>             -    typedef const T* const_pointer;
>>             -    typedef std::size_t size_type;
>>             -    typedef std::ptrdiff_t difference_type;
>>             -
>>             -    malloc_alloc() = default;
>>             -    template <class U> malloc_alloc(const
>>             malloc_alloc<U>&) noexcept {}
>>             -
>>             -    T* allocate(std::size_t n)
>>             -    {
>>             -        return static_cast<T*>(std::malloc(n*sizeof(T)));
>>             -    }
>>             -    void deallocate(T* p, std::size_t) noexcept
>>             -    {
>>             -        std::free(p);
>>             -    }
>>             -
>>             -    template <class U> struct rebind { using other =
>>             malloc_alloc<U>; };
>>             -    template <class U, class... Args>
>>             -    void construct(U* p, Args&&... args)
>>             -    {
>>             -        ::new ((void*)p) U(std::forward<Args>(args)...);
>>             -    }
>>             -    void destroy(T* p)
>>             -    {
>>             -        p->~T();
>>             -    }
>>             -};
>>             -
>>             -template <class T, class U>
>>             -inline
>>             -bool
>>             -operator==(const malloc_alloc<T>&, const
>>             malloc_alloc<U>&) noexcept
>>             -{
>>             -    return true;
>>             -}
>>             -
>>             -template <class T, class U>
>>             -inline
>>             -bool
>>             -operator!=(const malloc_alloc<T>& x, const
>>             malloc_alloc<U>& y) noexcept
>>             -{
>>             -    return !(x == y);
>>             -}
>>             -
>>              const size_t bs = 4 * 1024;
>>              template <class T> using Alloc = short_alloc<T, bs>;
>>              template <class T> using Vector = std::vector<T, Alloc<T>>;
>>
>>             -template <class StrT>
>>             -struct string_pair
>>             -{
>>             -    StrT first;
>>             -    StrT second;
>>             +class BumpPointerAllocator {
>>             +  struct BlockMeta {
>>             +    BlockMeta* Next;
>>             +    size_t Current;
>>             +  };
>>             +
>>             +  static constexpr size_t AllocSize = 4096;
>>             +  static constexpr size_t UsableAllocSize = AllocSize -
>>             sizeof(BlockMeta);
>>             +
>>             +  alignas(16) char InitialBuffer[AllocSize];
>>             +  BlockMeta* BlockList = nullptr;
>>             +
>>             +  void grow() {
>>             +    char* NewMeta = new char[AllocSize];
>>             +    BlockList = new (NewMeta) BlockMeta{BlockList, 0};
>>             +  }
>>             +
>>             +  void* allocateMassive(size_t NBytes) {
>>             +    NBytes += sizeof(BlockMeta);
>>             +    BlockMeta* NewMeta =
>>             reinterpret_cast<BlockMeta*>(new char[NBytes]);
>>             +    BlockList->Next = new (NewMeta)
>>             BlockMeta{BlockList->Next, 0};
>>             +    return static_cast<void*>(NewMeta + 1);
>>             +  }
>>
>>             -    string_pair() = default;
>>             -    string_pair(StrT f) : first(std::move(f)) {}
>>             -    string_pair(StrT f, StrT s)
>>             -        : first(std::move(f)), second(std::move(s)) {}
>>             -    template <size_t N>
>>             -        string_pair(const char (&s)[N]) : first(s, N-1) {}
>>             +public:
>>             +  BumpPointerAllocator()
>>             +      : BlockList(new (InitialBuffer) BlockMeta{nullptr,
>>             0}) {}
>>
>>             -    size_t size() const {return first.size() +
>>             second.size();}
>>             -    bool empty() const { return first.empty() &&
>>             second.empty(); }
>>             -    StrT full() const {return first + second;}
>>             -    StrT move_full() {return std::move(first) +
>>             std::move(second);}
>>             +  void* allocate(size_t N) {
>>             +    N = (N + 15u) & ~15u;
>>             +    if (N + BlockList->Current >= UsableAllocSize) {
>>             +      if (N > UsableAllocSize)
>>             +        return allocateMassive(N);
>>             +      grow();
>>             +    }
>>             +    BlockList->Current += N;
>>             +    return
>>             static_cast<void*>(reinterpret_cast<char*>(BlockList + 1) +
>>             + BlockList->Current - N);
>>             +  }
>>             +
>>             +  ~BumpPointerAllocator() {
>>             +    while (BlockList) {
>>             +      BlockMeta* Tmp = BlockList;
>>             +      BlockList = BlockList->Next;
>>             +      if (reinterpret_cast<char*>(Tmp) != InitialBuffer)
>>             +        delete[] reinterpret_cast<char*>(Tmp);
>>             +    }
>>             +  }
>>              };
>>
>>              struct Db
>>              {
>>             -    typedef std::basic_string<char, std::char_traits<char>,
>>             - malloc_alloc<char>> String;
>>             -    typedef Vector<string_pair<String>> sub_type;
>>             +    typedef Vector<Node*> sub_type;
>>                  typedef Vector<sub_type> template_param_type;
>>                  sub_type names;
>>                  template_param_type subs;
>>                  Vector<template_param_type> template_param;
>>             -    unsigned cv = 0;
>>             -    unsigned ref = 0;
>>             +    Qualifiers cv = QualNone;
>>             +    FunctionRefQual ref = FrefQualNone;
>>                  unsigned encoding_depth = 0;
>>                  bool parsed_ctor_dtor_cv = false;
>>                  bool tag_templates = true;
>>                  bool fix_forward_references = false;
>>                  bool try_to_parse_template_args = true;
>>
>>             +    BumpPointerAllocator ASTAllocator;
>>             +
>>                  template <size_t N>
>>                  Db(arena<N>& ar) :
>>                      names(ar),
>>                      subs(0, names, ar),
>>                      template_param(0, subs, ar)
>>                  {}
>>             -};
>>             -
>>
>>             -const char* parse_type(const char* first, const char*
>>             last, Db& db);
>>             -const char* parse_encoding(const char* first, const
>>             char* last, Db& db);
>>             -const char* parse_name(const char* first, const char*
>>             last, Db& db,
>>             -                       bool* ends_with_template_args = 0);
>>             -const char* parse_expression(const char* first, const
>>             char* last, Db& db);
>>             -const char* parse_template_args(const char* first, const
>>             char* last, Db& db);
>>             -const char* parse_operator_name(const char* first, const
>>             char* last, Db& db);
>>             -const char* parse_unqualified_name(const char* first,
>>             const char* last, Db& db);
>>             -const char* parse_decltype(const char* first, const
>>             char* last, Db& db);
>>             +    template <class T, class... Args> T* make(Args&&
>>             ...args)
>>             +    {
>>             +        return new (ASTAllocator.allocate(sizeof(T)))
>>             + T(std::forward<Args>(args)...);
>>             +    }
>>
>>             -template <class C>
>>             -void
>>             -print_stack(const C& db)
>>             -{
>>             -    fprintf(stderr, "---------\n");
>>             -    fprintf(stderr, "names:\n");
>>             -    for (auto& s : db.names)
>>             -        fprintf(stderr, "{%s#%s}\n", s.first.c_str(),
>>             s.second.c_str());
>>             -    int i = -1;
>>             -    fprintf(stderr, "subs:\n");
>>             -    for (auto& v : db.subs)
>>             +    template <class It> NodeArray makeNodeArray(It
>>             begin, It end)
>>                  {
>>             -        if (i >= 0)
>>             -            fprintf(stderr, "S%i_ = {", i);
>>             -        else
>>             -            fprintf(stderr, "S_  = {");
>>             -        for (auto& s : v)
>>             -            fprintf(stderr, "{%s#%s}", s.first.c_str(),
>>             s.second.c_str());
>>             -        fprintf(stderr, "}\n");
>>             -        ++i;
>>             -    }
>>             -    fprintf(stderr, "template_param:\n");
>>             -    for (auto& t : db.template_param)
>>             -    {
>>             -        fprintf(stderr, "--\n");
>>             -        i = -1;
>>             -        for (auto& v : t)
>>             -        {
>>             -            if (i >= 0)
>>             -                fprintf(stderr, "T%i_ = {", i);
>>             -            else
>>             -                fprintf(stderr, "T_  = {");
>>             -            for (auto& s : v)
>>             -                fprintf(stderr, "{%s#%s}",
>>             s.first.c_str(), s.second.c_str());
>>             -            fprintf(stderr, "}\n");
>>             -            ++i;
>>             -        }
>>             +        size_t sz = static_cast<size_t>(end - begin);
>>             +        void* mem = ASTAllocator.allocate(sizeof(Node*)
>>             * sz);
>>             +        Node** data = new (mem) Node*[sz];
>>             +        std::copy(begin, end, data);
>>             +        return NodeArray(data, sz);
>>                  }
>>             -    fprintf(stderr, "---------\n\n");
>>             -}
>>
>>             -template <class C>
>>             -void
>>             -print_state(const char* msg, const char* first, const
>>             char* last, const C& db)
>>             -{
>>             -    fprintf(stderr, "%s: ", msg);
>>             -    for (; first != last; ++first)
>>             -        fprintf(stderr, "%c", *first);
>>             -    fprintf(stderr, "\n");
>>             -    print_stack(db);
>>             -}
>>             +    NodeArray popTrailingNodeArray(size_t FromPosition)
>>             +    {
>>             +        assert(FromPosition <= names.size());
>>             +        NodeArray res = makeNodeArray(
>>             +            names.begin() + (long)FromPosition,
>>             names.end());
>>             +        names.erase(names.begin() + (long)FromPosition,
>>             names.end());
>>             +        return res;
>>             +    }
>>             +};
>>             +
>>             +const char* parse_type(const char* first, const char*
>>             last, Db& db);
>>             +const char* parse_encoding(const char* first, const
>>             char* last, Db& db);
>>             +const char* parse_name(const char* first, const char*
>>             last, Db& db,
>>             +                       bool* ends_with_template_args = 0);
>>             +const char* parse_expression(const char* first, const
>>             char* last, Db& db);
>>             +const char* parse_template_args(const char* first, const
>>             char* last, Db& db);
>>             +const char* parse_operator_name(const char* first, const
>>             char* last, Db& db);
>>             +const char* parse_unqualified_name(const char* first,
>>             const char* last, Db& db);
>>             +const char* parse_decltype(const char* first, const
>>             char* last, Db& db);
>>
>>              // <number> ::= [n] <non-negative decimal integer>
>>
>>             @@ -339,30 +1652,30 @@ parse_number(const char* first,
>>             const ch
>>              }
>>
>>              template <class Float>
>>             -struct float_data;
>>             +struct FloatData;
>>
>>              template <>
>>             -struct float_data<float>
>>             +struct FloatData<float>
>>              {
>>                  static const size_t mangled_size = 8;
>>                  static const size_t max_demangled_size = 24;
>>                  static constexpr const char* spec = "%af";
>>              };
>>
>>             -constexpr const char* float_data<float>::spec;
>>             +constexpr const char* FloatData<float>::spec;
>>
>>              template <>
>>             -struct float_data<double>
>>             +struct FloatData<double>
>>              {
>>                  static const size_t mangled_size = 16;
>>                  static const size_t max_demangled_size = 32;
>>                  static constexpr const char* spec = "%a";
>>              };
>>
>>             -constexpr const char* float_data<double>::spec;
>>             +constexpr const char* FloatData<double>::spec;
>>
>>              template <>
>>             -struct float_data<long double>
>>             +struct FloatData<long double>
>>              {
>>              #if defined(__mips__) && defined(__mips_n64) ||
>>             defined(__aarch64__) || \
>>                  defined(__wasm__)
>>             @@ -376,46 +1689,27 @@ struct float_data<long double>
>>                  static constexpr const char* spec = "%LaL";
>>              };
>>
>>             -constexpr const char* float_data<long double>::spec;
>>             +constexpr const char* FloatData<long double>::spec;
>>
>>              template <class Float>
>>              const char*
>>              parse_floating_number(const char* first, const char*
>>             last, Db& db)
>>              {
>>             -    const size_t N = float_data<Float>::mangled_size;
>>             -    if (static_cast<std::size_t>(last - first) > N)
>>             +    const size_t N = FloatData<Float>::mangled_size;
>>             +    if (static_cast<std::size_t>(last - first) <= N)
>>             +        return first;
>>             +    last = first + N;
>>             +    const char* t = first;
>>             +    for (; t != last; ++t)
>>                  {
>>             -        last = first + N;
>>             -        union
>>             -        {
>>             -            Float value;
>>             -            char buf[sizeof(Float)];
>>             -        };
>>             -        const char* t = first;
>>             -        char* e = buf;
>>             -        for (; t != last; ++t, ++e)
>>             -        {
>>             -            if (!isxdigit(*t))
>>             -                return first;
>>             -            unsigned d1 = isdigit(*t) ?
>>             static_cast<unsigned>(*t - '0') :
>>             - static_cast<unsigned>(*t - 'a' + 10);
>>             -            ++t;
>>             -            unsigned d0 = isdigit(*t) ?
>>             static_cast<unsigned>(*t - '0') :
>>             - static_cast<unsigned>(*t - 'a' + 10);
>>             -            *e = static_cast<char>((d1 << 4) + d0);
>>             -        }
>>             -        if (*t == 'E')
>>             -        {
>>             -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
>>             -            std::reverse(buf, e);
>>             -#endif
>>             -            char
>>             num[float_data<Float>::max_demangled_size] = {0};
>>             -            int n = snprintf(num, sizeof(num),
>>             float_data<Float>::spec, value);
>>             -            if (static_cast<std::size_t>(n) >= sizeof(num))
>>             -                return first;
>>             -            db.names.push_back(Db::String(num,
>>             static_cast<std::size_t>(n)));
>>             -            first = t+1;
>>             -        }
>>             +        if (!isxdigit(*t))
>>             +            return first;
>>             +    }
>>             +    if (*t == 'E')
>>             +    {
>>             +        db.names.push_back(
>>             + db.make<FloatExpr<Float>>(StringView(first, t)));
>>             +        first = t + 1;
>>                  }
>>                  return first;
>>              }
>>             @@ -440,11 +1734,11 @@ parse_source_name(const char*
>>             first, con
>>                          }
>>                          if (static_cast<size_t>(last - t) >= n)
>>                          {
>>             -                Db::String r(t, n);
>>             +                StringView r(t, t + n);
>>                              if (r.substr(0, 10) == "_GLOBAL__N")
>>             - db.names.push_back("(anonymous namespace)");
>>             + db.names.push_back(db.make<NameType>("(anonymous
>>             namespace)"));
>>                              else
>>             - db.names.push_back(std::move(r));
>>             + db.names.push_back(db.make<NameType>(r));
>>                              first = t + n;
>>                          }
>>                      }
>>             @@ -473,27 +1767,32 @@ parse_substitution(const char*
>>             first, co
>>                          switch (first[1])
>>                          {
>>                          case 'a':
>>             - db.names.push_back("std::allocator");
>>             +                db.names.push_back(
>>             + db.make<SpecialSubstitution>(
>>             + SpecialSubKind::allocator));
>>                              first += 2;
>>                              break;
>>                          case 'b':
>>             - db.names.push_back("std::basic_string");
>>             +                db.names.push_back(
>>             +
>>             db.make<SpecialSubstitution>(SpecialSubKind::basic_string));
>>                              first += 2;
>>                              break;
>>                          case 's':
>>             - db.names.push_back("std::string");
>>             +                db.names.push_back(
>>             + db.make<SpecialSubstitution>(
>>             + SpecialSubKind::string));
>>                              first += 2;
>>                              break;
>>                          case 'i':
>>             - db.names.push_back("std::istream");
>>             +
>>             db.names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::istream));
>>                              first += 2;
>>                              break;
>>                          case 'o':
>>             - db.names.push_back("std::ostream");
>>             +
>>             db.names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::ostream));
>>                              first += 2;
>>                              break;
>>                          case 'd':
>>             - db.names.push_back("std::iostream");
>>             +
>>             db.names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::iostream));
>>                              first += 2;
>>                              break;
>>                          case '_':
>>             @@ -578,87 +1877,87 @@ parse_builtin_type(const char*
>>             first, co
>>                      switch (*first)
>>                      {
>>                      case 'v':
>>             -            db.names.push_back("void");
>>             + db.names.push_back(db.make<NameType>("void"));
>>                          ++first;
>>                          break;
>>                      case 'w':
>>             -            db.names.push_back("wchar_t");
>>             + db.names.push_back(db.make<NameType>("wchar_t"));
>>                          ++first;
>>                          break;
>>                      case 'b':
>>             -            db.names.push_back("bool");
>>             + db.names.push_back(db.make<NameType>("bool"));
>>                          ++first;
>>                          break;
>>                      case 'c':
>>             -            db.names.push_back("char");
>>             + db.names.push_back(db.make<NameType>("char"));
>>                          ++first;
>>                          break;
>>                      case 'a':
>>             -            db.names.push_back("signed char");
>>             + db.names.push_back(db.make<NameType>("signed char"));
>>                          ++first;
>>                          break;
>>                      case 'h':
>>             -            db.names.push_back("unsigned char");
>>             + db.names.push_back(db.make<NameType>("unsigned char"));
>>                          ++first;
>>                          break;
>>                      case 's':
>>             -            db.names.push_back("short");
>>             + db.names.push_back(db.make<NameType>("short"));
>>                          ++first;
>>                          break;
>>                      case 't':
>>             -            db.names.push_back("unsigned short");
>>             + db.names.push_back(db.make<NameType>("unsigned short"));
>>                          ++first;
>>                          break;
>>                      case 'i':
>>             -            db.names.push_back("int");
>>             + db.names.push_back(db.make<NameType>("int"));
>>                          ++first;
>>                          break;
>>                      case 'j':
>>             -            db.names.push_back("unsigned int");
>>             + db.names.push_back(db.make<NameType>("unsigned int"));
>>                          ++first;
>>                          break;
>>                      case 'l':
>>             -            db.names.push_back("long");
>>             + db.names.push_back(db.make<NameType>("long"));
>>                          ++first;
>>                          break;
>>                      case 'm':
>>             -            db.names.push_back("unsigned long");
>>             + db.names.push_back(db.make<NameType>("unsigned long"));
>>                          ++first;
>>                          break;
>>                      case 'x':
>>             -            db.names.push_back("long long");
>>             + db.names.push_back(db.make<NameType>("long long"));
>>                          ++first;
>>                          break;
>>                      case 'y':
>>             -            db.names.push_back("unsigned long long");
>>             + db.names.push_back(db.make<NameType>("unsigned long
>>             long"));
>>                          ++first;
>>                          break;
>>                      case 'n':
>>             -            db.names.push_back("__int128");
>>             + db.names.push_back(db.make<NameType>("__int128"));
>>                          ++first;
>>                          break;
>>                      case 'o':
>>             -            db.names.push_back("unsigned __int128");
>>             + db.names.push_back(db.make<NameType>("unsigned __int128"));
>>                          ++first;
>>                          break;
>>                      case 'f':
>>             -            db.names.push_back("float");
>>             + db.names.push_back(db.make<NameType>("float"));
>>                          ++first;
>>                          break;
>>                      case 'd':
>>             -            db.names.push_back("double");
>>             + db.names.push_back(db.make<NameType>("double"));
>>                          ++first;
>>                          break;
>>                      case 'e':
>>             -            db.names.push_back("long double");
>>             + db.names.push_back(db.make<NameType>("long double"));
>>                          ++first;
>>                          break;
>>                      case 'g':
>>             -            db.names.push_back("__float128");
>>             + db.names.push_back(db.make<NameType>("__float128"));
>>                          ++first;
>>                          break;
>>                      case 'z':
>>             -            db.names.push_back("...");
>>             + db.names.push_back(db.make<NameType>("..."));
>>                          ++first;
>>                          break;
>>                      case 'u':
>>             @@ -674,39 +1973,39 @@ parse_builtin_type(const char*
>>             first, co
>>                              switch (first[1])
>>                              {
>>                              case 'd':
>>             - db.names.push_back("decimal64");
>>             + db.names.push_back(db.make<NameType>("decimal64"));
>>                                  first += 2;
>>                                  break;
>>                              case 'e':
>>             - db.names.push_back("decimal128");
>>             + db.names.push_back(db.make<NameType>("decimal128"));
>>                                  first += 2;
>>                                  break;
>>                              case 'f':
>>             - db.names.push_back("decimal32");
>>             + db.names.push_back(db.make<NameType>("decimal32"));
>>                                  first += 2;
>>                                  break;
>>                              case 'h':
>>             - db.names.push_back("decimal16");
>>             + db.names.push_back(db.make<NameType>("decimal16"));
>>                                  first += 2;
>>                                  break;
>>                              case 'i':
>>             - db.names.push_back("char32_t");
>>             + db.names.push_back(db.make<NameType>("char32_t"));
>>                                  first += 2;
>>                                  break;
>>                              case 's':
>>             - db.names.push_back("char16_t");
>>             + db.names.push_back(db.make<NameType>("char16_t"));
>>                                  first += 2;
>>                                  break;
>>                              case 'a':
>>             - db.names.push_back("auto");
>>             + db.names.push_back(db.make<NameType>("auto"));
>>                                  first += 2;
>>                                  break;
>>                              case 'c':
>>             - db.names.push_back("decltype(auto)");
>>             + db.names.push_back(db.make<NameType>("decltype(auto)"));
>>                                  first += 2;
>>                                  break;
>>                              case 'n':
>>             - db.names.push_back("std::nullptr_t");
>>             + db.names.push_back(db.make<NameType>("std::nullptr_t"));
>>                                  first += 2;
>>                                  break;
>>                              }
>>             @@ -717,27 +2016,27 @@ parse_builtin_type(const char*
>>             first, co
>>                  return first;
>>              }
>>
>>             -// <CV-qualifiers> ::= [r] [V] [K]
>>             +// <CV-Qualifiers> ::= [r] [V] [K]
>>
>>              const char*
>>             -parse_cv_qualifiers(const char* first, const char* last,
>>             unsigned& cv)
>>             +parse_cv_qualifiers(const char* first, const char* last,
>>             Qualifiers& cv)
>>              {
>>             -    cv = 0;
>>             +    cv = QualNone;
>>                  if (first != last)
>>                  {
>>                      if (*first == 'r')
>>                      {
>>             -            cv |= 4;
>>             +            addQualifiers(cv, QualRestrict);
>>                          ++first;
>>                      }
>>                      if (*first == 'V')
>>                      {
>>             -            cv |= 2;
>>             +            addQualifiers(cv, QualVolatile);
>>                          ++first;
>>                      }
>>                      if (*first == 'K')
>>                      {
>>             -            cv |= 1;
>>             +            addQualifiers(cv, QualConst);
>>                          ++first;
>>                      }
>>                  }
>>             @@ -766,7 +2065,7 @@ parse_template_param(const char* first,
>>                              }
>>                              else
>>                              {
>>             - db.names.push_back("T_");
>>             + db.names.push_back(db.make<NameType>("T_"));
>>                                  first += 2;
>>              db.fix_forward_references = true;
>>                              }
>>             @@ -791,7 +2090,8 @@ parse_template_param(const char* first,
>>                              }
>>                              else
>>                              {
>>             - db.names.push_back(Db::String(first, t+1));
>>             +                    db.names.push_back(
>>             + db.make<NameType>(StringView(first, t + 1)));
>>                                  first = t+1;
>>              db.fix_forward_references = true;
>>                              }
>>             @@ -816,11 +2116,12 @@ parse_const_cast_expr(const char*
>>             first,
>>                          {
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto expr = db.names.back().move_full();
>>             +                auto from_expr = db.names.back();
>>                              db.names.pop_back();
>>                              if (db.names.empty())
>>                                  return first;
>>             -                db.names.back() = "const_cast<" +
>>             db.names.back().move_full() + ">(" + expr + ")";
>>             +                db.names.back() = db.make<CastExpr>(
>>             +                    "const_cast", db.names.back(),
>>             from_expr);
>>                              first = t1;
>>                          }
>>                      }
>>             @@ -843,11 +2144,12 @@ parse_dynamic_cast_expr(const
>>             char* firs
>>                          {
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto expr = db.names.back().move_full();
>>             +                auto from_expr = db.names.back();
>>                              db.names.pop_back();
>>                              if (db.names.empty())
>>                                  return first;
>>             -                db.names.back() = "dynamic_cast<" +
>>             db.names.back().move_full() + ">(" + expr + ")";
>>             +                db.names.back() = db.make<CastExpr>(
>>             +                    "dynamic_cast", db.names.back(),
>>             from_expr);
>>                              first = t1;
>>                          }
>>                      }
>>             @@ -870,11 +2172,12 @@ parse_reinterpret_cast_expr(const
>>             char*
>>                          {
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto expr = db.names.back().move_full();
>>             +                auto from_expr = db.names.back();
>>                              db.names.pop_back();
>>                              if (db.names.empty())
>>                                  return first;
>>             -                db.names.back() = "reinterpret_cast<" +
>>             db.names.back().move_full() + ">(" + expr + ")";
>>             +                db.names.back() = db.make<CastExpr>(
>>             +                    "reinterpret_cast", db.names.back(),
>>             from_expr);
>>                              first = t1;
>>                          }
>>                      }
>>             @@ -897,9 +2200,10 @@ parse_static_cast_expr(const char*
>>             first
>>                          {
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto expr = db.names.back().move_full();
>>             +                auto from_expr = db.names.back();
>>                              db.names.pop_back();
>>             -                db.names.back() = "static_cast<" +
>>             db.names.back().move_full() + ">(" + expr + ")";
>>             +                db.names.back() = db.make<CastExpr>(
>>             +                    "static_cast", db.names.back(),
>>             from_expr);
>>                              first = t1;
>>                          }
>>                      }
>>             @@ -933,7 +2237,8 @@ parse_sizeof_type_expr(const char* first
>>                      {
>>                          if (db.names.empty())
>>                              return first;
>>             -            db.names.back() = "sizeof (" +
>>             db.names.back().move_full() + ")";
>>             +            db.names.back() = db.make<EnclosingExpr>(
>>             +                "sizeof (", db.names.back(), ")");
>>                          first = t;
>>                      }
>>                  }
>>             @@ -952,7 +2257,8 @@ parse_sizeof_expr_expr(const char* first
>>                      {
>>                          if (db.names.empty())
>>                              return first;
>>             -            db.names.back() = "sizeof (" +
>>             db.names.back().move_full() + ")";
>>             +            db.names.back() = db.make<EnclosingExpr>(
>>             +                "sizeof (", db.names.back(), ")");
>>                          first = t;
>>                      }
>>                  }
>>             @@ -969,30 +2275,21 @@ parse_sizeof_param_pack_expr(const
>>             char*
>>                      size_t k0 = db.names.size();
>>                      const char* t = parse_template_param(first+2,
>>             last, db);
>>                      size_t k1 = db.names.size();
>>             -        if (t != first+2)
>>             +        if (t != first+2 && k0 <= k1)
>>                      {
>>             -            Db::String tmp("sizeof...(");
>>             -            size_t k = k0;
>>             -            if (k != k1)
>>             -            {
>>             -                tmp += db.names[k].move_full();
>>             -                for (++k; k != k1; ++k)
>>             -                    tmp += ", " + db.names[k].move_full();
>>             -            }
>>             -            tmp += ")";
>>             -            for (; k1 != k0; --k1)
>>             -                db.names.pop_back();
>>             -            db.names.push_back(std::move(tmp));
>>             +            Node* sizeof_expr =
>>             db.make<SizeofParamPackExpr>(
>>             + db.popTrailingNodeArray(k0));
>>             +            db.names.push_back(sizeof_expr);
>>                          first = t;
>>                      }
>>                  }
>>                  return first;
>>              }
>>
>>             -// <function-param> ::= fp <top-level CV-qualifiers> _  
>>                                    # L == 0, first parameter
>>             -//                  ::= fp <top-level CV-qualifiers>
>>             <parameter-2 non-negative number> _   # L == 0, second
>>             and later parameters
>>             -//                  ::= fL <L-1 non-negative number> p
>>             <top-level CV-qualifiers> _         # L > 0, first parameter
>>             -//                  ::= fL <L-1 non-negative number> p
>>             <top-level CV-qualifiers> <parameter-2 non-negative
>>             number> _   # L > 0, second and later parameters
>>             +// <function-param> ::= fp <top-level CV-Qualifiers> _  
>>                                    # L == 0, first parameter
>>             +//                  ::= fp <top-level CV-Qualifiers>
>>             <parameter-2 non-negative number> _   # L == 0, second
>>             and later parameters
>>             +//                  ::= fL <L-1 non-negative number> p
>>             <top-level CV-Qualifiers> _         # L > 0, first parameter
>>             +//                  ::= fL <L-1 non-negative number> p
>>             <top-level CV-Qualifiers> <parameter-2 non-negative
>>             number> _   # L > 0, second and later parameters
>>
>>              const char*
>>              parse_function_param(const char* first, const char*
>>             last, Db& db)
>>             @@ -1001,18 +2298,19 @@ parse_function_param(const char*
>>             first,
>>                  {
>>                      if (first[1] == 'p')
>>                      {
>>             -            unsigned cv;
>>             +            Qualifiers cv;
>>                          const char* t = parse_cv_qualifiers(first+2,
>>             last, cv);
>>                          const char* t1 = parse_number(t, last);
>>                          if (t1 != last && *t1 == '_')
>>                          {
>>             -                db.names.push_back("fp" + Db::String(t,
>>             t1));
>>             +                db.names.push_back(
>>             + db.make<FunctionParam>(StringView(t, t1)));
>>                              first = t1+1;
>>                          }
>>                      }
>>                      else if (first[1] == 'L')
>>                      {
>>             -            unsigned cv;
>>             +            Qualifiers cv;
>>                          const char* t0 = parse_number(first+2, last);
>>                          if (t0 != last && *t0 == 'p')
>>                          {
>>             @@ -1021,7 +2319,8 @@ parse_function_param(const char* first,
>>                              const char* t1 = parse_number(t, last);
>>                              if (t1 != last && *t1 == '_')
>>                              {
>>             -                    db.names.push_back("fp" +
>>             Db::String(t, t1));
>>             +                    db.names.push_back(
>>             + db.make<FunctionParam>(StringView(t, t1)));
>>                                  first = t1+1;
>>                              }
>>                          }
>>             @@ -1042,7 +2341,8 @@
>>             parse_sizeof_function_param_pack_expr(co
>>                      {
>>                          if (db.names.empty())
>>                              return first;
>>             -            db.names.back() = "sizeof...(" +
>>             db.names.back().move_full() + ")";
>>             +            db.names.back() = db.make<EnclosingExpr>(
>>             +                "sizeof...(", db.names.back(), ")");
>>                          first = t;
>>                      }
>>                  }
>>             @@ -1066,7 +2366,8 @@ parse_typeid_expr(const char*
>>             first, con
>>                      {
>>                          if (db.names.empty())
>>                              return first;
>>             -            db.names.back() = "typeid(" +
>>             db.names.back().move_full() + ")";
>>             +            db.names.back() = db.make<EnclosingExpr>(
>>             +                "typeid(", db.names.back(), ")");
>>                          first = t;
>>                      }
>>                  }
>>             @@ -1085,7 +2386,7 @@ parse_throw_expr(const char* first,
>>             cons
>>                      {
>>                          if (db.names.empty())
>>                              return first;
>>             -            db.names.back() = "throw " +
>>             db.names.back().move_full();
>>             +            db.names.back() =
>>             db.make<ThrowExpr>(db.names.ba <http://db.names.ba>ck());
>>                          first = t;
>>                      }
>>                  }
>>             @@ -1107,9 +2408,10 @@ parse_dot_star_expr(const char*
>>             first, c
>>                          {
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto expr = db.names.back().move_full();
>>             +                auto rhs_expr = db.names.back();
>>                              db.names.pop_back();
>>             -                db.names.back().first += ".*" + expr;
>>             +                db.names.back() = db.make<MemberExpr>(
>>             +                    db.names.back(), ".*", rhs_expr);
>>                              first = t1;
>>                          }
>>                      }
>>             @@ -1132,9 +2434,10 @@ parse_simple_id(const char* first,
>>             const
>>                          {
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto args = db.names.back().move_full();
>>             +                auto args = db.names.back();
>>                              db.names.pop_back();
>>             -                db.names.back().first += std::move(args);
>>             +                db.names.back() =
>>             + db.make<NameWithTemplateArgs>(db.names.back(), args);
>>                          }
>>                          first = t1;
>>                      }
>>             @@ -1196,7 +2499,8 @@ parse_unresolved_type(const char*
>>             first,
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first.insert(0, "std::");
>>             +                        db.names.back() =
>>             + db.make<StdQualifiedName>(db.names.back());
>>              db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>                                      first = t;
>>                                  }
>>             @@ -1223,7 +2527,7 @@ parse_destructor_name(const char*
>>             first,
>>                      {
>>                          if (db.names.empty())
>>                              return first;
>>             -            db.names.back().first.insert(0, "~");
>>             +            db.names.back() =
>>             db.make<DtorName>(db.names.back());
>>                          first = t;
>>                      }
>>                  }
>>             @@ -1255,9 +2559,11 @@ parse_base_unresolved_name(const
>>             char* f
>>                                  {
>>                                      if (db.names.size() < 2)
>>                                          return first;
>>             -                        auto args =
>>             db.names.back().move_full();
>>             +                        auto args = db.names.back();
>>              db.names.pop_back();
>>             - db.names.back().first += std::move(args);
>>             +                        db.names.back() =
>>             + db.make<NameWithTemplateArgs>(
>>             + db.names.back(), args);
>>                                  }
>>                              }
>>                          }
>>             @@ -1281,9 +2587,11 @@ parse_base_unresolved_name(const
>>             char* f
>>                                  {
>>                                      if (db.names.size() < 2)
>>                                          return first;
>>             -                        auto args =
>>             db.names.back().move_full();
>>             +                        auto args = db.names.back();
>>              db.names.pop_back();
>>             - db.names.back().first += std::move(args);
>>             +                        db.names.back() =
>>             + db.make<NameWithTemplateArgs>(
>>             + db.names.back(), args);
>>                                  }
>>                              }
>>                          }
>>             @@ -1331,7 +2639,8 @@ parse_unresolved_name(const char*
>>             first,
>>                          {
>>                              if (db.names.empty())
>>                                  return first;
>>             - db.names.back().first.insert(0, "::");
>>             +                db.names.back() =
>>             + db.make<GlobalQualifiedName>(db.names.back());
>>                          }
>>                          first = t2;
>>                      }
>>             @@ -1349,9 +2658,10 @@ parse_unresolved_name(const char*
>>             first,
>>                              {
>>                                  if (db.names.size() < 2)
>>                                      return first;
>>             -                    auto args = db.names.back().move_full();
>>             +                    auto args = db.names.back();
>>                                  db.names.pop_back();
>>             -                    db.names.back().first +=
>>             std::move(args);
>>             +                    db.names.back() =
>>             db.make<NameWithTemplateArgs>(
>>             +                        db.names.back(), args);
>>                                  t = t1;
>>                                  if (t == last)
>>                                  {
>>             @@ -1364,9 +2674,10 @@ parse_unresolved_name(const char*
>>             first,
>>                                  t1 =
>>             parse_unresolved_qualifier_level(t, last, db);
>>                                  if (t1 == t || t1 == last ||
>>             db.names.size() < 2)
>>                                      return first;
>>             -                    auto s = db.names.back().move_full();
>>             +                    auto s = db.names.back();
>>                                  db.names.pop_back();
>>             -                    db.names.back().first += "::" +
>>             std::move(s);
>>             +                    db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), s);
>>                                  t = t1;
>>                              }
>>                              ++t;
>>             @@ -1379,9 +2690,10 @@ parse_unresolved_name(const char*
>>             first,
>>                              }
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto s = db.names.back().move_full();
>>             +                auto s = db.names.back();
>>                              db.names.pop_back();
>>             -                db.names.back().first += "::" +
>>             std::move(s);
>>             +                db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), s);
>>                              first = t1;
>>                          }
>>                          else
>>             @@ -1396,9 +2708,11 @@ parse_unresolved_name(const char*
>>             first,
>>                                  {
>>                                      if (db.names.size() < 2)
>>                                          return first;
>>             -                        auto args =
>>             db.names.back().move_full();
>>             +                        auto args = db.names.back();
>>              db.names.pop_back();
>>             - db.names.back().first += std::move(args);
>>             +                        db.names.back() =
>>             + db.make<NameWithTemplateArgs>(
>>             + db.names.back(), args);
>>                                      t = t1;
>>                                  }
>>                                  t1 = parse_base_unresolved_name(t,
>>             last, db);
>>             @@ -1410,9 +2724,10 @@ parse_unresolved_name(const char*
>>             first,
>>                                  }
>>                                  if (db.names.size() < 2)
>>                                      return first;
>>             -                    auto s = db.names.back().move_full();
>>             +                    auto s = db.names.back();
>>                                  db.names.pop_back();
>>             -                    db.names.back().first += "::" +
>>             std::move(s);
>>             +                    db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), s);
>>                                  first = t1;
>>                              }
>>                              else
>>             @@ -1425,16 +2740,19 @@ parse_unresolved_name(const char*
>>             first,
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first.insert(0, "::");
>>             +                        db.names.back() =
>>             + db.make<GlobalQualifiedName>(
>>             + db.names.back());
>>                                  }
>>                                  while (*t != 'E')
>>                                  {
>>                                      t1 =
>>             parse_unresolved_qualifier_level(t, last, db);
>>                                      if (t1 == t || t1 == last ||
>>             db.names.size() < 2)
>>                                          return first;
>>             -                        auto s =
>>             db.names.back().move_full();
>>             +                        auto s = db.names.back();
>>              db.names.pop_back();
>>             - db.names.back().first += "::" + std::move(s);
>>             +                        db.names.back() =
>>             db.make<QualifiedName>(
>>             + db.names.back(), s);
>>                                      t = t1;
>>                                  }
>>                                  ++t;
>>             @@ -1447,9 +2765,10 @@ parse_unresolved_name(const char*
>>             first,
>>                                  }
>>                                  if (db.names.size() < 2)
>>                                      return first;
>>             -                    auto s = db.names.back().move_full();
>>             +                    auto s = db.names.back();
>>                                  db.names.pop_back();
>>             -                    db.names.back().first += "::" +
>>             std::move(s);
>>             +                    db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), s);
>>                                  first = t1;
>>                              }
>>                          }
>>             @@ -1473,11 +2792,11 @@ parse_dot_expr(const char* first,
>>             const
>>                          {
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto name = db.names.back().move_full();
>>             +                auto name = db.names.back();
>>                              db.names.pop_back();
>>                              if (db.names.empty())
>>                                  return first;
>>             -                db.names.back().first += "." + name;
>>             +                db.names.back() =
>>             db.make<MemberExpr>(db.names.back(), ".", name);
>>                              first = t1;
>>                          }
>>                      }
>>             @@ -1493,44 +2812,25 @@ parse_call_expr(const char*
>>             first, const
>>                  if (last - first >= 4 && first[0] == 'c' && first[1]
>>             == 'l')
>>                  {
>>                      const char* t = parse_expression(first+2, last, db);
>>             -        if (t != first+2)
>>             +        if (t == last || t == first + 2 || db.names.empty())
>>             +            return first;
>>             +        Node* callee = db.names.back();
>>             +        db.names.pop_back();
>>             +        size_t args_begin = db.names.size();
>>             +        while (*t != 'E')
>>                      {
>>             -            if (t == last)
>>             -                return first;
>>             -            if (db.names.empty())
>>             -                return first;
>>             -            db.names.back().first += db.names.back().second;
>>             -            db.names.back().second = Db::String();
>>             -            db.names.back().first.append("(");
>>             -            bool first_expr = true;
>>             -            while (*t != 'E')
>>             -            {
>>             -                const char* t1 = parse_expression(t,
>>             last, db);
>>             -                if (t1 == t || t1 == last)
>>             -                    return first;
>>             -                if (db.names.empty())
>>             -                    return first;
>>             -                auto tmp = db.names.back().move_full();
>>             -                db.names.pop_back();
>>             -                if (!tmp.empty())
>>             -                {
>>             -                    if (db.names.empty())
>>             -                        return first;
>>             -                    if (!first_expr)
>>             -                    {
>>             - db.names.back().first.append(", ");
>>             -                        first_expr = false;
>>             -                    }
>>             - db.names.back().first.append(tmp);
>>             -                }
>>             -                t = t1;
>>             -            }
>>             -            ++t;
>>             -            if (db.names.empty())
>>             +            const char* t1 = parse_expression(t, last, db);
>>             +            if (t1 == last || t1 == t)
>>                              return first;
>>             -            db.names.back().first.append(")");
>>             -            first = t;
>>             +            t = t1;
>>                      }
>>             +        if (db.names.size() < args_begin)
>>             +            return first;
>>             +        ++t;
>>             +        CallExpr* the_call = db.make<CallExpr>(
>>             +            callee, db.popTrailingNodeArray(args_begin));
>>             +        db.names.push_back(the_call);
>>             +        first = t;
>>                  }
>>                  return first;
>>              }
>>             @@ -1559,31 +2859,18 @@ parse_new_expr(const char* first,
>>             const
>>                          t += 2;
>>                          if (t == last)
>>                              return first;
>>             -            bool has_expr_list = false;
>>             -            bool first_expr = true;
>>             +            size_t first_expr_in_list = db.names.size();
>>             +            NodeArray ExprList, init_list;
>>                          while (*t != '_')
>>                          {
>>                              const char* t1 = parse_expression(t,
>>             last, db);
>>                              if (t1 == t || t1 == last)
>>                                  return first;
>>             -                has_expr_list = true;
>>             -                if (!first_expr)
>>             -                {
>>             -                    if (db.names.empty())
>>             -                        return first;
>>             -                    auto tmp = db.names.back().move_full();
>>             -                    db.names.pop_back();
>>             -                    if (!tmp.empty())
>>             -                    {
>>             -                        if (db.names.empty())
>>             -                            return first;
>>             - db.names.back().first.append(", ");
>>             - db.names.back().first.append(tmp);
>>             -                        first_expr = false;
>>             -                    }
>>             -                }
>>                              t = t1;
>>                          }
>>             +            if (first_expr_in_list < db.names.size())
>>             +                return first;
>>             +            ExprList =
>>             db.popTrailingNodeArray(first_expr_in_list);
>>                          ++t;
>>                          const char* t1 = parse_type(t, last, db);
>>                          if (t1 == t || t1 == last)
>>             @@ -1594,65 +2881,25 @@ parse_new_expr(const char* first,
>>             const
>>                          {
>>                              t += 2;
>>                              has_init = true;
>>             -                first_expr = true;
>>             +                size_t init_list_begin = db.names.size();
>>                              while (*t != 'E')
>>                              {
>>                                  t1 = parse_expression(t, last, db);
>>                                  if (t1 == t || t1 == last)
>>                                      return first;
>>             -                    if (!first_expr)
>>             -                    {
>>             -                        if (db.names.empty())
>>             -                            return first;
>>             -                        auto tmp =
>>             db.names.back().move_full();
>>             - db.names.pop_back();
>>             -                        if (!tmp.empty())
>>             -                        {
>>             -                            if (db.names.empty())
>>             -                                return first;
>>             - db.names.back().first.append(", ");
>>             - db.names.back().first.append(tmp);
>>             -                            first_expr = false;
>>             -                        }
>>             -                    }
>>                                  t = t1;
>>                              }
>>             -            }
>>             -            if (*t != 'E')
>>             -                return first;
>>             -            Db::String init_list;
>>             -            if (has_init)
>>             -            {
>>             -                if (db.names.empty())
>>             +                if (init_list_begin < db.names.size())
>>                                  return first;
>>             -                init_list = db.names.back().move_full();
>>             -                db.names.pop_back();
>>             +                init_list =
>>             db.popTrailingNodeArray(init_list_begin);
>>                          }
>>             -            if (db.names.empty())
>>             +            if (*t != 'E')
>>                              return first;
>>             -            auto type = db.names.back().move_full();
>>             +            auto type = db.names.back();
>>                          db.names.pop_back();
>>             -            Db::String expr_list;
>>             -            if (has_expr_list)
>>             -            {
>>             -                if (db.names.empty())
>>             -                    return first;
>>             -                expr_list = db.names.back().move_full();
>>             -                db.names.pop_back();
>>             -            }
>>             -            Db::String r;
>>             -            if (parsed_gs)
>>             -                r = "::";
>>             -            if (is_array)
>>             -                r += "[] ";
>>             -            else
>>             -                r += " ";
>>             -            if (has_expr_list)
>>             -                r += "(" + expr_list + ") ";
>>             -            r += type;
>>             -            if (has_init)
>>             -                r += " (" + init_list + ")";
>>             -            db.names.push_back(std::move(r));
>>             +            db.names.push_back(
>>             + db.make<NewExpr>(ExprList, type, init_list,
>>             + parsed_gs, is_array));
>>                          first = t+1;
>>                      }
>>                  }
>>             @@ -1669,10 +2916,12 @@ parse_conversion_expr(const char*
>>             first,
>>                  {
>>                      bool try_to_parse_template_args =
>>             db.try_to_parse_template_args;
>>                      db.try_to_parse_template_args = false;
>>             +        size_t type_begin = db.names.size();
>>                      const char* t = parse_type(first+2, last, db);
>>                      db.try_to_parse_template_args =
>>             try_to_parse_template_args;
>>                      if (t != first+2 && t != last)
>>                      {
>>             +            size_t ExprList_begin = db.names.size();
>>                          if (*t != '_')
>>                          {
>>                              const char* t1 = parse_expression(t,
>>             last, db);
>>             @@ -1685,41 +2934,30 @@ parse_conversion_expr(const char*
>>             first,
>>                              ++t;
>>                              if (t == last)
>>                                  return first;
>>             -                if (*t == 'E')
>>             - db.names.emplace_back();
>>             -                else
>>             +                if (*t != 'E')
>>                              {
>>             -                    bool first_expr = true;
>>                                  while (*t != 'E')
>>                                  {
>>                                      const char* t1 =
>>             parse_expression(t, last, db);
>>                                      if (t1 == t || t1 == last)
>>                                          return first;
>>             -                        if (!first_expr)
>>             -                        {
>>             -                            if (db.names.empty())
>>             -                                return first;
>>             -                            auto tmp =
>>             db.names.back().move_full();
>>             - db.names.pop_back();
>>             -                            if (!tmp.empty())
>>             -                            {
>>             -                                if (db.names.empty())
>>             -                                    return first;
>>             - db.names.back().first.append(", ");
>>             - db.names.back().first.append(tmp);
>>             -                                first_expr = false;
>>             -                            }
>>             -                        }
>>                                      t = t1;
>>                                  }
>>                              }
>>                              ++t;
>>                          }
>>             -            if (db.names.size() < 2)
>>             +            if (db.names.size() < ExprList_begin)
>>                              return first;
>>             -            auto tmp = db.names.back().move_full();
>>             -            db.names.pop_back();
>>             -            db.names.back() = "(" +
>>             db.names.back().move_full() + ")(" + tmp + ")";
>>             +            NodeArray expressions = db.makeNodeArray(
>>             +                db.names.begin() + (long)ExprList_begin,
>>             db.names.end());
>>             +            NodeArray types = db.makeNodeArray(
>>             +                db.names.begin() + (long)type_begin,
>>             +                db.names.begin() + (long)ExprList_begin);
>>             +            auto* conv_expr = db.make<ConversionExpr>(
>>             +                types, expressions);
>>             +            db.names.erase(
>>             +                db.names.begin() + (long)type_begin,
>>             db.names.end());
>>             +            db.names.push_back(conv_expr);
>>                          first = t;
>>                      }
>>                  }
>>             @@ -1741,10 +2979,10 @@ parse_arrow_expr(const char*
>>             first, cons
>>                          {
>>                              if (db.names.size() < 2)
>>                                  return first;
>>             -                auto tmp = db.names.back().move_full();
>>             +                auto tmp = db.names.back();
>>                              db.names.pop_back();
>>             -                db.names.back().first += "->";
>>             -                db.names.back().first += tmp;
>>             +                db.names.back() = db.make<MemberExpr>(
>>             +                    db.names.back(), "->", tmp);
>>                              first = t1;
>>                          }
>>                      }
>>             @@ -1772,11 +3010,13 @@ parse_function_type(const char*
>>             first, c
>>                                  return first;
>>                          }
>>                          const char* t1 = parse_type(t, last, db);
>>             -            if (t1 != t)
>>             +            if (t1 != t && !db.names.empty())
>>                          {
>>             +                Node* ret_type = db.names.back();
>>             +                db.names.pop_back();
>>             +                size_t params_begin = db.names.size();
>>                              t = t1;
>>             -                Db::String sig("(");
>>             -                int ref_qual = 0;
>>             +                FunctionRefQual RefQuals = FrefQualNone;
>>                              while (true)
>>                              {
>>                                  if (t == last)
>>             @@ -1797,45 +3037,30 @@ parse_function_type(const char*
>>             first, c
>>                                  }
>>                                  if (*t == 'R' && t+1 != last && t[1]
>>             == 'E')
>>                                  {
>>             -                        ref_qual = 1;
>>             +                        RefQuals = FrefQualLValue;
>>                                      ++t;
>>                                      continue;
>>                                  }
>>                                  if (*t == 'O' && t+1 != last && t[1]
>>             == 'E')
>>                                  {
>>             -                        ref_qual = 2;
>>             +                        RefQuals = FrefQualRValue;
>>                                      ++t;
>>                                      continue;
>>                                  }
>>                                  size_t k0 = db.names.size();
>>                                  t1 = parse_type(t, last, db);
>>                                  size_t k1 = db.names.size();
>>             -                    if (t1 == t || t1 == last)
>>             +                    if (t1 == t || t1 == last || k1 < k0)
>>                                      return first;
>>             -                    for (size_t k = k0; k < k1; ++k)
>>             -                    {
>>             -                        if (sig.size() > 1)
>>             -                            sig += ", ";
>>             -                        sig += db.names[k].move_full();
>>             -                    }
>>             -                    for (size_t k = k0; k < k1; ++k)
>>             - db.names.pop_back();
>>                                  t = t1;
>>                              }
>>             -                sig += ")";
>>             -                switch (ref_qual)
>>             -                {
>>             -                case 1:
>>             -                    sig += " &";
>>             -                    break;
>>             -                case 2:
>>             -                    sig += " &&";
>>             -                    break;
>>             -                }
>>                              if (db.names.empty())
>>                                  return first;
>>             -                db.names.back().first += " ";
>>             - db.names.back().second.insert(0, sig);
>>             +                Node* fty = db.make<FunctionType>(
>>             +                    ret_type,
>>             db.popTrailingNodeArray(params_begin));
>>             +                if (RefQuals)
>>             +                    fty =
>>             db.make<FunctionRefQualType>(fty, RefQuals);
>>             +                db.names.push_back(fty);
>>                              first = t;
>>                          }
>>                      }
>>             @@ -1860,17 +3085,9 @@ parse_pointer_to_member_type(const
>>             char*
>>                                  return first;
>>                              auto func = std::move(db.names.back());
>>                              db.names.pop_back();
>>             -                auto class_type =
>>             std::move(db.names.back());
>>             -                if (!func.second.empty() &&
>>             func.second.front() == '(')
>>             -                {
>>             -                    db.names.back().first =
>>             std::move(func.first) + "(" + class_type.move_full() + "::*";
>>             -                    db.names.back().second = ")" +
>>             std::move(func.second);
>>             -                }
>>             -                else
>>             -                {
>>             -                    db.names.back().first =
>>             std::move(func.first) + " " + class_type.move_full() + "::*";
>>             -                    db.names.back().second =
>>             std::move(func.second);
>>             -                }
>>             +                auto ClassType = std::move(db.names.back());
>>             +                db.names.back() =
>>             + db.make<PointerToMemberType>(ClassType, func);
>>                              first = t2;
>>                          }
>>                      }
>>             @@ -1893,9 +3110,7 @@ parse_array_type(const char* first,
>>             cons
>>                          {
>>                              if (db.names.empty())
>>                                  return first;
>>             -                if (db.names.back().second.substr(0, 2)
>>             == " [")
>>             - db.names.back().second.erase(0, 1);
>>             - db.names.back().second.insert(0, " []");
>>             +                db.names.back() =
>>             db.make<ArrayType>(db.names.ba <http://db.names.ba>ck());
>>                              first = t;
>>                          }
>>                      }
>>             @@ -1909,9 +3124,9 @@ parse_array_type(const char* first,
>>             cons
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             -                    if (db.names.back().second.substr(0,
>>             2) == " [")
>>             - db.names.back().second.erase(0, 1);
>>             - db.names.back().second.insert(0, " [" +
>>             Db::String(first+1, t) + "]");
>>             +                    db.names.back() =
>>             + db.make<ArrayType>(db.names.ba <http://db.names.ba>ck(),
>>             + StringView(first + 1, t));
>>                                  first = t2;
>>                              }
>>                          }
>>             @@ -1926,13 +3141,11 @@ parse_array_type(const char*
>>             first, cons
>>                              {
>>                                  if (db.names.size() < 2)
>>                                      return first;
>>             -                    auto type = std::move(db.names.back());
>>             +                    auto base_type =
>>             std::move(db.names.back());
>>                                  db.names.pop_back();
>>             -                    auto expr = std::move(db.names.back());
>>             -                    db.names.back().first =
>>             std::move(type.first);
>>             -                    if (type.second.substr(0, 2) == " [")
>>             - type.second.erase(0, 1);
>>             -                    db.names.back().second = " [" +
>>             expr.move_full() + "]" + std::move(type.second);
>>             +                    auto dimension_expr =
>>             std::move(db.names.back());
>>             +                    db.names.back() =
>>             + db.make<ArrayType>(base_type, dimension_expr);
>>                                  first = t2;
>>                              }
>>                          }
>>             @@ -1959,7 +3172,8 @@ parse_decltype(const char* first, const
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             -                    db.names.back() = "decltype(" +
>>             db.names.back().move_full() + ")";
>>             +                    db.names.back() =
>>             db.make<EnclosingExpr>(
>>             +                        "decltype(", db.names.back(), ")");
>>                                  first = t+1;
>>                              }
>>                          }
>>             @@ -1997,21 +3211,24 @@ parse_vector_type(const char*
>>             first, con
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first += " vector[" + Db::String(num,
>>             sz) + "]";
>>             +                        db.names.back() =
>>             + db.make<VectorType>(db.names.back(),
>>             +      StringView(num, num + sz));
>>                                      first = t1;
>>                                  }
>>                              }
>>                              else
>>                              {
>>                                  ++t;
>>             - db.names.push_back("pixel vector[" + Db::String(num,
>>             sz) + "]");
>>             +                    db.names.push_back(
>>             + db.make<VectorType>(StringView(num, num + sz)));
>>                                  first = t;
>>                              }
>>                          }
>>                      }
>>                      else
>>                      {
>>             -            Db::String num;
>>             +            Node* num = nullptr;
>>                          const char* t1 = first+2;
>>                          if (*t1 != '_')
>>                          {
>>             @@ -2020,7 +3237,7 @@ parse_vector_type(const char*
>>             first, con
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             -                    num = db.names.back().move_full();
>>             +                    num = db.names.back();
>>                                  db.names.pop_back();
>>                                  t1 = t;
>>                              }
>>             @@ -2032,9 +3249,15 @@ parse_vector_type(const char*
>>             first, con
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             -                    db.names.back().first += " vector["
>>             + num + "]";
>>             +                    if (num)
>>             +                        db.names.back() =
>>             + db.make<VectorType>(db.names.back(), num);
>>             +                    else
>>             +                        db.names.back() =
>>             + db.make<VectorType>(db.names.back(), StringView());
>>                                  first = t;
>>             -                }
>>             +                } else if (num)
>>             + db.names.push_back(num);
>>                          }
>>                      }
>>                  }
>>             @@ -2050,7 +3273,7 @@ parse_vector_type(const char*
>>             first, con
>>              //        ::= <template-template-param> <template-args>
>>              //        ::= <decltype>
>>              //        ::= <substitution>
>>             -//        ::= <CV-qualifiers> <type>
>>             +//        ::= <CV-Qualifiers> <type>
>>              //        ::= P <type>        # pointer-to
>>              //        ::= R <type>        # reference-to
>>              //        ::= O <type>        # rvalue reference-to (C++0x)
>>             @@ -2075,7 +3298,7 @@ parse_type(const char* first, const
>>             char
>>                          case 'V':
>>                          case 'K':
>>                            {
>>             -                unsigned cv = 0;
>>             +                Qualifiers cv = QualNone;
>>                              const char* t =
>>             parse_cv_qualifiers(first, last, cv);
>>                              if (t != first)
>>                              {
>>             @@ -2090,35 +3313,13 @@ parse_type(const char* first,
>>             const char
>>              db.subs.emplace_back(db.names.get_allocator());
>>                                      for (size_t k = k0; k < k1; ++k)
>>                                      {
>>             -                            if (is_function)
>>             -                            {
>>             -                                size_t p =
>>             db.names[k].second.size();
>>             -                                if (db.names[k].second[p
>>             - 2] == '&' &&
>>             - db.names[k].second[p - 1] == '&')
>>             -                                    p -= 2;
>>             -                                else if
>>             (db.names[k].second.back() == '&')
>>             -                                    p -= 1;
>>             -                                if (cv & 1)
>>             -                                {
>>             - db.names[k].second.insert(p, " const");
>>             -                                    p += 6;
>>             -                                }
>>             -                                if (cv & 2)
>>             -                                {
>>             - db.names[k].second.insert(p, " volatile");
>>             -                                    p += 9;
>>             -                                }
>>             -                                if (cv & 4)
>>             - db.names[k].second.insert(p, " restrict");
>>             -                            }
>>             -                            else
>>             -                            {
>>             -                                if (cv & 1)
>>             - db.names[k].first.append(" const");
>>             -                                if (cv & 2)
>>             - db.names[k].first.append(" volatile");
>>             -                                if (cv & 4)
>>             - db.names[k].first.append(" restrict");
>>             +                            if (cv) {
>>             +                                if (is_function)
>>             + db.names[k] = db.make<FunctionQualType>(
>>             + db.names[k], cv);
>>             +                                else
>>             + db.names[k] =
>>             + db.make<QualType>(db.names[k], cv);
>>                                          }
>>              db.subs.back().push_back(db.names[k]);
>>                                      }
>>             @@ -2154,7 +3355,8 @@ parse_type(const char* first, const
>>             char
>>                                      {
>>                                          if (db.names.empty())
>>                                              return first;
>>             - db.names.back().first.append(" complex");
>>             +                            db.names.back() =
>>             db.make<PostfixQualifiedType>(
>>             + db.names.back(), " complex");
>>                                          first = t;
>>              db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>                                      }
>>             @@ -2175,7 +3377,8 @@ parse_type(const char* first, const
>>             char
>>                                      {
>>                                          if (db.names.empty())
>>                                              return first;
>>             - db.names.back().first.append(" imaginary");
>>             +                            db.names.back() =
>>             db.make<PostfixQualifiedType>(
>>             + db.names.back(), " imaginary");
>>                                          first = t;
>>              db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>                                      }
>>             @@ -2200,18 +3403,8 @@ parse_type(const char* first,
>>             const char
>>              db.subs.emplace_back(db.names.get_allocator());
>>                                          for (size_t k = k0; k < k1; ++k)
>>                                          {
>>             -                                if
>>             (db.names[k].second.substr(0, 2) == " [")
>>             -                                {
>>             - db.names[k].first += " (";
>>             - db.names[k].second.insert(0, ")");
>>             -                                }
>>             -                                else if
>>             (!db.names[k].second.empty() &&
>>             - db.names[k].second.front() == '(')
>>             -                                {
>>             - db.names[k].first += "(";
>>             - db.names[k].second.insert(0, ")");
>>             -                                }
>>             - db.names[k].first.append("&&");
>>             +                                db.names[k] =
>>             + db.make<RValueReferenceType>(db.names[k]);
>>              db.subs.back().push_back(db.names[k]);
>>                                          }
>>                                          first = t;
>>             @@ -2228,25 +3421,7 @@ parse_type(const char* first,
>>             const char
>>              db.subs.emplace_back(db.names.get_allocator());
>>                                          for (size_t k = k0; k < k1; ++k)
>>                                          {
>>             -                                if
>>             (db.names[k].second.substr(0, 2) == " [")
>>             -                                {
>>             - db.names[k].first += " (";
>>             - db.names[k].second.insert(0, ")");
>>             -                                }
>>             -                                else if
>>             (!db.names[k].second.empty() &&
>>             - db.names[k].second.front() == '(')
>>             -                                {
>>             - db.names[k].first += "(";
>>             - db.names[k].second.insert(0, ")");
>>             -                                }
>>             -                                if (first[1] != 'U' ||
>>             db.names[k].first.substr(0, 12) != "objc_object<")
>>             -                                {
>>             - db.names[k].first.append("*");
>>             -                                }
>>             -                                else
>>             -                                {
>>             - db.names[k].first.replace(0, 11, "id");
>>             -                                }
>>             +                                db.names[k] =
>>             db.make<PointerType>(db.names[k]);
>>              db.subs.back().push_back(db.names[k]);
>>                                          }
>>                                          first = t;
>>             @@ -2263,18 +3438,8 @@ parse_type(const char* first,
>>             const char
>>              db.subs.emplace_back(db.names.get_allocator());
>>                                          for (size_t k = k0; k < k1; ++k)
>>                                          {
>>             -                                if
>>             (db.names[k].second.substr(0, 2) == " [")
>>             -                                {
>>             - db.names[k].first += " (";
>>             - db.names[k].second.insert(0, ")");
>>             -                                }
>>             -                                else if
>>             (!db.names[k].second.empty() &&
>>             - db.names[k].second.front() == '(')
>>             -                                {
>>             - db.names[k].first += "(";
>>             - db.names[k].second.insert(0, ")");
>>             -                                }
>>             - db.names[k].first.append("&");
>>             +                                db.names[k] =
>>             + db.make<LValueReferenceType>(db.names[k]);
>>              db.subs.back().push_back(db.names[k]);
>>                                          }
>>                                          first = t;
>>             @@ -2296,10 +3461,14 @@ parse_type(const char* first,
>>             const char
>>                                              const char* t1 =
>>             parse_template_args(t, last, db);
>>                                              if (t1 != t)
>>                                              {
>>             -                                    auto args =
>>             db.names.back().move_full();
>>             +                                    auto args =
>>             db.names.back();
>>              db.names.pop_back();
>>             - db.names.back().first += std::move(args);
>>             - db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>             + db.names.back() = db.make<
>>             + NameWithTemplateArgs>(
>>             + db.names.back(), args);
>>             + db.subs.push_back(Db::sub_type(
>>             +                                        1, db.names.back(),
>>             + db.names.get_allocator()));
>>                                                  t = t1;
>>                                              }
>>                                          }
>>             @@ -2318,24 +3487,25 @@ parse_type(const char* first,
>>             const char
>>                                              {
>>                                                  if (db.names.size() < 2)
>>              return first;
>>             -                                    auto type =
>>             db.names.back().move_full();
>>             +                                    auto type =
>>             db.names.back();
>>              db.names.pop_back();
>>             -                                    if
>>             (db.names.back().first.substr(0, 9) != "objcproto")
>>             +                                    if
>>             (db.names.back()->K != Node::KNameType ||
>>             +
>>             !static_cast<NameType*>(db.names.back())->getName().startsWith("objcproto"))
>>                                                  {
>>             - db.names.back() = type + " " + db.names.back().move_full();
>>             + db.names.back() = db.make<VendorExtQualType>(type,
>>             db.names.back());
>>                                                  }
>>                                                  else
>>                                                  {
>>             - auto proto = db.names.back().move_full();
>>             + auto* proto = static_cast<NameType*>(db.name
>>             <http://db.name>s.back());
>>              db.names.pop_back();
>>             -                                        t =
>>             parse_source_name(proto.data() + 9, proto.data() +
>>             proto.size(), db);
>>             -                                        if (t !=
>>             proto.data() + 9)
>>             +                                        t =
>>             parse_source_name(proto->getName().begin() + 9,
>>             proto->getName().end(), db);
>>             +                                        if (t !=
>>             proto->getName().begin() + 9)
>>                                                      {
>>             - db.names.back() = type + "<" +
>>             db.names.back().move_full() + ">";
>>             + db.names.back() = db.make<ObjCProtoName>(type,
>>             db.names.back());
>>                                                      }
>>              else
>>                                                      {
>>             - db.names.push_back(type + " " + proto);
>>             + db.names.push_back(db.make<VendorExtQualType>(type,
>>             proto));
>>                                                      }
>>                                                  }
>>              db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>             @@ -2371,9 +3541,11 @@ parse_type(const char* first,
>>             const char
>>                                                  {
>>                                                      if
>>             (db.names.size() < 2)
>>              return first;
>>             - auto template_args = db.names.back().move_full();
>>             + auto template_args = db.names.back();
>>              db.names.pop_back();
>>             - db.names.back().first += template_args;
>>             + db.names.back() = db.make<
>>             + NameWithTemplateArgs>(
>>             +   db.names.back(), template_args);
>>                                                      // Need to
>>             create substitution for <template-template-param>
>>             <template-args>
>>              db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>              first = t;
>>             @@ -2520,20 +3692,20 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'a':
>>             - db.names.push_back("operator&&");
>>             + db.names.push_back(db.make<NameType>("operator&&"));
>>                              first += 2;
>>                              break;
>>                          case 'd':
>>                          case 'n':
>>             - db.names.push_back("operator&");
>>             + db.names.push_back(db.make<NameType>("operator&"));
>>                              first += 2;
>>                              break;
>>                          case 'N':
>>             - db.names.push_back("operator&=");
>>             + db.names.push_back(db.make<NameType>("operator&="));
>>                              first += 2;
>>                              break;
>>                          case 'S':
>>             - db.names.push_back("operator=");
>>             + db.names.push_back(db.make<NameType>("operator="));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2542,15 +3714,15 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'l':
>>             - db.names.push_back("operator()");
>>             + db.names.push_back(db.make<NameType>("operator()"));
>>                              first += 2;
>>                              break;
>>                          case 'm':
>>             - db.names.push_back("operator,");
>>             + db.names.push_back(db.make<NameType>("operator,"));
>>                              first += 2;
>>                              break;
>>                          case 'o':
>>             - db.names.push_back("operator~");
>>             + db.names.push_back(db.make<NameType>("operator~"));
>>                              first += 2;
>>                              break;
>>                          case 'v':
>>             @@ -2563,7 +3735,8 @@ parse_operator_name(const char*
>>             first, c
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first.insert(0, "operator ");
>>             +                        db.names.back() =
>>             + db.make<ConversionOperatorType>(db.names.back());
>>              db.parsed_ctor_dtor_cv = true;
>>                                      first = t;
>>                                  }
>>             @@ -2575,23 +3748,23 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'a':
>>             - db.names.push_back("operator delete[]");
>>             + db.names.push_back(db.make<NameType>("operator delete[]"));
>>                              first += 2;
>>                              break;
>>                          case 'e':
>>             - db.names.push_back("operator*");
>>             + db.names.push_back(db.make<NameType>("operator*"));
>>                              first += 2;
>>                              break;
>>                          case 'l':
>>             - db.names.push_back("operator delete");
>>             + db.names.push_back(db.make<NameType>("operator delete"));
>>                              first += 2;
>>                              break;
>>                          case 'v':
>>             - db.names.push_back("operator/");
>>             + db.names.push_back(db.make<NameType>("operator/"));
>>                              first += 2;
>>                              break;
>>                          case 'V':
>>             - db.names.push_back("operator/=");
>>             + db.names.push_back(db.make<NameType>("operator/="));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2600,15 +3773,15 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'o':
>>             - db.names.push_back("operator^");
>>             + db.names.push_back(db.make<NameType>("operator^"));
>>                              first += 2;
>>                              break;
>>                          case 'O':
>>             - db.names.push_back("operator^=");
>>             + db.names.push_back(db.make<NameType>("operator^="));
>>                              first += 2;
>>                              break;
>>                          case 'q':
>>             - db.names.push_back("operator==");
>>             + db.names.push_back(db.make<NameType>("operator=="));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2617,11 +3790,11 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'e':
>>             - db.names.push_back("operator>=");
>>             + db.names.push_back(db.make<NameType>("operator>="));
>>                              first += 2;
>>                              break;
>>                          case 't':
>>             - db.names.push_back("operator>");
>>             + db.names.push_back(db.make<NameType>("operator>"));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2629,7 +3802,7 @@ parse_operator_name(const char*
>>             first, c
>>                      case 'i':
>>                          if (first[1] == 'x')
>>                          {
>>             - db.names.push_back("operator[]");
>>             + db.names.push_back(db.make<NameType>("operator[]"));
>>                              first += 2;
>>                          }
>>                          break;
>>             @@ -2637,7 +3810,7 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'e':
>>             - db.names.push_back("operator<=");
>>             + db.names.push_back(db.make<NameType>("operator<="));
>>                              first += 2;
>>                              break;
>>                          case 'i':
>>             @@ -2647,21 +3820,22 @@ parse_operator_name(const char*
>>             first, c
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first.insert(0, "operator\"\" ");
>>             +                        db.names.back() =
>>             + db.make<LiteralOperator>(db.na <http://db.na>mes.back());
>>                                      first = t;
>>                                  }
>>                              }
>>                              break;
>>                          case 's':
>>             - db.names.push_back("operator<<");
>>             + db.names.push_back(db.make<NameType>("operator<<"));
>>                              first += 2;
>>                              break;
>>                          case 'S':
>>             - db.names.push_back("operator<<=");
>>             + db.names.push_back(db.make<NameType>("operator<<="));
>>                              first += 2;
>>                              break;
>>                          case 't':
>>             - db.names.push_back("operator<");
>>             + db.names.push_back(db.make<NameType>("operator<"));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2670,23 +3844,23 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'i':
>>             - db.names.push_back("operator-");
>>             + db.names.push_back(db.make<NameType>("operator-"));
>>                              first += 2;
>>                              break;
>>                          case 'I':
>>             - db.names.push_back("operator-=");
>>             + db.names.push_back(db.make<NameType>("operator-="));
>>                              first += 2;
>>                              break;
>>                          case 'l':
>>             - db.names.push_back("operator*");
>>             + db.names.push_back(db.make<NameType>("operator*"));
>>                              first += 2;
>>                              break;
>>                          case 'L':
>>             - db.names.push_back("operator*=");
>>             + db.names.push_back(db.make<NameType>("operator*="));
>>                              first += 2;
>>                              break;
>>                          case 'm':
>>             - db.names.push_back("operator--");
>>             + db.names.push_back(db.make<NameType>("operator--"));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2695,23 +3869,23 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'a':
>>             - db.names.push_back("operator new[]");
>>             + db.names.push_back(db.make<NameType>("operator new[]"));
>>                              first += 2;
>>                              break;
>>                          case 'e':
>>             - db.names.push_back("operator!=");
>>             + db.names.push_back(db.make<NameType>("operator!="));
>>                              first += 2;
>>                              break;
>>                          case 'g':
>>             - db.names.push_back("operator-");
>>             + db.names.push_back(db.make<NameType>("operator-"));
>>                              first += 2;
>>                              break;
>>                          case 't':
>>             - db.names.push_back("operator!");
>>             + db.names.push_back(db.make<NameType>("operator!"));
>>                              first += 2;
>>                              break;
>>                          case 'w':
>>             - db.names.push_back("operator new");
>>             + db.names.push_back(db.make<NameType>("operator new"));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2720,15 +3894,15 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'o':
>>             - db.names.push_back("operator||");
>>             + db.names.push_back(db.make<NameType>("operator||"));
>>                              first += 2;
>>                              break;
>>                          case 'r':
>>             - db.names.push_back("operator|");
>>             + db.names.push_back(db.make<NameType>("operator|"));
>>                              first += 2;
>>                              break;
>>                          case 'R':
>>             - db.names.push_back("operator|=");
>>             + db.names.push_back(db.make<NameType>("operator|="));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2737,27 +3911,27 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'm':
>>             - db.names.push_back("operator->*");
>>             + db.names.push_back(db.make<NameType>("operator->*"));
>>                              first += 2;
>>                              break;
>>                          case 'l':
>>             - db.names.push_back("operator+");
>>             + db.names.push_back(db.make<NameType>("operator+"));
>>                              first += 2;
>>                              break;
>>                          case 'L':
>>             - db.names.push_back("operator+=");
>>             + db.names.push_back(db.make<NameType>("operator+="));
>>                              first += 2;
>>                              break;
>>                          case 'p':
>>             - db.names.push_back("operator++");
>>             + db.names.push_back(db.make<NameType>("operator++"));
>>                              first += 2;
>>                              break;
>>                          case 's':
>>             - db.names.push_back("operator+");
>>             + db.names.push_back(db.make<NameType>("operator+"));
>>                              first += 2;
>>                              break;
>>                          case 't':
>>             - db.names.push_back("operator->");
>>             + db.names.push_back(db.make<NameType>("operator->"));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2765,7 +3939,7 @@ parse_operator_name(const char*
>>             first, c
>>                      case 'q':
>>                          if (first[1] == 'u')
>>                          {
>>             - db.names.push_back("operator?");
>>             + db.names.push_back(db.make<NameType>("operator?"));
>>                              first += 2;
>>                          }
>>                          break;
>>             @@ -2773,19 +3947,19 @@ parse_operator_name(const char*
>>             first, c
>>                          switch (first[1])
>>                          {
>>                          case 'm':
>>             - db.names.push_back("operator%");
>>             + db.names.push_back(db.make<NameType>("operator%"));
>>                              first += 2;
>>                              break;
>>                          case 'M':
>>             - db.names.push_back("operator%=");
>>             + db.names.push_back(db.make<NameType>("operator%="));
>>                              first += 2;
>>                              break;
>>                          case 's':
>>             - db.names.push_back("operator>>");
>>             + db.names.push_back(db.make<NameType>("operator>>"));
>>                              first += 2;
>>                              break;
>>                          case 'S':
>>             - db.names.push_back("operator>>=");
>>             + db.names.push_back(db.make<NameType>("operator>>="));
>>                              first += 2;
>>                              break;
>>                          }
>>             @@ -2798,7 +3972,8 @@ parse_operator_name(const char*
>>             first, c
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             - db.names.back().first.insert(0, "operator ");
>>             +                    db.names.back() =
>>             + db.make<ConversionOperatorType>(db.names.back());
>>                                  first = t;
>>                              }
>>                          }
>>             @@ -2809,23 +3984,13 @@ parse_operator_name(const char*
>>             first, c
>>              }
>>
>>              const char*
>>             -parse_integer_literal(const char* first, const char*
>>             last, const Db::String& lit, Db& db)
>>             +parse_integer_literal(const char* first, const char*
>>             last, StringView lit, Db& db)
>>              {
>>                  const char* t = parse_number(first, last);
>>                  if (t != first && t != last && *t == 'E')
>>                  {
>>             -        if (lit.size() > 3)
>>             -            db.names.push_back("(" + lit + ")");
>>             -        else
>>             -            db.names.emplace_back();
>>             -        if (*first == 'n')
>>             -        {
>>             -            db.names.back().first += '-';
>>             -            ++first;
>>             -        }
>>             -        db.names.back().first.append(first, t);
>>             -        if (lit.size() <= 3)
>>             -            db.names.back().first += lit;
>>             +        db.names.push_back(
>>             +            db.make<IntegerExpr>(lit, StringView(first,
>>             t)));
>>                      first = t+1;
>>                  }
>>                  return first;
>>             @@ -2858,11 +4023,11 @@ parse_expr_primary(const char*
>>             first, co
>>                              switch (first[2])
>>                              {
>>                              case '0':
>>             - db.names.push_back("false");
>>             + db.names.push_back(db.make<BoolExpr>(0));
>>                                  first += 4;
>>                                  break;
>>                              case '1':
>>             - db.names.push_back("true");
>>             + db.names.push_back(db.make<BoolExpr>(1));
>>                                  first += 4;
>>                                  break;
>>                              }
>>             @@ -3007,7 +4172,8 @@ parse_expr_primary(const char*
>>             first, co
>>                                      {
>>                                          if (db.names.empty())
>>                                              return first;
>>             -                            db.names.back() = "(" +
>>             db.names.back().move_full() + ")" + Db::String(t, n);
>>             +                            db.names.back() =
>>             db.make<IntegerCastExpr>(
>>             + db.names.back(), StringView(t, n));
>>                                          first = n+1;
>>                                          break;
>>                                      }
>>             @@ -3024,69 +4190,22 @@ parse_expr_primary(const char*
>>             first, co
>>                  return first;
>>              }
>>
>>             -template <class String>
>>             -String
>>             -base_name(String& s)
>>             +Node* maybe_change_special_sub_name(Node* inp, Db& db)
>>              {
>>             -    if (s.empty())
>>             -        return s;
>>             -    if (s == "std::string")
>>             -    {
>>             -        s = "std::basic_string<char,
>>             std::char_traits<char>, std::allocator<char> >";
>>             -        return "basic_string";
>>             -    }
>>             -    if (s == "std::istream")
>>             -    {
>>             -        s = "std::basic_istream<char,
>>             std::char_traits<char> >";
>>             -        return "basic_istream";
>>             -    }
>>             -    if (s == "std::ostream")
>>             -    {
>>             -        s = "std::basic_ostream<char,
>>             std::char_traits<char> >";
>>             -        return "basic_ostream";
>>             -    }
>>             -    if (s == "std::iostream")
>>             -    {
>>             -        s = "std::basic_iostream<char,
>>             std::char_traits<char> >";
>>             -        return "basic_iostream";
>>             -    }
>>             -    const char* const pf = s.data();
>>             -    const char* pe = pf + s.size();
>>             -    if (pe[-1] == '>')
>>             -    {
>>             -        unsigned c = 1;
>>             -        while (true)
>>             -        {
>>             -            if (--pe == pf)
>>             -                return String();
>>             -            if (pe[-1] == '<')
>>             -            {
>>             -                if (--c == 0)
>>             -                {
>>             -                    --pe;
>>             -                    break;
>>             -                }
>>             -            }
>>             -            else if (pe[-1] == '>')
>>             -                ++c;
>>             -        }
>>             +    if (inp->K != Node::KSpecialSubstitution)
>>             +        return inp;
>>             +    auto Kind = static_cast<SpecialSubstitution*>(inp)->SSK;
>>             +    switch (Kind)
>>             +    {
>>             +    case SpecialSubKind::string:
>>             +    case SpecialSubKind::istream:
>>             +    case SpecialSubKind::ostream:
>>             +    case SpecialSubKind::iostream:
>>             +        return db.make<ExpandedSpecialSubstitution>(Kind);
>>             +    default:
>>             +        break;
>>                  }
>>             -    if (pe - pf <= 1)
>>             -      return String();
>>             -    const char* p0 = pe - 1;
>>             -    for (; p0 != pf; --p0)
>>             -    {
>>             -        if (*p0 == ':')
>>             -        {
>>             -            ++p0;
>>             -            break;
>>             -        }
>>             -        if (!isalpha(*p0) && !isdigit(*p0) && *p0 != '_')
>>             -        {
>>             -            return String();
>>             -        }
>>             -    }
>>             -    return String(p0, pe);
>>             +    return inp;
>>              }
>>
>>              // <ctor-dtor-name> ::= C1    # complete object constructor
>>             @@ -3114,7 +4233,10 @@ parse_ctor_dtor_name(const char*
>>             first,
>>                          case '5':
>>                              if (db.names.empty())
>>                                  return first;
>>             - db.names.push_back(base_name(db.names.back().first));
>>             +                db.names.back() =
>>             + maybe_change_special_sub_name(db.names.back(), db);
>>             +                db.names.push_back(
>>             + db.make<CtorDtorName>(db.names.back(), false));
>>                              first += 2;
>>                              db.parsed_ctor_dtor_cv = true;
>>                              break;
>>             @@ -3129,7 +4251,8 @@ parse_ctor_dtor_name(const char* first,
>>                          case '5':
>>                              if (db.names.empty())
>>                                  return first;
>>             -                db.names.push_back("~" +
>>             base_name(db.names.back().first));
>>             +                db.names.push_back(
>>             + db.make<CtorDtorName>(db.names.back(), true));
>>                              first += 2;
>>                              db.parsed_ctor_dtor_cv = true;
>>                              break;
>>             @@ -3157,106 +4280,63 @@ parse_unnamed_type_name(const
>>             char* firs
>>                      {
>>                      case 't':
>>                        {
>>             -            db.names.push_back(Db::String("'unnamed"));
>>                          const char* t0 = first+2;
>>                          if (t0 == last)
>>             -            {
>>             -                db.names.pop_back();
>>                              return first;
>>             -            }
>>             +            StringView count;
>>                          if (std::isdigit(*t0))
>>                          {
>>                              const char* t1 = t0 + 1;
>>                              while (t1 != last && std::isdigit(*t1))
>>                                  ++t1;
>>             - db.names.back().first.append(t0, t1);
>>             +                count = StringView(t0, t1);
>>                              t0 = t1;
>>                          }
>>             -            db.names.back().first.push_back('\'');
>>                          if (t0 == last || *t0 != '_')
>>             -            {
>>             -                db.names.pop_back();
>>                              return first;
>>             -            }
>>             + db.names.push_back(db.make<UnnamedTypeName>(count));
>>                          first = t0 + 1;
>>                        }
>>                          break;
>>                      case 'l':
>>                        {
>>             -            size_t lambda_pos = db.names.size();
>>             -            db.names.push_back(Db::String("'lambda'("));
>>             +            size_t begin_pos = db.names.size();
>>                          const char* t0 = first+2;
>>             +            NodeArray lambda_params;
>>                          if (first[2] == 'v')
>>                          {
>>             -                db.names.back().first += ')';
>>                              ++t0;
>>                          }
>>                          else
>>                          {
>>             -                bool is_first_it = true;
>>                              while (true)
>>                              {
>>             -                    long k0 =
>>             static_cast<long>(db.names.size());
>>                                  const char* t1 = parse_type(t0,
>>             last, db);
>>             -                    long k1 =
>>             static_cast<long>(db.names.size());
>>                                  if (t1 == t0)
>>                                      break;
>>             -                    if (k0 >= k1)
>>             -                        return first;
>>             -                    // If the call to parse_type above
>>             found a pack expansion
>>             -                    // substitution, then multiple names
>>             could have been
>>             -                    // inserted into the name table.
>>             Walk through the names,
>>             -                    // appending each onto the lambda's
>>             parameter list.
>>             - std::for_each(db.names.begin() + k0, db.names.begin() + k1,
>>             - [&](Db::sub_type::value_type &pair) {
>>             -                                      if (pair.empty())
>>             - return;
>>             -                                      auto &lambda =
>>             db.names[lambda_pos].first;
>>             -                                      if (!is_first_it)
>>             - lambda.append(", ");
>>             - is_first_it = false;
>>             - lambda.append(pair.move_full());
>>             -                                  });
>>             - db.names.erase(db.names.begin() + k0, db.names.end());
>>                                  t0 = t1;
>>                              }
>>             -                if (is_first_it)
>>             -                {
>>             -                    if (!db.names.empty())
>>             - db.names.pop_back();
>>             +                if (db.names.size() < begin_pos)
>>                                  return first;
>>             -                }
>>             -                if (db.names.empty() || db.names.size()
>>             - 1 != lambda_pos)
>>             -                  return first;
>>             - db.names.back().first.append(")");
>>             +                lambda_params =
>>             db.popTrailingNodeArray(begin_pos);
>>                          }
>>                          if (t0 == last || *t0 != 'E')
>>             -            {
>>             -              if (!db.names.empty())
>>             -                db.names.pop_back();
>>             -              return first;
>>             -            }
>>             +                return first;
>>                          ++t0;
>>                          if (t0 == last)
>>             -            {
>>             -                if(!db.names.empty())
>>             -                  db.names.pop_back();
>>                              return first;
>>             -            }
>>             +            StringView count;
>>                          if (std::isdigit(*t0))
>>                          {
>>                              const char* t1 = t0 + 1;
>>                              while (t1 != last && std::isdigit(*t1))
>>                                  ++t1;
>>             -
>>             db.names.back().first.insert(db.names.back().first.begin()+7,
>>             t0, t1);
>>             +                count = StringView(t0, t1);
>>                              t0 = t1;
>>                          }
>>                          if (t0 == last || *t0 != '_')
>>             -            {
>>             -                if(!db.names.empty())
>>             -                  db.names.pop_back();
>>                              return first;
>>             -            }
>>             +
>>             db.names.push_back(db.make<LambdaTypeName>(lambda_params,
>>             count));
>>                          first = t0 + 1;
>>                        }
>>                          break;
>>             @@ -3337,7 +4417,8 @@ parse_unscoped_name(const char*
>>             first, c
>>                          {
>>                              if (db.names.empty())
>>                                  return first;
>>             - db.names.back().first.insert(0, "std::");
>>             +                db.names.back() =
>>             + db.make<StdQualifiedName>(db.names.back());
>>                          }
>>                          first = t1;
>>                      }
>>             @@ -3357,7 +4438,8 @@ parse_alignof_type(const char*
>>             first, co
>>                      {
>>                          if (db.names.empty())
>>                              return first;
>>             -            db.names.back().first = "alignof (" +
>>             db.names.back().move_full() + ")";
>>             +            db.names.back() =
>>             + db.make<EnclosingExpr>("alignof (", db.names.back(), ")");
>>                          first = t;
>>                      }
>>                  }
>>             @@ -3376,7 +4458,8 @@ parse_alignof_expr(const char*
>>             first, co
>>                      {
>>                          if (db.names.empty())
>>                              return first;
>>             -            db.names.back().first = "alignof (" +
>>             db.names.back().move_full() + ")";
>>             +            db.names.back() =
>>             + db.make<EnclosingExpr>("alignof (", db.names.back(), ")");
>>                          first = t;
>>                      }
>>                  }
>>             @@ -3391,28 +4474,29 @@ parse_noexcept_expression(const
>>             char* fi
>>                  {
>>                      if (db.names.empty())
>>                          return first;
>>             -        db.names.back().first =  "noexcept (" +
>>             db.names.back().move_full() + ")";
>>             +        db.names.back() =
>>             + db.make<EnclosingExpr>("noexcept (", db.names.back(), ")");
>>                      first = t1;
>>                  }
>>                  return first;
>>              }
>>
>>              const char*
>>             -parse_prefix_expression(const char* first, const char*
>>             last, const Db::String& op, Db& db)
>>             +parse_prefix_expression(const char* first, const char*
>>             last, StringView op, Db& db)
>>              {
>>                  const char* t1 = parse_expression(first, last, db);
>>                  if (t1 != first)
>>                  {
>>                      if (db.names.empty())
>>                          return first;
>>             -        db.names.back().first =  op + "(" +
>>             db.names.back().move_full() + ")";
>>             +        db.names.back() = db.make<PrefixExpr>(op,
>>             db.names.back());
>>                      first = t1;
>>                  }
>>                  return first;
>>              }
>>
>>              const char*
>>             -parse_binary_expression(const char* first, const char*
>>             last, const Db::String& op, Db& db)
>>             +parse_binary_expression(const char* first, const char*
>>             last, StringView op, Db& db)
>>              {
>>                  const char* t1 = parse_expression(first, last, db);
>>                  if (t1 != first)
>>             @@ -3422,20 +4506,12 @@ parse_binary_expression(const
>>             char* firs
>>                      {
>>                          if (db.names.size() < 2)
>>                              return first;
>>             -            auto op2 = db.names.back().move_full();
>>             +            auto op2 = db.names.back();
>>                          db.names.pop_back();
>>             -            auto op1 = db.names.back().move_full();
>>             -            auto& nm = db.names.back().first;
>>             -            nm.clear();
>>             -            if (op == ">")
>>             -                nm += '(';
>>             -            nm += "(" + op1 + ") " + op + " (" + op2 + ")";
>>             -            if (op == ">")
>>             -                nm += ')';
>>             +            auto op1 = db.names.back();
>>             +            db.names.back() = db.make<BinaryExpr>(op1,
>>             op, op2);
>>                          first = t2;
>>                      }
>>             -        else if(!db.names.empty())
>>             -            db.names.pop_back();
>>                  }
>>                  return first;
>>              }
>>             @@ -3573,8 +4649,8 @@ parse_expression(const char* first,
>>             cons
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first = (parsed_gs ? Db::String("::") :
>>             Db::String()) +
>>             - "delete[] " + db.names.back().move_full();
>>             +                        db.names.back() =
>>             db.make<DeleteExpr>(
>>             + db.names.back(), parsed_gs, /*is_array=*/true);
>>                                      first = t1;
>>                                  }
>>                              }
>>             @@ -3594,8 +4670,8 @@ parse_expression(const char* first,
>>             cons
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first = (parsed_gs ? Db::String("::") :
>>             Db::String()) +
>>             - "delete " + db.names.back().move_full();
>>             +                        db.names.back() =
>>             db.make<DeleteExpr>(
>>             + db.names.back(), parsed_gs, /*is_array=*/false);
>>                                      first = t1;
>>                                  }
>>                              }
>>             @@ -3666,10 +4742,11 @@ parse_expression(const char*
>>             first, cons
>>                                  {
>>                                      if (db.names.size() < 2)
>>                                          return first;
>>             -                        auto op2 =
>>             db.names.back().move_full();
>>             +                        auto op2 = db.names.back();
>>              db.names.pop_back();
>>             -                        auto op1 =
>>             db.names.back().move_full();
>>             -                        db.names.back() = "(" + op1 +
>>             ")[" + op2 + "]";
>>             +                        auto op1 = db.names.back();
>>             +                        db.names.back() =
>>             + db.make<ArraySubscriptExpr>(op1, op2);
>>                                      first = t2;
>>                                  }
>>                                  else if (!db.names.empty())
>>             @@ -3739,7 +4816,8 @@ parse_expression(const char* first,
>>             cons
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             -                        db.names.back() = "(" +
>>             db.names.back().move_full() + ")--";
>>             +                        db.names.back() =
>>             + db.make<PostfixExpr>(db.names.back(), "--");
>>                                      first = t1;
>>                                  }
>>                              }
>>             @@ -3829,7 +4907,8 @@ parse_expression(const char* first,
>>             cons
>>                                  {
>>                                      if (db.names.empty())
>>                                          return first;
>>             -                        db.names.back() = "(" +
>>             db.names.back().move_full() + ")++";
>>             +                        db.names.back() =
>>             + db.make<PostfixExpr>(db.names.back(), "++");
>>                                      first = t1;
>>                                  }
>>                              }
>>             @@ -3858,12 +4937,13 @@ parse_expression(const char*
>>             first, cons
>>                                      {
>>                                          if (db.names.size() < 3)
>>                                              return first;
>>             -                            auto op3 =
>>             db.names.back().move_full();
>>             +                            auto op3 = db.names.back();
>>              db.names.pop_back();
>>             -                            auto op2 =
>>             db.names.back().move_full();
>>             +                            auto op2 = db.names.back();
>>              db.names.pop_back();
>>             -                            auto op1 =
>>             db.names.back().move_full();
>>             -                            db.names.back() = "(" + op1
>>             + ") ? (" + op2 + ") : (" + op3 + ")";
>>             +                            auto op1 = db.names.back();
>>             +                            db.names.back() =
>>             + db.make<ConditionalExpr>(op1, op2, op3);
>>                                          first = t3;
>>                                      }
>>                                      else
>>             @@ -3948,7 +5028,7 @@ parse_expression(const char* first,
>>             cons
>>                              first = parse_typeid_expr(first, last, db);
>>                              break;
>>                          case 'r':
>>             - db.names.push_back("throw");
>>             + db.names.push_back(db.make<NameType>("throw"));
>>                              first += 2;
>>                              break;
>>                          case 'w':
>>             @@ -4037,7 +5117,7 @@ parse_template_args(const char*
>>             first, c
>>                      if (db.tag_templates)
>>                          db.template_param.back().clear();
>>                      const char* t = first+1;
>>             -        Db::String args("<");
>>             +        size_t begin_idx = db.names.size();
>>                      while (*t != 'E')
>>                      {
>>                          if (db.tag_templates)
>>             @@ -4047,7 +5127,7 @@ parse_template_args(const char*
>>             first, c
>>                          size_t k1 = db.names.size();
>>                          if (db.tag_templates)
>>              db.template_param.pop_back();
>>             -            if (t1 == t || t1 == last)
>>             +            if (t1 == t || t1 == last || k0 > k1)
>>                              return first;
>>                          if (db.tag_templates)
>>                          {
>>             @@ -4055,30 +5135,18 @@ parse_template_args(const char*
>>             first, c
>>                              for (size_t k = k0; k < k1; ++k)
>>              db.template_param.back().back().push_back(db.names[k]);
>>                          }
>>             -            for (size_t k = k0; k < k1; ++k)
>>             -            {
>>             -                if (args.size() > 1)
>>             -                    args += ", ";
>>             -                args += db.names[k].move_full();
>>             -            }
>>             -            for (; k1 > k0; --k1)
>>             -                if (!db.names.empty())
>>             -                    db.names.pop_back();
>>                          t = t1;
>>                      }
>>                      first = t + 1;
>>             -        if (args.back() != '>')
>>             -            args += ">";
>>             -        else
>>             -            args += " >";
>>             -        db.names.push_back(std::move(args));
>>             -
>>             +        TemplateParams* tp = db.make<TemplateParams>(
>>             +            db.popTrailingNodeArray(begin_idx));
>>             +        db.names.push_back(tp);
>>                  }
>>                  return first;
>>              }
>>
>>             -// <nested-name> ::= N [<CV-qualifiers>]
>>             [<ref-qualifier>] <prefix> <unqualified-name> E
>>             -//               ::= N [<CV-qualifiers>]
>>             [<ref-qualifier>] <template-prefix> <template-args> E
>>             +// <nested-name> ::= N [<CV-Qualifiers>]
>>             [<ref-qualifier>] <prefix> <unqualified-name> E
>>             +//               ::= N [<CV-Qualifiers>]
>>             [<ref-qualifier>] <template-prefix> <template-args> E
>>              //
>>              // <prefix> ::= <prefix> <unqualified-name>
>>              //          ::= <template-prefix> <template-args>
>>             @@ -4099,32 +5167,29 @@ parse_nested_name(const char*
>>             first, con
>>              {
>>                  if (first != last && *first == 'N')
>>                  {
>>             -        unsigned cv;
>>             +        Qualifiers cv;
>>                      const char* t0 = parse_cv_qualifiers(first+1,
>>             last, cv);
>>                      if (t0 == last)
>>                          return first;
>>             -        db.ref = 0;
>>             +        db.ref = FrefQualNone;
>>                      if (*t0 == 'R')
>>                      {
>>             -            db.ref = 1;
>>             +            db.ref = FrefQualLValue;
>>                          ++t0;
>>                      }
>>                      else if (*t0 == 'O')
>>                      {
>>             -            db.ref = 2;
>>             +            db.ref = FrefQualRValue;
>>                          ++t0;
>>                      }
>>             -        db.names.emplace_back();
>>             +        db.names.push_back(db.make<EmptyName>());
>>                      if (last - t0 >= 2 && t0[0] == 'S' && t0[1] == 't')
>>                      {
>>                          t0 += 2;
>>             -            db.names.back().first = "std";
>>             +            db.names.back() = db.make<NameType>("std");
>>                      }
>>                      if (t0 == last)
>>             -        {
>>             -            db.names.pop_back();
>>                          return first;
>>             -        }
>>                      bool pop_subs = false;
>>                      bool component_ends_with_template_args = false;
>>                      while (*t0 != 'E')
>>             @@ -4139,17 +5204,18 @@ parse_nested_name(const char*
>>             first, con
>>                              t1 = parse_substitution(t0, last, db);
>>                              if (t1 != t0 && t1 != last)
>>                              {
>>             -                    auto name = db.names.back().move_full();
>>             +                    auto name = db.names.back();
>>                                  db.names.pop_back();
>>             -                    if (db.names.empty())
>>             -                        return first;
>>             -                    if (!db.names.back().first.empty())
>>             +                    if (db.names.back()->K !=
>>             Node::KEmptyName)
>>                                  {
>>             - db.names.back().first += "::" + name;
>>             - db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>             +                        db.names.back() =
>>             db.make<QualifiedName>(
>>             + db.names.back(), name);
>>             +                        db.subs.push_back(
>>             +                            Db::sub_type(1, db.names.back(),
>>             +  db.names.get_allocator()));
>>                                  }
>>                                  else
>>             - db.names.back().first = name;
>>             +                        db.names.back() = name;
>>                                  pop_subs = true;
>>                                  t0 = t1;
>>                              }
>>             @@ -4160,14 +5226,13 @@ parse_nested_name(const char*
>>             first, con
>>                              t1 = parse_template_param(t0, last, db);
>>                              if (t1 != t0 && t1 != last)
>>                              {
>>             -                    auto name = db.names.back().move_full();
>>             +                    auto name = db.names.back();
>>                                  db.names.pop_back();
>>             -                    if (db.names.empty())
>>             -                        return first;
>>             -                    if (!db.names.back().first.empty())
>>             - db.names.back().first += "::" + name;
>>             +                    if (db.names.back()->K !=
>>             Node::KEmptyName)
>>             +                        db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), name);
>>                                  else
>>             - db.names.back().first = name;
>>             +                        db.names.back() = name;
>>              db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>                                  pop_subs = true;
>>                                  t0 = t1;
>>             @@ -4181,14 +5246,13 @@ parse_nested_name(const char*
>>             first, con
>>                              t1 = parse_decltype(t0, last, db);
>>                              if (t1 != t0 && t1 != last)
>>                              {
>>             -                    auto name = db.names.back().move_full();
>>             +                    auto name = db.names.back();
>>                                  db.names.pop_back();
>>             -                    if (db.names.empty())
>>             -                        return first;
>>             -                    if (!db.names.back().first.empty())
>>             - db.names.back().first += "::" + name;
>>             +                    if (db.names.back()->K !=
>>             Node::KEmptyName)
>>             +                        db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), name);
>>                                  else
>>             - db.names.back().first = name;
>>             +                        db.names.back() = name;
>>              db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>                                  pop_subs = true;
>>                                  t0 = t1;
>>             @@ -4200,12 +5264,12 @@ parse_nested_name(const char*
>>             first, con
>>                              t1 = parse_template_args(t0, last, db);
>>                              if (t1 != t0 && t1 != last)
>>                              {
>>             -                    auto name = db.names.back().move_full();
>>             +                    auto name = db.names.back();
>>                                  db.names.pop_back();
>>             -                    if (db.names.empty())
>>             -                        return first;
>>             -                    db.names.back().first += name;
>>             - db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>             +                    db.names.back() =
>>             db.make<NameWithTemplateArgs>(
>>             +                        db.names.back(), name);
>>             + db.subs.push_back(Db::sub_type(
>>             +                        1, db.names.back(),
>>             db.names.get_allocator()));
>>                                  t0 = t1;
>>              component_ends_with_template_args = true;
>>                              }
>>             @@ -4221,14 +5285,13 @@ parse_nested_name(const char*
>>             first, con
>>                              t1 = parse_unqualified_name(t0, last, db);
>>                              if (t1 != t0 && t1 != last)
>>                              {
>>             -                    auto name = db.names.back().move_full();
>>             +                    auto name = db.names.back();
>>                                  db.names.pop_back();
>>             -                    if (db.names.empty())
>>             -                        return first;
>>             -                    if (!db.names.back().first.empty())
>>             - db.names.back().first += "::" + name;
>>             +                    if (db.names.back()->K !=
>>             Node::KEmptyName)
>>             +                        db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), name);
>>                                  else
>>             - db.names.back().first = name;
>>             +                        db.names.back() = name;
>>              db.subs.push_back(Db::sub_type(1, db.names.back(),
>>             db.names.get_allocator()));
>>                                  pop_subs = true;
>>                                  t0 = t1;
>>             @@ -4304,7 +5367,8 @@ parse_local_name(const char* first,
>>             cons
>>                              first = parse_discriminator(t+1, last);
>>                              if (db.names.empty())
>>                                  return first;
>>             - db.names.back().first.append("::string literal");
>>             +                db.names.back() = db.make<QualifiedName>(
>>             +                    db.names.back(),
>>             db.make<NameType>("string literal"));
>>                              break;
>>                          case 'd':
>>                              if (++t != last)
>>             @@ -4319,12 +5383,12 @@ parse_local_name(const char*
>>             first, cons
>>                                      {
>>                                          if (db.names.size() < 2)
>>                                              return first;
>>             -                            auto name =
>>             db.names.back().move_full();
>>             +                            auto name = db.names.back();
>>              db.names.pop_back();
>>                                          if (db.names.empty())
>>                                              return first;
>>             - db.names.back().first.append("::");
>>             - db.names.back().first.append(name);
>>             +                            db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), name);
>>                                          first = t1;
>>                                      }
>>                                      else if (!db.names.empty())
>>             @@ -4342,12 +5406,12 @@ parse_local_name(const char*
>>             first, cons
>>                                      first = parse_discriminator(t1,
>>             last);
>>                                      if (db.names.size() < 2)
>>                                          return first;
>>             -                        auto name =
>>             db.names.back().move_full();
>>             +                        auto name = db.names.back();
>>              db.names.pop_back();
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first.append("::");
>>             - db.names.back().first.append(name);
>>             +                        db.names.back() =
>>             + db.make<QualifiedName>(db.name
>>             <http://db.name>s.back(), name);
>>                                  }
>>                                  else if (!db.names.empty())
>>              db.names.pop_back();
>>             @@ -4411,11 +5475,13 @@ parse_name(const char* first,
>>             const char
>>                                  {
>>                                      if (db.names.size() < 2)
>>                                          return first;
>>             -                        auto tmp =
>>             db.names.back().move_full();
>>             +                        auto tmp = db.names.back();
>>              db.names.pop_back();
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first += tmp;
>>             +                        db.names.back() =
>>             + db.make<NameWithTemplateArgs>(
>>             + db.names.back(), tmp);
>>                                      first = t1;
>>                                      if (ends_with_template_args)
>>              *ends_with_template_args = true;
>>             @@ -4435,11 +5501,13 @@ parse_name(const char* first,
>>             const char
>>                                  {
>>                                      if (db.names.size() < 2)
>>                                          return first;
>>             -                        auto tmp =
>>             db.names.back().move_full();
>>             +                        auto tmp = db.names.back();
>>              db.names.pop_back();
>>                                      if (db.names.empty())
>>                                          return first;
>>             - db.names.back().first += tmp;
>>             +                        db.names.back() =
>>             + db.make<NameWithTemplateArgs>(
>>             + db.names.back(), tmp);
>>                                      first = t1;
>>                                      if (ends_with_template_args)
>>              *ends_with_template_args = true;
>>             @@ -4527,7 +5595,8 @@ parse_special_name(const char*
>>             first, co
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             - db.names.back().first.insert(0, "vtable for ");
>>             +                    db.names.back() =
>>             + db.make<SpecialName>("vtable for ", db.names.back());
>>                                  first = t;
>>                              }
>>                              break;
>>             @@ -4538,7 +5607,8 @@ parse_special_name(const char*
>>             first, co
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             - db.names.back().first.insert(0, "VTT for ");
>>             +                    db.names.back() =
>>             + db.make<SpecialName>("VTT for ", db.names.back());
>>                                  first = t;
>>                              }
>>                              break;
>>             @@ -4549,7 +5619,8 @@ parse_special_name(const char*
>>             first, co
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             - db.names.back().first.insert(0, "typeinfo for ");
>>             +                    db.names.back() =
>>             + db.make<SpecialName>("typeinfo for ", db.names.back());
>>                                  first = t;
>>                              }
>>                              break;
>>             @@ -4560,7 +5631,8 @@ parse_special_name(const char*
>>             first, co
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             - db.names.back().first.insert(0, "typeinfo name for ");
>>             +                    db.names.back() =
>>             + db.make<SpecialName>("typeinfo name for ",
>>             db.names.back());
>>                                  first = t;
>>                              }
>>                              break;
>>             @@ -4578,7 +5650,9 @@ parse_special_name(const char*
>>             first, co
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             - db.names.back().first.insert(0, "covariant return thunk
>>             to ");
>>             +                    db.names.back() =
>>             + db.make<SpecialName>("covariant return thunk to ",
>>             +   db.names.back());
>>                                  first = t;
>>                              }
>>                            }
>>             @@ -4596,13 +5670,12 @@ parse_special_name(const char*
>>             first, co
>>                                      {
>>                                          if (db.names.size() < 2)
>>                                              return first;
>>             -                            auto left =
>>             db.names.back().move_full();
>>             +                            auto left = db.names.back();
>>              db.names.pop_back();
>>                                          if (db.names.empty())
>>                                              return first;
>>             - db.names.back().first = "construction vtable for " +
>>             -         std::move(left) + "-in-" +
>>             -         db.names.back().move_full();
>>             +                            db.names.back() =
>>             db.make<CtorVtableSpecialName>(
>>             +                                left, db.names.back());
>>                                          first = t1;
>>                                      }
>>                                  }
>>             @@ -4614,8 +5687,10 @@ parse_special_name(const char*
>>             first, co
>>                              if (t != first + 2)
>>                              {
>>                                  if (db.names.empty())
>>             -                    return first;
>>             - db.names.back().first.insert(0, "thread-local wrapper
>>             routine for ");
>>             +                        return first;
>>             +                    db.names.back() =
>>             + db.make<SpecialName>("thread-local wrapper routine for ",
>>             +   db.names.back());
>>                                  first = t;
>>                              }
>>                              break;
>>             @@ -4625,8 +5700,9 @@ parse_special_name(const char*
>>             first, co
>>                              if (t != first + 2)
>>                              {
>>                                  if (db.names.empty())
>>             -                    return first;
>>             - db.names.back().first.insert(0, "thread-local
>>             initialization routine for ");
>>             +                        return first;
>>             +                    db.names.back() = db.make<SpecialName>(
>>             +                        "thread-local initialization
>>             routine for ", db.names.back());
>>                                  first = t;
>>                              }
>>                              break;
>>             @@ -4643,12 +5719,16 @@ parse_special_name(const char*
>>             first, co
>>                                      return first;
>>                                  if (first[1] == 'v')
>>                                  {
>>             - db.names.back().first.insert(0, "virtual thunk to ");
>>             +                        db.names.back() =
>>             + db.make<SpecialName>("virtual thunk to ",
>>             +       db.names.back());
>>                                      first = t;
>>                                  }
>>                                  else
>>                                  {
>>             - db.names.back().first.insert(0, "non-virtual thunk to ");
>>             +                        db.names.back() =
>>             + db.make<SpecialName>("non-virtual thunk to ",
>>             +       db.names.back());
>>                                      first = t;
>>                                  }
>>                              }
>>             @@ -4666,7 +5746,8 @@ parse_special_name(const char*
>>             first, co
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             - db.names.back().first.insert(0, "guard variable for ");
>>             +                    db.names.back() =
>>             + db.make<SpecialName>("guard variable for ",
>>             db.names.back());
>>                                  first = t;
>>                              }
>>                              break;
>>             @@ -4677,7 +5758,9 @@ parse_special_name(const char*
>>             first, co
>>                              {
>>                                  if (db.names.empty())
>>                                      return first;
>>             - db.names.back().first.insert(0, "reference temporary
>>             for ");
>>             +                    db.names.back() =
>>             + db.make<SpecialName>("reference temporary for ",
>>             +   db.names.back());
>>                                  first = t;
>>                              }
>>                              break;
>>             @@ -4735,8 +5818,10 @@ parse_encoding(const char* first,
>>             const
>>                          bool ends_with_template_args = false;
>>                          const char* t = parse_name(first, last, db,
>>             &ends_with_template_args);
>>             -            unsigned cv = db.cv <http://db.cv>;
>>             -            unsigned ref = db.ref;
>>             +            if (db.names.empty())
>>             +                return first;
>>             +            Qualifiers cv = db.cv <http://db.cv>;
>>             +            FunctionRefQual ref = db.ref;
>>                          if (t != first)
>>                          {
>>                              if (t != last && *t != 'E' && *t != '.')
>>             @@ -4744,87 +5829,59 @@ parse_encoding(const char* first,
>>             const
>>                                  save_value<bool> sb2(db.tag_templates);
>>                                  db.tag_templates = false;
>>                                  const char* t2;
>>             -                    Db::String ret2;
>>                                  if (db.names.empty())
>>                                      return first;
>>             -                    const Db::String& nm =
>>             db.names.back().first;
>>             -                    if (nm.empty())
>>             +                    if (!db.names.back())
>>                                      return first;
>>             +                    Node* return_type = nullptr;
>>                                  if (!db.parsed_ctor_dtor_cv &&
>>             ends_with_template_args)
>>                                  {
>>                                      t2 = parse_type(t, last, db);
>>                                      if (t2 == t)
>>                                          return first;
>>             -                        if (db.names.size() < 2)
>>             +                        if (db.names.size() < 1)
>>                                          return first;
>>             -                        auto ret1 =
>>             std::move(db.names.back().first);
>>             -                        ret2 =
>>             std::move(db.names.back().second);
>>             -                        if (ret2.empty())
>>             -                            ret1 += ' ';
>>             +                        return_type = db.names.back();
>>              db.names.pop_back();
>>             -                        if (db.names.empty())
>>             -                            return first;
>>             -
>>             - db.names.back().first.insert(0, ret1);
>>                                      t = t2;
>>                                  }
>>             -                    db.names.back().first += '(';
>>             +
>>             +                    Node* result = nullptr;
>>             +
>>                                  if (t != last && *t == 'v')
>>                                  {
>>                                      ++t;
>>             +                        Node* name = db.names.back();
>>             + db.names.pop_back();
>>             +                        result =
>>             db.make<TopLevelFunctionDecl>(
>>             +                            return_type, name, NodeArray());
>>                                  }
>>                                  else
>>                                  {
>>             -                        bool first_arg = true;
>>             +                        size_t params_begin =
>>             db.names.size();
>>                                      while (true)
>>                                      {
>>             -                            size_t k0 = db.names.size();
>>                                          t2 = parse_type(t, last, db);
>>             -                            size_t k1 = db.names.size();
>>                                          if (t2 == t)
>>                                              break;
>>             -                            if (k1 > k0)
>>             -                            {
>>             -                                Db::String tmp;
>>             -                                for (size_t k = k0; k <
>>             k1; ++k)
>>             -                                {
>>             -                                    if (!tmp.empty())
>>             -                                        tmp += ", ";
>>             -                                    tmp +=
>>             db.names[k].move_full();
>>             -                                }
>>             -                                for (size_t k = k0; k <
>>             k1; ++k) {
>>             -                                    if (db.names.empty())
>>             - return first;
>>             - db.names.pop_back();
>>             -                                }
>>             -                                if (!tmp.empty())
>>             -                                {
>>             -                                    if (db.names.empty())
>>             - return first;
>>             -                                    if (!first_arg)
>>             - db.names.back().first += ", ";
>>             -                                    else
>>             - first_arg = false;
>>             - db.names.back().first += tmp;
>>             -                                }
>>             -                            }
>>                                          t = t2;
>>                                      }
>>             +                        if (db.names.size() < params_begin)
>>             +                            return first;
>>             +                        NodeArray params =
>>             + db.popTrailingNodeArray(params_begin);
>>             +                        if (db.names.empty())
>>             +                            return first;
>>             +                        Node* name = db.names.back();
>>             + db.names.pop_back();
>>             +                        result =
>>             db.make<TopLevelFunctionDecl>(
>>             +                            return_type, name, params);
>>                                  }
>>             -                    if (db.names.empty())
>>             -                        return first;
>>             -                    db.names.back().first += ')';
>>             -                    if (cv & 1)
>>             - db.names.back().first.append(" const");
>>             -                    if (cv & 2)
>>             - db.names.back().first.append(" volatile");
>>             -                    if (cv & 4)
>>             - db.names.back().first.append(" restrict");
>>             -                    if (ref == 1)
>>             - db.names.back().first.append(" &");
>>             -                    else if (ref == 2)
>>             - db.names.back().first.append(" &&");
>>             -                    db.names.back().first += ret2;
>>             +                    if (ref != FrefQualNone)
>>             +                        result =
>>             db.make<FunctionRefQualType>(result, ref);
>>             +                    if (cv != QualNone)
>>             +                        result =
>>             db.make<FunctionQualType>(result, cv);
>>             + db.names.push_back(result);
>>                                  first = t;
>>                              }
>>                              else
>>             @@ -4846,6 +5903,7 @@ parse_block_invoke(const char*
>>             first, co
>>              {
>>                  if (last - first >= 13)
>>                  {
>>             +        // FIXME: strcmp?
>>                      const char test[] = "_block_invoke";
>>                      const char* t = first;
>>                      for (int i = 0; i < 13; ++i, ++t)
>>             @@ -4868,7 +5926,9 @@ parse_block_invoke(const char*
>>             first, co
>>                      }
>>                      if (db.names.empty())
>>                          return first;
>>             -        db.names.back().first.insert(0, "invocation
>>             function for block in ");
>>             +        db.names.back() =
>>             + db.make<SpecialName>("invocation function for block in ",
>>             + db.names.back());
>>                      first = t;
>>                  }
>>                  return first;
>>             @@ -4884,7 +5944,8 @@ parse_dot_suffix(const char* first,
>>             cons
>>                  {
>>                      if (db.names.empty())
>>                          return first;
>>             -        db.names.back().first += " (" +
>>             Db::String(first, last) + ")";
>>             +        db.names.back() =
>>             +            db.make<DotSuffix>(db.names.ba
>>             <http://db.names.ba>ck(), StringView(first, last));
>>                      first = last;
>>                  }
>>                  return first;
>>             @@ -4963,6 +6024,7 @@ __cxa_demangle(const char
>>             *mangled_name,
>>                  size_t len = std::strlen(mangled_name);
>>                  demangle(mangled_name, mangled_name + len, db,
>>                           internal_status);
>>             +
>>                  if (internal_status == success &&
>>             db.fix_forward_references &&
>>                         !db.template_param.empty() &&
>>             !db.template_param.front().empty())
>>                  {
>>             @@ -4974,30 +6036,25 @@ __cxa_demangle(const char
>>             *mangled_name,
>>                      if (db.fix_forward_references)
>>                          internal_status = invalid_mangled_name;
>>                  }
>>             +
>>                  if (internal_status == success)
>>                  {
>>             -        size_t sz = db.names.back().size() + 1;
>>             -        if (sz > internal_size)
>>             +        if (!buf)
>>                      {
>>             -            char* newbuf =
>>             static_cast<char*>(std::realloc(buf, sz));
>>             -            if (newbuf == nullptr)
>>             -            {
>>             -                internal_status = memory_alloc_failure;
>>             -                buf = nullptr;
>>             -            }
>>             -            else
>>             -            {
>>             -                buf = newbuf;
>>             -                if (n != nullptr)
>>             -                    *n = sz;
>>             -            }
>>             +            internal_size = 1024;
>>             +            buf =
>>             static_cast<char*>(std::malloc(internal_size));
>>                      }
>>             -        if (buf != nullptr)
>>             +
>>             +        if (buf)
>>                      {
>>             -            db.names.back().first += db.names.back().second;
>>             -            std::memcpy(buf,
>>             db.names.back().first.data(), sz-1);
>>             -            buf[sz-1] = char(0);
>>             +            OutputStream s(buf, internal_size);
>>             +            db.names.back()->print(s);
>>             +            s += '\0';
>>             +            if (n) *n = s.getCurrentPosition();
>>             +            buf = s.getBuffer();
>>                      }
>>             +        else
>>             +            internal_status = memory_alloc_failure;
>>                  }
>>                  else
>>                      buf = nullptr;
>>
>>             Modified: libcxxabi/trunk/test/test_demangle.pass.cpp
>>             URL:
>>             http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/test_demangle.pass.cpp?rev=309340&r1=309339&r2=309340&view=diff
>>             <http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/test_demangle.pass.cpp?rev=309340&r1=309339&r2=309340&view=diff>
>>             ==============================================================================
>>             --- libcxxabi/trunk/test/test_demangle.pass.cpp (original)
>>             +++ libcxxabi/trunk/test/test_demangle.pass.cpp Thu Jul
>>             27 17:43:49 2017
>>             @@ -29600,8 +29600,7 @@ const char* cases[][2] =
>>                  {"i", "int"},
>>
>>                  {"PKFvRiE", "void (*)(int&) const"},
>>             -    // FIXME(compnerd) pretty print this as void
>>             (*)(unsigned long &) volatile &&
>>             -    {"PVFvRmOE", "void (*)(unsigned long&)  volatile&&"},
>>             +    {"PVFvRmOE", "void (*)(unsigned long&) volatile &&"},
>>                  {"PFvRmOE", "void (*)(unsigned long&) &&"},
>>                  {"_ZTW1x", "thread-local wrapper routine for x"},
>>                  {"_ZTHN3fooE", "thread-local initialization routine
>>             for foo"},
>>
>>
>>             _______________________________________________
>>             cfe-commits mailing list
>>             cfe-commits at lists.llvm.org
>>             <mailto:cfe-commits at lists.llvm.org>
>>             http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>             <http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits>
>>
>>
>
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20170806/cd2becb5/attachment-0001.html>


More information about the cfe-commits mailing list