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