[libcxxabi] r324111 - [demangler] Clean up the expression parser

Erik Pilkington via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 2 10:04:32 PST 2018


Author: epilk
Date: Fri Feb  2 10:04:32 2018
New Revision: 324111

URL: http://llvm.org/viewvc/llvm-project?rev=324111&view=rev
Log:
[demangler] Clean up the expression parser

This commit cleans up the expression parser, using a new style:
  - parse* functions now return Node pointers.
  - The mangled name is now held in Db and accessed with look() and consume()
  - LLVM coding style

This style is meant to avoid the 2 most common types of bugs in the
old demanger, namely misusing the Names stack (ie, calling back() on
empty) and going out of bounds on the mangled name. I also think it
makes the demangler a lot cleaner.

Differential revision: https://reviews.llvm.org/D41887

Modified:
    libcxxabi/trunk/src/cxa_demangle.cpp
    libcxxabi/trunk/test/test_demangle.pass.cpp

Modified: libcxxabi/trunk/src/cxa_demangle.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp?rev=324111&r1=324110&r2=324111&view=diff
==============================================================================
--- libcxxabi/trunk/src/cxa_demangle.cpp (original)
+++ libcxxabi/trunk/src/cxa_demangle.cpp Fri Feb  2 10:04:32 2018
@@ -1522,6 +1522,7 @@ public:
     S += "new";
     if (IsArray)
       S += "[]";
+    S += ' ';
     if (!ExprList.empty()) {
       S += "(";
       ExprList.printWithComma(S);
@@ -1879,67 +1880,839 @@ public:
   }
 };
 
-struct Db
-{
-    // Name stack, this is used by the parser to hold temporary names that were
-    // parsed. The parser colapses multiple names into new nodes to construct
-    // the AST. Once the parser is finished, names.size() == 1.
-    PODSmallVector<Node*, 32> Names;
+struct Db {
+  const char *First;
+  const char *Last;
+
+  // Name stack, this is used by the parser to hold temporary names that were
+  // parsed. The parser colapses multiple names into new nodes to construct
+  // the AST. Once the parser is finished, names.size() == 1.
+  PODSmallVector<Node *, 32> Names;
+
+  // Substitution table. Itanium supports name substitutions as a means of
+  // compression. The string "S42_" refers to the 44nd entry (base-36) in this
+  // table.
+  PODSmallVector<Node *, 32> Subs;
+
+  // Template parameter table. Like the above, but referenced like "T42_".
+  // This has a smaller size compared to Subs and Names because it can be
+  // stored on the stack.
+  PODSmallVector<Node *, 8> TemplateParams;
+
+  Qualifiers CV = QualNone;
+  FunctionRefQual RefQuals = FrefQualNone;
+  unsigned EncodingDepth = 0;
+  bool ParsedCtorDtorCV = false;
+  bool TagTemplates = true;
+  bool FixForwardReferences = false;
+  bool TryToParseTemplateArgs = true;
+
+  BumpPointerAllocator ASTAllocator;
+
+  template <class T, class... Args> T *make(Args &&... args) {
+    return new (ASTAllocator.allocate(sizeof(T)))
+        T(std::forward<Args>(args)...);
+  }
+
+  template <class It> NodeArray makeNodeArray(It begin, It end) {
+    size_t sz = static_cast<size_t>(end - begin);
+    void *mem = ASTAllocator.allocate(sizeof(Node *) * sz);
+    Node **data = new (mem) Node *[sz];
+    std::copy(begin, end, data);
+    return NodeArray(data, sz);
+  }
+
+  NodeArray popTrailingNodeArray(size_t FromPosition) {
+    assert(FromPosition <= Names.size());
+    NodeArray res =
+        makeNodeArray(Names.begin() + (long)FromPosition, Names.end());
+    Names.dropBack(FromPosition);
+    return res;
+  }
+
+  bool consumeIf(StringView S) {
+    if (StringView(First, Last).startsWith(S)) {
+      First += S.size();
+      return true;
+    }
+    return false;
+  }
+
+  bool consumeIf(char C) {
+    if (First != Last && *First == C) {
+      ++First;
+      return true;
+    }
+    return false;
+  }
+
+  char consume() { return First != Last ? *First++ : '\0'; }
+
+  char look(unsigned Lookahead = 0) {
+    if (static_cast<size_t>(Last - First) <= Lookahead)
+      return '\0';
+    return First[Lookahead];
+  }
+
+  size_t numLeft() const { return static_cast<size_t>(Last - First); }
+
+  StringView parseNumber(bool AllowNegative = false);
+  Qualifiers parseCVQualifiers();
+
+  /// Parse the <expr> production.
+  Node *parseExpr();
+  Node *parsePrefixExpr(StringView Kind);
+  Node *parseBinaryExpr(StringView Kind);
+  Node *parseIntegerLiteral(StringView Lit);
+  Node *parseExprPrimary();
+  template <class Float> Node *parseFloatingLiteral();
+  Node *parseFunctionParam();
+  Node *parseNewExpr();
+  Node *parseConversionExpr();
+
+  // FIXME: remove this when all the parse_* functions have been rewritten.
+  template <const char *(*parse_fn)(const char *, const char *, Db &)>
+  Node *legacyParse() {
+    size_t BeforeType = Names.size();
+    const char *OrigFirst = First;
+    const char *T = parse_fn(First, Last, *this);
+    if (T == OrigFirst || BeforeType + 1 != Names.size())
+      return nullptr;
+    First = T;
+    Node *R = Names.back();
+    Names.pop_back();
+    return R;
+  }
+};
 
-    // Substitution table. Itanium supports name substitutions as a means of
-    // compression. The string "S42_" refers to the 44nd entry (base-36) in this
-    // table.
-    PODSmallVector<Node*, 32> Subs;
+const char *parse_expression(const char *first, const char *last, Db &db) {
+  db.First = first;
+  db.Last = last;
+  Node *R = db.parseExpr();
+  if (R == nullptr)
+    return first;
+  db.Names.push_back(R);
+  return db.First;
+}
+
+const char *parse_expr_primary(const char *first, const char *last, Db &db) {
+  db.First = first;
+  db.Last = last;
+  Node *R = db.parseExprPrimary();
+  if (R == nullptr)
+    return first;
+  db.Names.push_back(R);
+  return db.First;
+}
+
+const char *parse_type(const char *first, const char *last, Db &db);
+const char *parse_encoding(const char *first, const char *last, Db &db);
+const char *parse_name(const char *first, const char *last, Db &db,
+                       bool *ends_with_template_args = 0);
+const char *parse_template_args(const char *first, const char *last, Db &db);
+const char *parse_template_param(const char *, const char *, Db &);
+const char *parse_operator_name(const char *first, const char *last, Db &db);
+const char *parse_unqualified_name(const char *first, const char *last, Db &db);
+const char *parse_decltype(const char *first, const char *last, Db &db);
+const char *parse_unresolved_name(const char *, const char *, Db &);
 
-    // Template parameter table. Like the above, but referenced like "T42_".
-    // This has a smaller size compared to Subs and Names because it can be
-    // stored on the stack.
-    PODSmallVector<Node *, 8> TemplateParams;
+// <number> ::= [n] <non-negative decimal integer>
+StringView Db::parseNumber(bool AllowNegative) {
+  const char *Tmp = First;
+  if (AllowNegative)
+    consumeIf('n');
+  if (numLeft() == 0 || !std::isdigit(*First))
+    return StringView();
+  while (numLeft() != 0 && std::isdigit(*First))
+    ++First;
+  return StringView(Tmp, First);
+}
+
+Node *Db::parsePrefixExpr(StringView Kind) {
+  Node *E = parseExpr();
+  if (E == nullptr)
+    return nullptr;
+  return make<PrefixExpr>(Kind, E);
+}
+
+Node *Db::parseBinaryExpr(StringView Kind) {
+  Node *LHS = parseExpr();
+  if (LHS == nullptr)
+    return nullptr;
+  Node *RHS = parseExpr();
+  if (RHS == nullptr)
+    return nullptr;
+  return make<BinaryExpr>(LHS, Kind, RHS);
+}
+
+Node *Db::parseIntegerLiteral(StringView Lit) {
+  StringView Tmp = parseNumber(true);
+  if (!Tmp.empty() && consumeIf('E'))
+    return make<IntegerExpr>(Lit, Tmp);
+  return nullptr;
+}
+
+Qualifiers Db::parseCVQualifiers() {
+  Qualifiers CVR = QualNone;
+  if (consumeIf('r'))
+    addQualifiers(CVR, QualRestrict);
+  if (consumeIf('V'))
+    addQualifiers(CVR, QualVolatile);
+  if (consumeIf('K'))
+    addQualifiers(CVR, QualConst);
+  return CVR;
+}
 
-    Qualifiers CV = QualNone;
-    FunctionRefQual RefQuals = FrefQualNone;
-    unsigned EncodingDepth = 0;
-    bool ParsedCtorDtorCV = false;
-    bool TagTemplates = true;
-    bool FixForwardReferences = false;
-    bool TryToParseTemplateArgs = true;
+// <function-param> ::= fp <top-level CV-Qualifiers> _                                     # L == 0, first parameter
+//                  ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _   # L == 0, second and later parameters
+//                  ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _         # L > 0, first parameter
+//                  ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _   # L > 0, second and later parameters
+Node *Db::parseFunctionParam() {
+  if (consumeIf("fp")) {
+    parseCVQualifiers();
+    StringView Num = parseNumber();
+    if (!consumeIf('_'))
+      return nullptr;
+    return make<FunctionParam>(Num);
+  }
+  if (consumeIf("fL")) {
+    if (parseNumber().empty())
+      return nullptr;
+    if (!consumeIf('p'))
+      return nullptr;
+    parseCVQualifiers();
+    StringView Num = parseNumber();
+    if (!consumeIf('_'))
+      return nullptr;
+    return make<FunctionParam>(Num);
+  }
+  return nullptr;
+}
 
-    BumpPointerAllocator ASTAllocator;
+// [gs] nw <expression>* _ <type> E                     # new (expr-list) type
+// [gs] nw <expression>* _ <type> <initializer>         # new (expr-list) type (init)
+// [gs] na <expression>* _ <type> E                     # new[] (expr-list) type
+// [gs] na <expression>* _ <type> <initializer>         # new[] (expr-list) type (init)
+// <initializer> ::= pi <expression>* E                 # parenthesized initialization
+Node *Db::parseNewExpr() {
+  bool Global = consumeIf("gs");
+  bool IsArray = look(1) == 'a';
+  if (!consumeIf("nw") && !consumeIf("na"))
+    return nullptr;
+  size_t Exprs = Names.size();
+  while (!consumeIf('_')) {
+    Node *Ex = parseExpr();
+    if (Ex == nullptr)
+      return nullptr;
+    Names.push_back(Ex);
+  }
+  NodeArray ExprList = popTrailingNodeArray(Exprs);
+  Node *Ty = legacyParse<parse_type>();
+  if (Ty == nullptr)
+    return Ty;
+  if (consumeIf("pi")) {
+    size_t InitsBegin = Names.size();
+    while (!consumeIf('E')) {
+      Node *Init = parseExpr();
+      if (Init == nullptr)
+        return Init;
+      Names.push_back(Init);
+    }
+    NodeArray Inits = popTrailingNodeArray(InitsBegin);
+    return make<NewExpr>(ExprList, Ty, Inits, Global, IsArray);
+  } else if (!consumeIf('E'))
+    return nullptr;
+  return make<NewExpr>(ExprList, Ty, NodeArray(), Global, IsArray);
+}
 
-    template <class T, class... Args> T* make(Args&& ...args)
-    {
-        return new (ASTAllocator.allocate(sizeof(T)))
-            T(std::forward<Args>(args)...);
-    }
+// cv <type> <expression>                               # conversion with one argument
+// cv <type> _ <expression>* E                          # conversion with a different number of arguments
+Node *Db::parseConversionExpr() {
+  if (!consumeIf("cv"))
+    return nullptr;
+  Node *Ty;
+  {
+    SwapAndRestore<bool> SaveTemp(TryToParseTemplateArgs, false);
+    Ty = legacyParse<parse_type>();
+  }
+
+  if (Ty == nullptr)
+    return nullptr;
+
+  if (consumeIf('_')) {
+    size_t ExprsBegin = Names.size();
+    while (!consumeIf('E')) {
+      Node *E = parseExpr();
+      if (E == nullptr)
+        return E;
+      Names.push_back(E);
+    }
+    NodeArray Exprs = popTrailingNodeArray(ExprsBegin);
+    return make<ConversionExpr>(Ty, Exprs);
+  }
+
+  Node *E[1] = {parseExpr()};
+  if (E[0] == nullptr)
+    return nullptr;
+  return make<ConversionExpr>(Ty, makeNodeArray(E, E + 1));
+}
 
-    template <class It> NodeArray makeNodeArray(It begin, It end)
-    {
-        size_t sz = static_cast<size_t>(end - begin);
-        void* mem = ASTAllocator.allocate(sizeof(Node*) * sz);
-        Node** data = new (mem) Node*[sz];
-        std::copy(begin, end, data);
-        return NodeArray(data, sz);
-    }
+// <expr-primary> ::= L <type> <value number> E                          # integer literal
+//                ::= L <type> <value float> E                           # floating literal
+//                ::= L <string type> E                                  # string literal
+//                ::= L <nullptr type> E                                 # nullptr literal (i.e., "LDnE")
+// FIXME:         ::= L <type> <real-part float> _ <imag-part float> E   # complex floating point literal (C 2000)
+//                ::= L <mangled-name> E                                 # external name
+Node *Db::parseExprPrimary() {
+  if (!consumeIf('L'))
+    return nullptr;
+  switch (look()) {
+  case 'w':
+    ++First;
+    return parseIntegerLiteral("wchar_t");
+  case 'b':
+    if (consumeIf("b0E"))
+      return make<BoolExpr>(0);
+    if (consumeIf("b1E"))
+      return make<BoolExpr>(1);
+    return nullptr;
+  case 'c':
+    ++First;
+    return parseIntegerLiteral("char");
+  case 'a':
+    ++First;
+    return parseIntegerLiteral("signed char");
+  case 'h':
+    ++First;
+    return parseIntegerLiteral("unsigned char");
+  case 's':
+    ++First;
+    return parseIntegerLiteral("short");
+  case 't':
+    ++First;
+    return parseIntegerLiteral("unsigned short");
+  case 'i':
+    ++First;
+    return parseIntegerLiteral("");
+  case 'j':
+    ++First;
+    return parseIntegerLiteral("u");
+  case 'l':
+    ++First;
+    return parseIntegerLiteral("l");
+  case 'm':
+    ++First;
+    return parseIntegerLiteral("ul");
+  case 'x':
+    ++First;
+    return parseIntegerLiteral("ll");
+  case 'y':
+    ++First;
+    return parseIntegerLiteral("ull");
+  case 'n':
+    ++First;
+    return parseIntegerLiteral("__int128");
+  case 'o':
+    ++First;
+    return parseIntegerLiteral("unsigned __int128");
+  case 'f':
+    ++First;
+    return parseFloatingLiteral<float>();
+  case 'd':
+    ++First;
+    return parseFloatingLiteral<double>();
+  case 'e':
+    ++First;
+    return parseFloatingLiteral<long double>();
+  case '_':
+    if (consumeIf("_Z")) {
+      Node *R = legacyParse<parse_encoding>();
+      if (R != nullptr && consumeIf('E'))
+        return R;
+    }
+    return nullptr;
+  case 'T':
+    // Invalid mangled name per
+    //   http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html
+    return nullptr;
+  default: {
+    // might be named type
+    Node *T = legacyParse<parse_type>();
+    if (T == nullptr)
+      return nullptr;
+    StringView N = parseNumber();
+    if (!N.empty()) {
+      if (!consumeIf('E'))
+        return nullptr;
+      return make<IntegerCastExpr>(T, N);
+    }
+    if (consumeIf('E'))
+      return T;
+    return nullptr;
+  }
+  }
+}
 
-    NodeArray popTrailingNodeArray(size_t FromPosition)
-    {
-        assert(FromPosition <= Names.size());
-        NodeArray res = makeNodeArray(
-            Names.begin() + (long)FromPosition, Names.end());
-        Names.dropBack(FromPosition);
-        return res;
+// <expression> ::= <unary operator-name> <expression>
+//              ::= <binary operator-name> <expression> <expression>
+//              ::= <ternary operator-name> <expression> <expression> <expression>
+//              ::= cl <expression>+ E                                   # call
+//              ::= cv <type> <expression>                               # conversion with one argument
+//              ::= cv <type> _ <expression>* E                          # conversion with a different number of arguments
+//              ::= [gs] nw <expression>* _ <type> E                     # new (expr-list) type
+//              ::= [gs] nw <expression>* _ <type> <initializer>         # new (expr-list) type (init)
+//              ::= [gs] na <expression>* _ <type> E                     # new[] (expr-list) type
+//              ::= [gs] na <expression>* _ <type> <initializer>         # new[] (expr-list) type (init)
+//              ::= [gs] dl <expression>                                 # delete expression
+//              ::= [gs] da <expression>                                 # delete[] expression
+//              ::= pp_ <expression>                                     # prefix ++
+//              ::= mm_ <expression>                                     # prefix --
+//              ::= ti <type>                                            # typeid (type)
+//              ::= te <expression>                                      # typeid (expression)
+//              ::= dc <type> <expression>                               # dynamic_cast<type> (expression)
+//              ::= sc <type> <expression>                               # static_cast<type> (expression)
+//              ::= cc <type> <expression>                               # const_cast<type> (expression)
+//              ::= rc <type> <expression>                               # reinterpret_cast<type> (expression)
+//              ::= st <type>                                            # sizeof (a type)
+//              ::= sz <expression>                                      # sizeof (an expression)
+//              ::= at <type>                                            # alignof (a type)
+//              ::= az <expression>                                      # alignof (an expression)
+//              ::= nx <expression>                                      # noexcept (expression)
+//              ::= <template-param>
+//              ::= <function-param>
+//              ::= dt <expression> <unresolved-name>                    # expr.name
+//              ::= pt <expression> <unresolved-name>                    # expr->name
+//              ::= ds <expression> <expression>                         # expr.*expr
+//              ::= sZ <template-param>                                  # size of a parameter pack
+//              ::= sZ <function-param>                                  # size of a function parameter pack
+//              ::= sp <expression>                                      # pack expansion
+//              ::= tw <expression>                                      # throw expression
+//              ::= tr                                                   # throw with no operand (rethrow)
+//              ::= <unresolved-name>                                    # f(p), N::f(p), ::f(p),
+//                                                                       # freestanding dependent name (e.g., T::x),
+//                                                                       # objectless nonstatic member reference
+//              ::= fL <binary-operator-name> <expression> <expression>
+//              ::= fR <binary-operator-name> <expression> <expression>
+//              ::= fl <binary-operator-name> <expression>
+//              ::= fr <binary-operator-name> <expression>
+//              ::= <expr-primary>
+Node *Db::parseExpr() {
+  bool Global = consumeIf("gs");
+  if (numLeft() < 2)
+    return nullptr;
+
+  switch (*First) {
+  case 'L':
+    return parseExprPrimary();
+  case 'T':
+    return legacyParse<parse_template_param>();
+  case 'f':
+    return parseFunctionParam();
+  case 'a':
+    switch (First[1]) {
+    case 'a':
+      First += 2;
+      return parseBinaryExpr("&&");
+    case 'd':
+      First += 2;
+      return parsePrefixExpr("&");
+    case 'n':
+      First += 2;
+      return parseBinaryExpr("&");
+    case 'N':
+      First += 2;
+      return parseBinaryExpr("&=");
+    case 'S':
+      First += 2;
+      return parseBinaryExpr("=");
+    case 't': {
+      First += 2;
+      Node *Ty = legacyParse<parse_type>();
+      if (Ty == nullptr)
+        return nullptr;
+      return make<EnclosingExpr>("alignof (", Ty, ")");
+    }
+    case 'z': {
+      First += 2;
+      Node *Ty = parseExpr();
+      if (Ty == nullptr)
+        return nullptr;
+      return make<EnclosingExpr>("alignof (", Ty, ")");
+    }
+    }
+    return nullptr;
+  case 'c':
+    switch (First[1]) {
+    // cc <type> <expression>                               # const_cast<type>(expression)
+    case 'c': {
+      First += 2;
+      Node *Ty = legacyParse<parse_type>();
+      if (Ty == nullptr)
+        return Ty;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<CastExpr>("const_cast", Ty, Ex);
+    }
+    // cl <expression>+ E                                   # call
+    case 'l': {
+      First += 2;
+      Node *Callee = parseExpr();
+      if (Callee == nullptr)
+        return Callee;
+      size_t ExprsBegin = Names.size();
+      while (!consumeIf('E')) {
+        Node *E = parseExpr();
+        if (E == nullptr)
+          return E;
+        Names.push_back(E);
+      }
+      return make<CallExpr>(Callee, popTrailingNodeArray(ExprsBegin));
     }
-};
-
-const char* parse_type(const char* first, const char* last, Db& db);
-const char* parse_encoding(const char* first, const char* last, Db& db);
-const char* parse_name(const char* first, const char* last, Db& db,
-                       bool* ends_with_template_args = 0);
-const char* parse_expression(const char* first, const char* last, Db& db);
-const char* parse_template_args(const char* first, const char* last, Db& db);
-const char* parse_operator_name(const char* first, const char* last, Db& db);
-const char* parse_unqualified_name(const char* first, const char* last, Db& db);
-const char* parse_decltype(const char* first, const char* last, Db& db);
+    case 'm':
+      First += 2;
+      return parseBinaryExpr(",");
+    case 'o':
+      First += 2;
+      return parsePrefixExpr("~");
+    case 'v':
+      return parseConversionExpr();
+    }
+    return nullptr;
+  case 'd':
+    switch (First[1]) {
+    case 'a': {
+      First += 2;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<DeleteExpr>(Ex, Global, /*is_array=*/true);
+    }
+    case 'c': {
+      First += 2;
+      Node *T = legacyParse<parse_type>();
+      if (T == nullptr)
+        return T;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<CastExpr>("dynamic_cast", T, Ex);
+    }
+    case 'e':
+      First += 2;
+      return parsePrefixExpr("*");
+    case 'l': {
+      First += 2;
+      Node *E = parseExpr();
+      if (E == nullptr)
+        return E;
+      return make<DeleteExpr>(E, Global, /*is_array=*/false);
+    }
+    case 'n':
+      return legacyParse<parse_unresolved_name>();
+    case 's': {
+      First += 2;
+      Node *LHS = parseExpr();
+      if (LHS == nullptr)
+        return nullptr;
+      Node *RHS = parseExpr();
+      if (RHS == nullptr)
+        return nullptr;
+      return make<MemberExpr>(LHS, ".*", RHS);
+    }
+    case 't': {
+      First += 2;
+      Node *LHS = parseExpr();
+      if (LHS == nullptr)
+        return LHS;
+      Node *RHS = parseExpr();
+      if (RHS == nullptr)
+        return nullptr;
+      return make<MemberExpr>(LHS, ".", RHS);
+    }
+    case 'v':
+      First += 2;
+      return parseBinaryExpr("/");
+    case 'V':
+      First += 2;
+      return parseBinaryExpr("/=");
+    }
+    return nullptr;
+  case 'e':
+    switch (First[1]) {
+    case 'o':
+      First += 2;
+      return parseBinaryExpr("^");
+    case 'O':
+      First += 2;
+      return parseBinaryExpr("^=");
+    case 'q':
+      First += 2;
+      return parseBinaryExpr("==");
+    }
+    return nullptr;
+  case 'g':
+    switch (First[1]) {
+    case 'e':
+      First += 2;
+      return parseBinaryExpr(">=");
+    case 't':
+      First += 2;
+      return parseBinaryExpr(">");
+    }
+    return nullptr;
+  case 'i':
+    if (First[1] == 'x') {
+      First += 2;
+      Node *Base = parseExpr();
+      if (Base == nullptr)
+        return nullptr;
+      Node *Index = parseExpr();
+      if (Index == nullptr)
+        return Index;
+      return make<ArraySubscriptExpr>(Base, Index);
+    }
+    return nullptr;
+  case 'l':
+    switch (First[1]) {
+    case 'e':
+      First += 2;
+      return parseBinaryExpr("<=");
+    case 's':
+      First += 2;
+      return parseBinaryExpr("<<");
+    case 'S':
+      First += 2;
+      return parseBinaryExpr("<<=");
+    case 't':
+      First += 2;
+      return parseBinaryExpr("<");
+    }
+    return nullptr;
+  case 'm':
+    switch (First[1]) {
+    case 'i':
+      First += 2;
+      return parseBinaryExpr("-");
+    case 'I':
+      First += 2;
+      return parseBinaryExpr("-=");
+    case 'l':
+      First += 2;
+      return parseBinaryExpr("*");
+    case 'L':
+      First += 2;
+      return parseBinaryExpr("*=");
+    case 'm':
+      First += 2;
+      if (consumeIf('_'))
+        return parsePrefixExpr("--");
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return nullptr;
+      return make<PostfixExpr>(Ex, "--");
+    }
+    return nullptr;
+  case 'n':
+    switch (First[1]) {
+    case 'a':
+    case 'w':
+      return parseNewExpr();
+    case 'e':
+      First += 2;
+      return parseBinaryExpr("!=");
+    case 'g':
+      First += 2;
+      return parsePrefixExpr("-");
+    case 't':
+      First += 2;
+      return parsePrefixExpr("!");
+    case 'x':
+      First += 2;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<EnclosingExpr>("noexcept (", Ex, ")");
+    }
+    return nullptr;
+  case 'o':
+    switch (First[1]) {
+    case 'n':
+      return legacyParse<parse_unresolved_name>();
+    case 'o':
+      First += 2;
+      return parseBinaryExpr("||");
+    case 'r':
+      First += 2;
+      return parseBinaryExpr("|");
+    case 'R':
+      First += 2;
+      return parseBinaryExpr("|=");
+    }
+    return nullptr;
+  case 'p':
+    switch (First[1]) {
+    case 'm':
+      First += 2;
+      return parseBinaryExpr("->*");
+    case 'l':
+      First += 2;
+      return parseBinaryExpr("+");
+    case 'L':
+      First += 2;
+      return parseBinaryExpr("+=");
+    case 'p': {
+      First += 2;
+      if (consumeIf('_'))
+        return parsePrefixExpr("++");
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<PostfixExpr>(Ex, "++");
+    }
+    case 's':
+      First += 2;
+      return parsePrefixExpr("+");
+    case 't': {
+      First += 2;
+      Node *L = parseExpr();
+      if (L == nullptr)
+        return nullptr;
+      Node *R = parseExpr();
+      if (R == nullptr)
+        return nullptr;
+      return make<MemberExpr>(L, "->", R);
+    }
+    }
+    return nullptr;
+  case 'q':
+    if (First[1] == 'u') {
+      First += 2;
+      Node *Cond = parseExpr();
+      Node *LHS = parseExpr();
+      Node *RHS = parseExpr();
+      if (Cond && LHS && RHS)
+        return make<ConditionalExpr>(Cond, LHS, RHS);
+    }
+    return nullptr;
+  case 'r':
+    switch (First[1]) {
+    case 'c': {
+      First += 2;
+      Node *T = legacyParse<parse_type>();
+      if (T == nullptr)
+        return T;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<CastExpr>("reinterpret_cast", T, Ex);
+    }
+    case 'm':
+      First += 2;
+      return parseBinaryExpr("%");
+    case 'M':
+      First += 2;
+      return parseBinaryExpr("%=");
+    case 's':
+      First += 2;
+      return parseBinaryExpr(">>");
+    case 'S':
+      First += 2;
+      return parseBinaryExpr(">>=");
+    }
+    return nullptr;
+  case 's':
+    switch (First[1]) {
+    case 'c': {
+      First += 2;
+      Node *T = legacyParse<parse_type>();
+      if (T == nullptr)
+        return T;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<CastExpr>("static_cast", T, Ex);
+    }
+    case 'p': {
+      First += 2;
+      Node *Child = parseExpr();
+      if (Child == nullptr)
+        return nullptr;
+      return make<ParameterPackExpansion>(Child);
+    }
+    case 'r':
+      return legacyParse<parse_unresolved_name>();
+    case 't': {
+      First += 2;
+      Node *Ty = legacyParse<parse_type>();
+      if (Ty == nullptr)
+        return Ty;
+      return make<EnclosingExpr>("sizeof (", Ty, ")");
+    }
+    case 'z': {
+      First += 2;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<EnclosingExpr>("sizeof (", Ex, ")");
+    }
+    case 'Z':
+      First += 2;
+      if (look() == 'T') {
+        Node *R = legacyParse<parse_template_param>();
+        if (R == nullptr)
+          return nullptr;
+        return make<SizeofParamPackExpr>(R);
+      } else if (look() == 'f') {
+        Node *FP = parseFunctionParam();
+        if (FP == nullptr)
+          return nullptr;
+        return make<EnclosingExpr>("sizeof...", FP, ")");
+      }
+      return nullptr;
+    }
+    return nullptr;
+  case 't':
+    switch (First[1]) {
+    case 'e': {
+      First += 2;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return Ex;
+      return make<EnclosingExpr>("typeid (", Ex, ")");
+    }
+    case 'i': {
+      First += 2;
+      Node *Ty = legacyParse<parse_type>();
+      if (Ty == nullptr)
+        return Ty;
+      return make<EnclosingExpr>("typeid (", Ty, ")");
+    }
+    case 'r':
+      First += 2;
+      return make<NameType>("throw");
+    case 'w': {
+      First += 2;
+      Node *Ex = parseExpr();
+      if (Ex == nullptr)
+        return nullptr;
+      return make<ThrowExpr>(Ex);
+    }
+    }
+    return nullptr;
+  case '1':
+  case '2':
+  case '3':
+  case '4':
+  case '5':
+  case '6':
+  case '7':
+  case '8':
+  case '9':
+    return legacyParse<parse_unresolved_name>();
+  }
+  return nullptr;
+}
 
 // <number> ::= [n] <non-negative decimal integer>
 
@@ -2003,32 +2776,23 @@ struct FloatData<long double>
     static const size_t mangled_size = 20;  // May need to be adjusted to 16 or 24 on other platforms
 #endif
     static const size_t max_demangled_size = 40;
-    static constexpr const char* spec = "%LaL";
+    static constexpr const char *spec = "%LaL";
 };
 
-constexpr const char* FloatData<long double>::spec;
+constexpr const char *FloatData<long double>::spec;
 
-template <class Float>
-const char*
-parse_floating_number(const char* first, const char* last, Db& db)
-{
-    const size_t N = FloatData<Float>::mangled_size;
-    if (static_cast<std::size_t>(last - first) <= N)
-        return first;
-    last = first + N;
-    const char* t = first;
-    for (; t != last; ++t)
-    {
-        if (!isxdigit(*t))
-            return first;
-    }
-    if (*t == 'E')
-    {
-        db.Names.push_back(
-            db.make<FloatExpr<Float>>(StringView(first, t)));
-        first = t + 1;
-    }
-    return first;
+template <class Float> Node *Db::parseFloatingLiteral() {
+  const size_t N = FloatData<Float>::mangled_size;
+  if (numLeft() <= N)
+    return nullptr;
+  StringView Data(First, First + N);
+  for (char C : Data)
+    if (!std::isxdigit(C))
+      return nullptr;
+  First += N;
+  if (!consumeIf('E'))
+    return nullptr;
+  return make<FloatExpr<Float>>(Data);
 }
 
 // <positive length number> ::= [0-9]*
@@ -2449,2928 +3213,1524 @@ parse_template_param(const char* first,
     return first;
 }
 
-// cc <type> <expression>                               # const_cast<type> (expression)
+// <simple-id> ::= <source-name> [ <template-args> ]
 
 const char*
-parse_const_cast_expr(const char* first, const char* last, Db& db)
+parse_simple_id(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 3 && first[0] == 'c' && first[1] == 'c')
+    if (first != last)
     {
-        const char* t = parse_type(first+2, last, db);
-        if (t != first+2)
+        const char* t = parse_source_name(first, last, db);
+        if (t != first)
         {
-            const char* t1 = parse_expression(t, last, db);
+            const char* t1 = parse_template_args(t, last, db);
             if (t1 != t)
             {
                 if (db.Names.size() < 2)
                     return first;
-                auto from_expr = db.Names.back();
+                auto args = db.Names.back();
                 db.Names.pop_back();
-                if (db.Names.empty())
-                    return first;
-                db.Names.back() = db.make<CastExpr>(
-                    "const_cast", db.Names.back(), from_expr);
-                first = t1;
+                db.Names.back() =
+                    db.make<NameWithTemplateArgs>(db.Names.back(), args);
             }
+            first = t1;
         }
+        else
+            first = t;
     }
     return first;
 }
 
-// dc <type> <expression>                               # dynamic_cast<type> (expression)
+// <unresolved-type> ::= <template-param>
+//                   ::= <decltype>
+//                   ::= <substitution>
 
 const char*
-parse_dynamic_cast_expr(const char* first, const char* last, Db& db)
+parse_unresolved_type(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 3 && first[0] == 'd' && first[1] == 'c')
+    if (first != last)
     {
-        const char* t = parse_type(first+2, last, db);
-        if (t != first+2)
+        const char* t = first;
+        switch (*first)
         {
-            const char* t1 = parse_expression(t, last, db);
-            if (t1 != t)
+        case 'T':
+          {
+            size_t k0 = db.Names.size();
+            t = parse_template_param(first, last, db);
+            size_t k1 = db.Names.size();
+            if (t != first && k1 == k0 + 1)
+            {
+                db.Subs.push_back(db.Names.back());
+                first = t;
+            }
+            else
+            {
+                for (; k1 != k0; --k1)
+                    db.Names.pop_back();
+            }
+            break;
+          }
+        case 'D':
+            t = parse_decltype(first, last, db);
+            if (t != first)
             {
-                if (db.Names.size() < 2)
-                    return first;
-                auto from_expr = db.Names.back();
-                db.Names.pop_back();
                 if (db.Names.empty())
                     return first;
-                db.Names.back() = db.make<CastExpr>(
-                    "dynamic_cast", db.Names.back(), from_expr);
-                first = t1;
+                db.Subs.push_back(db.Names.back());
+                first = t;
             }
-        }
+            break;
+        case 'S':
+            t = parse_substitution(first, last, db);
+            if (t != first)
+                first = t;
+            else
+            {
+                if (last - first > 2 && first[1] == 't')
+                {
+                    t = parse_unqualified_name(first+2, last, db);
+                    if (t != first+2)
+                    {
+                        if (db.Names.empty())
+                            return first;
+                        db.Names.back() =
+                            db.make<StdQualifiedName>(db.Names.back());
+                        db.Subs.push_back(db.Names.back());
+                        first = t;
+                    }
+                }
+            }
+            break;
+       }
     }
     return first;
 }
 
-// rc <type> <expression>                               # reinterpret_cast<type> (expression)
+// <destructor-name> ::= <unresolved-type>                               # e.g., ~T or ~decltype(f())
+//                   ::= <simple-id>                                     # e.g., ~A<2*N>
 
 const char*
-parse_reinterpret_cast_expr(const char* first, const char* last, Db& db)
+parse_destructor_name(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 3 && first[0] == 'r' && first[1] == 'c')
+    if (first != last)
     {
-        const char* t = parse_type(first+2, last, db);
-        if (t != first+2)
+        const char* t = parse_unresolved_type(first, last, db);
+        if (t == first)
+            t = parse_simple_id(first, last, db);
+        if (t != first)
         {
-            const char* t1 = parse_expression(t, last, db);
-            if (t1 != t)
-            {
-                if (db.Names.size() < 2)
-                    return first;
-                auto from_expr = db.Names.back();
-                db.Names.pop_back();
-                if (db.Names.empty())
-                    return first;
-                db.Names.back() = db.make<CastExpr>(
-                    "reinterpret_cast", db.Names.back(), from_expr);
-                first = t1;
-            }
+            if (db.Names.empty())
+                return first;
+            db.Names.back() = db.make<DtorName>(db.Names.back());
+            first = t;
         }
     }
     return first;
 }
 
-// sc <type> <expression>                               # static_cast<type> (expression)
+// <base-unresolved-name> ::= <simple-id>                                # unresolved name
+//          extension     ::= <operator-name>                            # unresolved operator-function-id
+//          extension     ::= <operator-name> <template-args>            # unresolved operator template-id
+//                        ::= on <operator-name>                         # unresolved operator-function-id
+//                        ::= on <operator-name> <template-args>         # unresolved operator template-id
+//                        ::= dn <destructor-name>                       # destructor or pseudo-destructor;
+//                                                                         # e.g. ~X or ~X<N-1>
 
 const char*
-parse_static_cast_expr(const char* first, const char* last, Db& db)
+parse_base_unresolved_name(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 3 && first[0] == 's' && first[1] == 'c')
+    if (last - first >= 2)
     {
-        const char* t = parse_type(first+2, last, db);
-        if (t != first+2)
+        if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n')
         {
-            const char* t1 = parse_expression(t, last, db);
-            if (t1 != t)
+            if (first[0] == 'o')
             {
-                if (db.Names.size() < 2)
-                    return first;
-                auto from_expr = db.Names.back();
-                db.Names.pop_back();
-                db.Names.back() = db.make<CastExpr>(
-                    "static_cast", db.Names.back(), from_expr);
-                first = t1;
+                const char* t = parse_operator_name(first+2, last, db);
+                if (t != first+2)
+                {
+                    first = parse_template_args(t, last, db);
+                    if (first != t)
+                    {
+                        if (db.Names.size() < 2)
+                            return first;
+                        auto args = db.Names.back();
+                        db.Names.pop_back();
+                        db.Names.back() =
+                            db.make<NameWithTemplateArgs>(
+                                db.Names.back(), args);
+                    }
+                }
+            }
+            else
+            {
+                const char* t = parse_destructor_name(first+2, last, db);
+                if (t != first+2)
+                    first = t;
+            }
+        }
+        else
+        {
+            const char* t = parse_simple_id(first, last, db);
+            if (t == first)
+            {
+                t = parse_operator_name(first, last, db);
+                if (t != first)
+                {
+                    first = parse_template_args(t, last, db);
+                    if (first != t)
+                    {
+                        if (db.Names.size() < 2)
+                            return first;
+                        auto args = db.Names.back();
+                        db.Names.pop_back();
+                        db.Names.back() =
+                            db.make<NameWithTemplateArgs>(
+                                db.Names.back(), args);
+                    }
+                }
             }
+            else
+                first = t;
         }
     }
     return first;
 }
 
-// sp <expression>                                  # pack expansion
+// <unresolved-qualifier-level> ::= <simple-id>
 
 const char*
-parse_pack_expansion(const char* first, const char* last, Db& db)
+parse_unresolved_qualifier_level(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 3 && first[0] == 's' && first[1] == 'p')
-    {
-        size_t before = db.Names.size();
-        const char* t = parse_expression(first+2, last, db);
-        if (before + 1 != db.Names.size())
-            return first;
-        if (t != first+2)
-            first = t;
-        db.Names.back() = db.make<ParameterPackExpansion>(db.Names.back());
-    }
-    return first;
+    return parse_simple_id(first, last, db);
 }
 
-// st <type>                                            # sizeof (a type)
-
-const char*
-parse_sizeof_type_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 's' && first[1] == 't')
-    {
-        const char* t = parse_type(first+2, last, db);
-        if (t != first+2)
-        {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() = db.make<EnclosingExpr>(
-                "sizeof (", db.Names.back(), ")");
-            first = t;
-        }
-    }
-    return first;
-}
-
-// sz <expr>                                            # sizeof (a expression)
+// <unresolved-name>
+//  extension        ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name>
+//                   ::= [gs] <base-unresolved-name>                     # x or (with "gs") ::x
+//                   ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>  
+//                                                                       # A::x, N::y, A<T>::z; "gs" means leading "::"
+//                   ::= sr <unresolved-type> <base-unresolved-name>     # T::x / decltype(p)::x
+//  extension        ::= sr <unresolved-type> <template-args> <base-unresolved-name>
+//                                                                       # T::N::x /decltype(p)::N::x
+//  (ignored)        ::= srN <unresolved-type>  <unresolved-qualifier-level>+ E <base-unresolved-name>
 
 const char*
-parse_sizeof_expr_expr(const char* first, const char* last, Db& db)
+parse_unresolved_name(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 3 && first[0] == 's' && first[1] == 'z')
+    if (last - first > 2)
     {
-        const char* t = parse_expression(first+2, last, db);
-        if (t != first+2)
+        const char* t = first;
+        bool global = false;
+        if (t[0] == 'g' && t[1] == 's')
         {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() = db.make<EnclosingExpr>(
-                "sizeof (", db.Names.back(), ")");
-            first = t;
+            global = true;
+            t += 2;
         }
-    }
-    return first;
-}
-
-// sZ <template-param>                                  # size of a parameter pack
-
-const char*
-parse_sizeof_param_pack_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T')
-    {
-        size_t k0 = db.Names.size();
-        const char* t = parse_template_param(first+2, last, db);
-        size_t k1 = db.Names.size();
-        if (t != first+2 && k0 + 1 == k1)
+        const char* t2 = parse_base_unresolved_name(t, last, db);
+        if (t2 != t)
         {
-            db.Names.back() = db.make<SizeofParamPackExpr>(db.Names.back());
-            first = t;
+            if (global)
+            {
+                if (db.Names.empty())
+                    return first;
+                db.Names.back() =
+                    db.make<GlobalQualifiedName>(db.Names.back());
+            }
+            first = t2;
+        }
+        else if (last - t > 2 && t[0] == 's' && t[1] == 'r')
+        {
+            if (t[2] == 'N')
+            {
+                t += 3;
+                const char* t1 = parse_unresolved_type(t, last, db);
+                if (t1 == t || t1 == last)
+                    return first;
+                t = t1;
+                t1 = parse_template_args(t, last, db);
+                if (t1 != t)
+                {
+                    if (db.Names.size() < 2)
+                        return first;
+                    auto args = db.Names.back();
+                    db.Names.pop_back();
+                    db.Names.back() = db.make<NameWithTemplateArgs>(
+                        db.Names.back(), args);
+                    t = t1;
+                    if (t == last)
+                    {
+                        db.Names.pop_back();
+                        return first;
+                    }
+                }
+                while (*t != 'E')
+                {
+                    t1 = parse_unresolved_qualifier_level(t, last, db);
+                    if (t1 == t || t1 == last || db.Names.size() < 2)
+                        return first;
+                    auto s = db.Names.back();
+                    db.Names.pop_back();
+                    db.Names.back() =
+                        db.make<QualifiedName>(db.Names.back(), s);
+                    t = t1;
+                }
+                ++t;
+                t1 = parse_base_unresolved_name(t, last, db);
+                if (t1 == t)
+                {
+                    if (!db.Names.empty())
+                        db.Names.pop_back();
+                    return first;
+                }
+                if (db.Names.size() < 2)
+                    return first;
+                auto s = db.Names.back();
+                db.Names.pop_back();
+                db.Names.back() =
+                    db.make<QualifiedName>(db.Names.back(), s);
+                first = t1;
+            }
+            else
+            {
+                t += 2;
+                const char* t1 = parse_unresolved_type(t, last, db);
+                if (t1 != t)
+                {
+                    t = t1;
+                    t1 = parse_template_args(t, last, db);
+                    if (t1 != t)
+                    {
+                        if (db.Names.size() < 2)
+                            return first;
+                        auto args = db.Names.back();
+                        db.Names.pop_back();
+                        db.Names.back() =
+                            db.make<NameWithTemplateArgs>(
+                                db.Names.back(), args);
+                        t = t1;
+                    }
+                    t1 = parse_base_unresolved_name(t, last, db);
+                    if (t1 == t)
+                    {
+                        if (!db.Names.empty())
+                            db.Names.pop_back();
+                        return first;
+                    }
+                    if (db.Names.size() < 2)
+                        return first;
+                    auto s = db.Names.back();
+                    db.Names.pop_back();
+                    db.Names.back() =
+                        db.make<QualifiedName>(db.Names.back(), s);
+                    first = t1;
+                }
+                else
+                {
+                    t1 = parse_unresolved_qualifier_level(t, last, db);
+                    if (t1 == t || t1 == last)
+                        return first;
+                    t = t1;
+                    if (global)
+                    {
+                        if (db.Names.empty())
+                            return first;
+                        db.Names.back() =
+                            db.make<GlobalQualifiedName>(
+                                db.Names.back());
+                    }
+                    while (*t != 'E')
+                    {
+                        t1 = parse_unresolved_qualifier_level(t, last, db);
+                        if (t1 == t || t1 == last || db.Names.size() < 2)
+                            return first;
+                        auto s = db.Names.back();
+                        db.Names.pop_back();
+                        db.Names.back() = db.make<QualifiedName>(
+                            db.Names.back(), s);
+                        t = t1;
+                    }
+                    ++t;
+                    t1 = parse_base_unresolved_name(t, last, db);
+                    if (t1 == t)
+                    {
+                        if (!db.Names.empty())
+                            db.Names.pop_back();
+                        return first;
+                    }
+                    if (db.Names.size() < 2)
+                        return first;
+                    auto s = db.Names.back();
+                    db.Names.pop_back();
+                    db.Names.back() =
+                        db.make<QualifiedName>(db.Names.back(), s);
+                    first = t1;
+                }
+            }
         }
     }
     return first;
 }
 
-// <function-param> ::= fp <top-level CV-Qualifiers> _                                     # L == 0, first parameter
-//                  ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _   # L == 0, second and later parameters
-//                  ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _         # L > 0, first parameter
-//                  ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _   # L > 0, second and later parameters
+//  <ref-qualifier> ::= R                   # & ref-qualifier
+//  <ref-qualifier> ::= O                   # && ref-qualifier
+
+// <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E
 
 const char*
-parse_function_param(const char* first, const char* last, Db& db)
+parse_function_type(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 3 && *first == 'f')
+    if (first != last && *first == 'F')
     {
-        if (first[1] == 'p')
+        const char* t = first+1;
+        if (t != last)
         {
-            Qualifiers cv;
-            const char* t = parse_cv_qualifiers(first+2, last, cv);
-            const char* t1 = parse_number(t, last);
-            if (t1 != last && *t1 == '_')
+            if (*t == 'Y')
             {
-                db.Names.push_back(
-                    db.make<FunctionParam>(StringView(t, t1)));
-                first = t1+1;
+                /* extern "C" */
+                if (++t == last)
+                    return first;
             }
-        }
-        else if (first[1] == 'L')
-        {
-            Qualifiers cv;
-            const char* t0 = parse_number(first+2, last);
-            if (t0 != last && *t0 == 'p')
+            const char* t1 = parse_type(t, last, db);
+            if (t1 != t && !db.Names.empty())
             {
-                ++t0;
-                const char* t = parse_cv_qualifiers(t0, last, cv);
-                const char* t1 = parse_number(t, last);
-                if (t1 != last && *t1 == '_')
+                Node* ret_type = db.Names.back();
+                db.Names.pop_back();
+                size_t params_begin = db.Names.size();
+                t = t1;
+                FunctionRefQual RefQuals = FrefQualNone;
+                while (true)
                 {
-                    db.Names.push_back(
-                        db.make<FunctionParam>(StringView(t, t1)));
-                    first = t1+1;
+                    if (t == last)
+                    {
+                        if (!db.Names.empty())
+                          db.Names.pop_back();
+                        return first;
+                    }
+                    if (*t == 'E')
+                    {
+                        ++t;
+                        break;
+                    }
+                    if (*t == 'v')
+                    {
+                        ++t;
+                        continue;
+                    }
+                    if (*t == 'R' && t+1 != last && t[1] == 'E')
+                    {
+                        RefQuals = FrefQualLValue;
+                        ++t;
+                        continue;
+                    }
+                    if (*t == 'O' && t+1 != last && t[1] == 'E')
+                    {
+                        RefQuals = FrefQualRValue;
+                        ++t;
+                        continue;
+                    }
+                    size_t k0 = db.Names.size();
+                    t1 = parse_type(t, last, db);
+                    size_t k1 = db.Names.size();
+                    if (t1 == t || t1 == last || k1 < k0)
+                        return first;
+                    t = t1;
                 }
+                if (db.Names.empty() || params_begin > db.Names.size())
+                    return first;
+                Node* fty = db.make<FunctionType>(
+                    ret_type, db.popTrailingNodeArray(params_begin));
+                if (RefQuals)
+                    fty = db.make<FunctionRefQualType>(fty, RefQuals);
+                db.Names.push_back(fty);
+                first = t;
             }
         }
     }
     return first;
 }
 
-// sZ <function-param>                                  # size of a function parameter pack
+// <pointer-to-member-type> ::= M <class type> <member type>
 
 const char*
-parse_sizeof_function_param_pack_expr(const char* first, const char* last, Db& db)
+parse_pointer_to_member_type(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'f')
+    if (first != last && *first == 'M')
     {
-        const char* t = parse_function_param(first+2, last, db);
-        if (t != first+2)
+        const char* t = parse_type(first+1, last, db);
+        if (t != first+1)
         {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() = db.make<EnclosingExpr>(
-                "sizeof...(", db.Names.back(), ")");
-            first = t;
-        }
-    }
-    return first;
-}
-
-// te <expression>                                      # typeid (expression)
-// ti <type>                                            # typeid (type)
-
-const char*
-parse_typeid_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i'))
-    {
-        const char* t;
-        if (first[1] == 'e')
-            t = parse_expression(first+2, last, db);
-        else
-            t = parse_type(first+2, last, db);
-        if (t != first+2)
-        {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() = db.make<EnclosingExpr>(
-                "typeid(", db.Names.back(), ")");
-            first = t;
-        }
-    }
-    return first;
-}
-
-// tw <expression>                                      # throw expression
-
-const char*
-parse_throw_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 't' && first[1] == 'w')
-    {
-        const char* t = parse_expression(first+2, last, db);
-        if (t != first+2)
-        {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() = db.make<ThrowExpr>(db.Names.back());
-            first = t;
-        }
-    }
-    return first;
-}
-
-// ds <expression> <expression>                         # expr.*expr
-
-const char*
-parse_dot_star_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 'd' && first[1] == 's')
-    {
-        const char* t = parse_expression(first+2, last, db);
-        if (t != first+2)
-        {
-            const char* t1 = parse_expression(t, last, db);
-            if (t1 != t)
+            const char* t2 = parse_type(t, last, db);
+            if (t2 != t)
             {
                 if (db.Names.size() < 2)
                     return first;
-                auto rhs_expr = db.Names.back();
+                auto func = std::move(db.Names.back());
                 db.Names.pop_back();
-                db.Names.back() = db.make<MemberExpr>(
-                    db.Names.back(), ".*", rhs_expr);
-                first = t1;
+                auto ClassType = std::move(db.Names.back());
+                db.Names.back() =
+                    db.make<PointerToMemberType>(ClassType, func);
+                first = t2;
             }
         }
     }
     return first;
 }
 
-// <simple-id> ::= <source-name> [ <template-args> ]
+// <array-type> ::= A <positive dimension number> _ <element type>
+//              ::= A [<dimension expression>] _ <element type>
 
 const char*
-parse_simple_id(const char* first, const char* last, Db& db)
+parse_array_type(const char* first, const char* last, Db& db)
 {
-    if (first != last)
+    if (first != last && *first == 'A' && first+1 != last)
     {
-        const char* t = parse_source_name(first, last, db);
-        if (t != first)
+        if (first[1] == '_')
         {
-            const char* t1 = parse_template_args(t, last, db);
-            if (t1 != t)
+            const char* t = parse_type(first+2, last, db);
+            if (t != first+2)
             {
-                if (db.Names.size() < 2)
+                if (db.Names.empty())
                     return first;
-                auto args = db.Names.back();
-                db.Names.pop_back();
-                db.Names.back() =
-                    db.make<NameWithTemplateArgs>(db.Names.back(), args);
+                db.Names.back() = db.make<ArrayType>(db.Names.back());
+                first = t;
             }
-            first = t1;
         }
-        else
-            first = t;
-    }
-    return first;
-}
-
-// <unresolved-type> ::= <template-param>
-//                   ::= <decltype>
-//                   ::= <substitution>
-
-const char*
-parse_unresolved_type(const char* first, const char* last, Db& db)
-{
-    if (first != last)
-    {
-        const char* t = first;
-        switch (*first)
+        else if ('1' <= first[1] && first[1] <= '9')
         {
-        case 'T':
-          {
-            size_t k0 = db.Names.size();
-            t = parse_template_param(first, last, db);
-            size_t k1 = db.Names.size();
-            if (t != first && k1 == k0 + 1)
-            {
-                db.Subs.push_back(db.Names.back());
-                first = t;
-            }
-            else
-            {
-                for (; k1 != k0; --k1)
-                    db.Names.pop_back();
-            }
-            break;
-          }
-        case 'D':
-            t = parse_decltype(first, last, db);
-            if (t != first)
+            const char* t = parse_number(first+1, last);
+            if (t != last && *t == '_')
             {
-                if (db.Names.empty())
-                    return first;
-                db.Subs.push_back(db.Names.back());
-                first = t;
+                const char* t2 = parse_type(t+1, last, db);
+                if (t2 != t+1)
+                {
+                    if (db.Names.empty())
+                        return first;
+                    db.Names.back() =
+                        db.make<ArrayType>(db.Names.back(),
+                                            StringView(first + 1, t));
+                    first = t2;
+                }
             }
-            break;
-        case 'S':
-            t = parse_substitution(first, last, db);
-            if (t != first)
-                first = t;
-            else
+        }
+        else
+        {
+            const char* t = parse_expression(first+1, last, db);
+            if (t != first+1 && t != last && *t == '_')
             {
-                if (last - first > 2 && first[1] == 't')
+                const char* t2 = parse_type(++t, last, db);
+                if (t2 != t)
                 {
-                    t = parse_unqualified_name(first+2, last, db);
-                    if (t != first+2)
-                    {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<StdQualifiedName>(db.Names.back());
-                        db.Subs.push_back(db.Names.back());
-                        first = t;
-                    }
+                    if (db.Names.size() < 2)
+                        return first;
+                    auto base_type = std::move(db.Names.back());
+                    db.Names.pop_back();
+                    auto dimension_expr = std::move(db.Names.back());
+                    db.Names.back() =
+                        db.make<ArrayType>(base_type, dimension_expr);
+                    first = t2;
                 }
             }
-            break;
-       }
+        }
     }
     return first;
 }
 
-// <destructor-name> ::= <unresolved-type>                               # e.g., ~T or ~decltype(f())
-//                   ::= <simple-id>                                     # e.g., ~A<2*N>
+// <decltype>  ::= Dt <expression> E  # decltype of an id-expression or class member access (C++0x)
+//             ::= DT <expression> E  # decltype of an expression (C++0x)
 
 const char*
-parse_destructor_name(const char* first, const char* last, Db& db)
+parse_decltype(const char* first, const char* last, Db& db)
 {
-    if (first != last)
+    if (last - first >= 4 && first[0] == 'D')
     {
-        const char* t = parse_unresolved_type(first, last, db);
-        if (t == first)
-            t = parse_simple_id(first, last, db);
-        if (t != first)
+        switch (first[1])
         {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() = db.make<DtorName>(db.Names.back());
-            first = t;
+        case 't':
+        case 'T':
+            {
+                const char* t = parse_expression(first+2, last, db);
+                if (t != first+2 && t != last && *t == 'E')
+                {
+                    if (db.Names.empty())
+                        return first;
+                    db.Names.back() = db.make<EnclosingExpr>(
+                        "decltype(", db.Names.back(), ")");
+                    first = t+1;
+                }
+            }
+            break;
         }
     }
     return first;
 }
 
-// <base-unresolved-name> ::= <simple-id>                                # unresolved name
-//          extension     ::= <operator-name>                            # unresolved operator-function-id
-//          extension     ::= <operator-name> <template-args>            # unresolved operator template-id
-//                        ::= on <operator-name>                         # unresolved operator-function-id
-//                        ::= on <operator-name> <template-args>         # unresolved operator template-id
-//                        ::= dn <destructor-name>                       # destructor or pseudo-destructor;
-//                                                                         # e.g. ~X or ~X<N-1>
+// extension:
+// <vector-type>           ::= Dv <positive dimension number> _
+//                                    <extended element type>
+//                         ::= Dv [<dimension expression>] _ <element type>
+// <extended element type> ::= <element type>
+//                         ::= p # AltiVec vector pixel
 
 const char*
-parse_base_unresolved_name(const char* first, const char* last, Db& db)
+parse_vector_type(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 2)
+    if (last - first > 3 && first[0] == 'D' && first[1] == 'v')
     {
-        if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n')
+        if ('1' <= first[2] && first[2] <= '9')
         {
-            if (first[0] == 'o')
+            const char* t = parse_number(first+2, last);
+            if (t == last || *t != '_')
+                return first;
+            const char* num = first + 2;
+            size_t sz = static_cast<size_t>(t - num);
+            if (++t != last)
             {
-                const char* t = parse_operator_name(first+2, last, db);
-                if (t != first+2)
+                if (*t != 'p')
                 {
-                    first = parse_template_args(t, last, db);
-                    if (first != t)
+                    const char* t1 = parse_type(t, last, db);
+                    if (t1 != t)
                     {
-                        if (db.Names.size() < 2)
+                        if (db.Names.empty())
                             return first;
-                        auto args = db.Names.back();
-                        db.Names.pop_back();
                         db.Names.back() =
-                            db.make<NameWithTemplateArgs>(
-                                db.Names.back(), args);
+                            db.make<VectorType>(db.Names.back(),
+                                                 StringView(num, num + sz));
+                        first = t1;
                     }
                 }
-            }
-            else
-            {
-                const char* t = parse_destructor_name(first+2, last, db);
-                if (t != first+2)
+                else
+                {
+                    ++t;
+                    db.Names.push_back(
+                        db.make<VectorType>(StringView(num, num + sz)));
                     first = t;
+                }
             }
         }
         else
         {
-            const char* t = parse_simple_id(first, last, db);
-            if (t == first)
+            Node* num = nullptr;
+            const char* t1 = first+2;
+            if (*t1 != '_')
             {
-                t = parse_operator_name(first, last, db);
-                if (t != first)
+                const char* t = parse_expression(t1, last, db);
+                if (t != t1)
                 {
-                    first = parse_template_args(t, last, db);
-                    if (first != t)
-                    {
-                        if (db.Names.size() < 2)
-                            return first;
-                        auto args = db.Names.back();
-                        db.Names.pop_back();
-                        db.Names.back() =
-                            db.make<NameWithTemplateArgs>(
-                                db.Names.back(), args);
-                    }
+                    if (db.Names.empty())
+                        return first;
+                    num = db.Names.back();
+                    db.Names.pop_back();
+                    t1 = t;
                 }
             }
-            else
-                first = t;
+            if (t1 != last && *t1 == '_' && ++t1 != last)
+            {
+                const char* t = parse_type(t1, last, db);
+                if (t != t1)
+                {
+                    if (db.Names.empty())
+                        return first;
+                    if (num)
+                        db.Names.back() =
+                            db.make<VectorType>(db.Names.back(), num);
+                    else
+                        db.Names.back() =
+                            db.make<VectorType>(db.Names.back(), StringView());
+                    first = t;
+                } else if (num)
+                    db.Names.push_back(num);
+            }
         }
     }
     return first;
 }
 
-// <unresolved-qualifier-level> ::= <simple-id>
-
-const char*
-parse_unresolved_qualifier_level(const char* first, const char* last, Db& db)
-{
-    return parse_simple_id(first, last, db);
-}
+// <type> ::= <builtin-type>
+//        ::= <function-type>
+//        ::= <class-enum-type>
+//        ::= <array-type>
+//        ::= <pointer-to-member-type>
+//        ::= <template-param>
+//        ::= <template-template-param> <template-args>
+//        ::= <decltype>
+//        ::= <substitution>
+//        ::= <CV-Qualifiers> <type>
+//        ::= P <type>        # pointer-to
+//        ::= R <type>        # reference-to
+//        ::= O <type>        # rvalue reference-to (C++0x)
+//        ::= C <type>        # complex pair (C 2000)
+//        ::= G <type>        # imaginary (C 2000)
+//        ::= Dp <type>       # pack expansion (C++0x)
+//        ::= U <source-name> <type>  # vendor extended type qualifier
+// extension := U <objc-name> <objc-type>  # objc-type<identifier>
+// extension := <vector-type> # <vector-type> starts with Dv
 
-// <unresolved-name>
-//  extension        ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name>
-//                   ::= [gs] <base-unresolved-name>                     # x or (with "gs") ::x
-//                   ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>  
-//                                                                       # A::x, N::y, A<T>::z; "gs" means leading "::"
-//                   ::= sr <unresolved-type> <base-unresolved-name>     # T::x / decltype(p)::x
-//  extension        ::= sr <unresolved-type> <template-args> <base-unresolved-name>
-//                                                                       # T::N::x /decltype(p)::N::x
-//  (ignored)        ::= srN <unresolved-type>  <unresolved-qualifier-level>+ E <base-unresolved-name>
+// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier>  # k0 = 9 + <number of digits in k1> + k1
+// <objc-type> := <source-name>  # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name>
 
 const char*
-parse_unresolved_name(const char* first, const char* last, Db& db)
+parse_type(const char* first, const char* last, Db& db)
 {
-    if (last - first > 2)
+    if (first != last)
     {
-        const char* t = first;
-        bool global = false;
-        if (t[0] == 'g' && t[1] == 's')
-        {
-            global = true;
-            t += 2;
-        }
-        const char* t2 = parse_base_unresolved_name(t, last, db);
-        if (t2 != t)
-        {
-            if (global)
-            {
-                if (db.Names.empty())
-                    return first;
-                db.Names.back() =
-                    db.make<GlobalQualifiedName>(db.Names.back());
-            }
-            first = t2;
-        }
-        else if (last - t > 2 && t[0] == 's' && t[1] == 'r')
+        switch (*first)
         {
-            if (t[2] == 'N')
-            {
-                t += 3;
-                const char* t1 = parse_unresolved_type(t, last, db);
-                if (t1 == t || t1 == last)
-                    return first;
-                t = t1;
-                t1 = parse_template_args(t, last, db);
-                if (t1 != t)
+            case 'r':
+            case 'V':
+            case 'K':
+              {
+                Qualifiers cv = QualNone;
+                const char* t = parse_cv_qualifiers(first, last, cv);
+                if (t != first)
                 {
-                    if (db.Names.size() < 2)
-                        return first;
-                    auto args = db.Names.back();
-                    db.Names.pop_back();
-                    db.Names.back() = db.make<NameWithTemplateArgs>(
-                        db.Names.back(), args);
-                    t = t1;
-                    if (t == last)
+                    bool is_function = *t == 'F';
+                    size_t k0 = db.Names.size();
+                    const char* t1 = parse_type(t, last, db);
+                    size_t k1 = db.Names.size();
+                    if (t1 != t && k0 + 1 == k1)
                     {
-                        db.Names.pop_back();
-                        return first;
+                        if (is_function)
+                            db.Subs.pop_back();
+                        if (cv)
+                        {
+                            if (is_function)
+                                db.Names.back() = db.make<FunctionQualType>(
+                                    db.Names.back(), cv);
+                            else
+                                db.Names.back() =
+                                    db.make<QualType>(db.Names.back(), cv);
+                        }
+                        db.Subs.push_back(db.Names.back());
+                        first = t1;
                     }
                 }
-                while (*t != 'E')
-                {
-                    t1 = parse_unresolved_qualifier_level(t, last, db);
-                    if (t1 == t || t1 == last || db.Names.size() < 2)
-                        return first;
-                    auto s = db.Names.back();
-                    db.Names.pop_back();
-                    db.Names.back() =
-                        db.make<QualifiedName>(db.Names.back(), s);
-                    t = t1;
-                }
-                ++t;
-                t1 = parse_base_unresolved_name(t, last, db);
-                if (t1 == t)
-                {
-                    if (!db.Names.empty())
-                        db.Names.pop_back();
-                    return first;
-                }
-                if (db.Names.size() < 2)
-                    return first;
-                auto s = db.Names.back();
-                db.Names.pop_back();
-                db.Names.back() =
-                    db.make<QualifiedName>(db.Names.back(), s);
-                first = t1;
-            }
-            else
-            {
-                t += 2;
-                const char* t1 = parse_unresolved_type(t, last, db);
-                if (t1 != t)
+              }
+                break;
+            default:
+              {
+                const char* t = parse_builtin_type(first, last, db);
+                if (t != first)
                 {
-                    t = t1;
-                    t1 = parse_template_args(t, last, db);
-                    if (t1 != t)
-                    {
-                        if (db.Names.size() < 2)
-                            return first;
-                        auto args = db.Names.back();
-                        db.Names.pop_back();
-                        db.Names.back() =
-                            db.make<NameWithTemplateArgs>(
-                                db.Names.back(), args);
-                        t = t1;
-                    }
-                    t1 = parse_base_unresolved_name(t, last, db);
-                    if (t1 == t)
-                    {
-                        if (!db.Names.empty())
-                            db.Names.pop_back();
-                        return first;
-                    }
-                    if (db.Names.size() < 2)
-                        return first;
-                    auto s = db.Names.back();
-                    db.Names.pop_back();
-                    db.Names.back() =
-                        db.make<QualifiedName>(db.Names.back(), s);
-                    first = t1;
+                    first = t;
                 }
                 else
                 {
-                    t1 = parse_unresolved_qualifier_level(t, last, db);
-                    if (t1 == t || t1 == last)
-                        return first;
-                    t = t1;
-                    if (global)
-                    {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<GlobalQualifiedName>(
-                                db.Names.back());
-                    }
-                    while (*t != 'E')
-                    {
-                        t1 = parse_unresolved_qualifier_level(t, last, db);
-                        if (t1 == t || t1 == last || db.Names.size() < 2)
-                            return first;
-                        auto s = db.Names.back();
-                        db.Names.pop_back();
-                        db.Names.back() = db.make<QualifiedName>(
-                            db.Names.back(), s);
-                        t = t1;
-                    }
-                    ++t;
-                    t1 = parse_base_unresolved_name(t, last, db);
-                    if (t1 == t)
+                    switch (*first)
                     {
-                        if (!db.Names.empty())
-                            db.Names.pop_back();
-                        return first;
-                    }
-                    if (db.Names.size() < 2)
-                        return first;
-                    auto s = db.Names.back();
-                    db.Names.pop_back();
-                    db.Names.back() =
-                        db.make<QualifiedName>(db.Names.back(), s);
-                    first = t1;
-                }
-            }
-        }
-    }
-    return first;
-}
-
-// dt <expression> <unresolved-name>                    # expr.name
-
-const char*
-parse_dot_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 'd' && first[1] == 't')
-    {
-        const char* t = parse_expression(first+2, last, db);
-        if (t != first+2)
-        {
-            const char* t1 = parse_unresolved_name(t, last, db);
-            if (t1 != t)
-            {
-                if (db.Names.size() < 2)
-                    return first;
-                auto name = db.Names.back();
-                db.Names.pop_back();
-                if (db.Names.empty())
-                    return first;
-                db.Names.back() = db.make<MemberExpr>(db.Names.back(), ".", name);
-                first = t1;
-            }
-        }
-    }
-    return first;
-}
-
-// cl <expression>+ E                                   # call
-
-const char*
-parse_call_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 4 && first[0] == 'c' && first[1] == 'l')
-    {
-        const char* t = parse_expression(first+2, last, db);
-        if (t == last || t == first + 2 || db.Names.empty())
-            return first;
-        Node* callee = db.Names.back();
-        db.Names.pop_back();
-        size_t args_begin = db.Names.size();
-        while (*t != 'E')
-        {
-            const char* t1 = parse_expression(t, last, db);
-            if (t1 == last || t1 == t)
-                return first;
-            t = t1;
-        }
-        if (db.Names.size() < args_begin)
-            return first;
-        ++t;
-        CallExpr* the_call = db.make<CallExpr>(
-            callee, db.popTrailingNodeArray(args_begin));
-        db.Names.push_back(the_call);
-        first = t;
-    }
-    return first;
-}
-
-// [gs] nw <expression>* _ <type> E                     # new (expr-list) type
-// [gs] nw <expression>* _ <type> <initializer>         # new (expr-list) type (init)
-// [gs] na <expression>* _ <type> E                     # new[] (expr-list) type
-// [gs] na <expression>* _ <type> <initializer>         # new[] (expr-list) type (init)
-// <initializer> ::= pi <expression>* E                 # parenthesized initialization
-
-const char*
-parse_new_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 4)
-    {
-        const char* t = first;
-        bool parsed_gs = false;
-        if (t[0] == 'g' && t[1] == 's')
-        {
-            t += 2;
-            parsed_gs = true;
-        }
-        if (t[0] == 'n' && (t[1] == 'w' || t[1] == 'a'))
-        {
-            bool is_array = t[1] == 'a';
-            t += 2;
-            if (t == last)
-                return first;
-            size_t first_expr_in_list = db.Names.size();
-            NodeArray ExprList, init_list;
-            while (*t != '_')
-            {
-                const char* t1 = parse_expression(t, last, db);
-                if (t1 == t || t1 == last)
-                    return first;
-                t = t1;
-            }
-            if (first_expr_in_list > db.Names.size())
-                return first;
-            ExprList = db.popTrailingNodeArray(first_expr_in_list);
-            ++t;
-            const char* t1 = parse_type(t, last, db);
-            if (t1 == t || t1 == last)
-                return first;
-            t = t1;
-            bool has_init = false;
-            if (last - t >= 3 && t[0] == 'p' && t[1] == 'i')
-            {
-                t += 2;
-                has_init = true;
-                size_t init_list_begin = db.Names.size();
-                while (*t != 'E')
-                {
-                    t1 = parse_expression(t, last, db);
-                    if (t1 == t || t1 == last)
-                        return first;
-                    t = t1;
-                }
-                if (init_list_begin > db.Names.size())
-                    return first;
-                init_list = db.popTrailingNodeArray(init_list_begin);
-            }
-            if (*t != 'E' || db.Names.empty())
-                return first;
-            auto type = db.Names.back();
-            db.Names.pop_back();
-            db.Names.push_back(
-                db.make<NewExpr>(ExprList, type, init_list,
-                                  parsed_gs, is_array));
-            first = t+1;
-        }
-    }
-    return first;
-}
-
-// cv <type> <expression>                               # conversion with one argument
-// cv <type> _ <expression>* E                          # conversion with a different number of arguments
-
-const char*
-parse_conversion_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 'c' && first[1] == 'v')
-    {
-        bool TryToParseTemplateArgs = db.TryToParseTemplateArgs;
-        db.TryToParseTemplateArgs = false;
-        size_t type_index = db.Names.size();
-        const char* t = parse_type(first+2, last, db);
-        db.TryToParseTemplateArgs = TryToParseTemplateArgs;
-        if (t != first+2 && t != last)
-        {
-            size_t expr_list_begin = db.Names.size();
-            if (*t != '_')
-            {
-                const char* t1 = parse_expression(t, last, db);
-                if (t1 == t)
-                    return first;
-                t = t1;
-            }
-            else
-            {
-                ++t;
-                if (t == last)
-                    return first;
-                if (*t != 'E')
-                {
-                    while (*t != 'E')
-                    {
-                        const char* t1 = parse_expression(t, last, db);
-                        if (t1 == t || t1 == last)
-                            return first;
-                        t = t1;
-                    }
-                }
-                ++t;
-            }
-            if (db.Names.size() < expr_list_begin ||
-                type_index + 1 != expr_list_begin)
-                return first;
-            NodeArray expressions = db.makeNodeArray(
-                db.Names.begin() + (long)expr_list_begin, db.Names.end());
-            auto* conv_expr = db.make<ConversionExpr>(
-                db.Names[type_index], expressions);
-            db.Names.dropBack(type_index);
-            db.Names.push_back(conv_expr);
-            first = t;
-        }
-    }
-    return first;
-}
-
-// pt <expression> <expression>                    # expr->name
-
-const char*
-parse_arrow_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 'p' && first[1] == 't')
-    {
-        const char* t = parse_expression(first+2, last, db);
-        if (t != first+2)
-        {
-            const char* t1 = parse_expression(t, last, db);
-            if (t1 != t)
-            {
-                if (db.Names.size() < 2)
-                    return first;
-                auto tmp = db.Names.back();
-                db.Names.pop_back();
-                db.Names.back() = db.make<MemberExpr>(
-                    db.Names.back(), "->", tmp);
-                first = t1;
-            }
-        }
-    }
-    return first;
-}
-
-//  <ref-qualifier> ::= R                   # & ref-qualifier
-//  <ref-qualifier> ::= O                   # && ref-qualifier
-
-// <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E
-
-const char*
-parse_function_type(const char* first, const char* last, Db& db)
-{
-    if (first != last && *first == 'F')
-    {
-        const char* t = first+1;
-        if (t != last)
-        {
-            if (*t == 'Y')
-            {
-                /* extern "C" */
-                if (++t == last)
-                    return first;
-            }
-            const char* t1 = parse_type(t, last, db);
-            if (t1 != t && !db.Names.empty())
-            {
-                Node* ret_type = db.Names.back();
-                db.Names.pop_back();
-                size_t params_begin = db.Names.size();
-                t = t1;
-                FunctionRefQual RefQuals = FrefQualNone;
-                while (true)
-                {
-                    if (t == last)
-                    {
-                        if (!db.Names.empty())
-                          db.Names.pop_back();
-                        return first;
-                    }
-                    if (*t == 'E')
-                    {
-                        ++t;
-                        break;
-                    }
-                    if (*t == 'v')
-                    {
-                        ++t;
-                        continue;
-                    }
-                    if (*t == 'R' && t+1 != last && t[1] == 'E')
-                    {
-                        RefQuals = FrefQualLValue;
-                        ++t;
-                        continue;
-                    }
-                    if (*t == 'O' && t+1 != last && t[1] == 'E')
-                    {
-                        RefQuals = FrefQualRValue;
-                        ++t;
-                        continue;
-                    }
-                    size_t k0 = db.Names.size();
-                    t1 = parse_type(t, last, db);
-                    size_t k1 = db.Names.size();
-                    if (t1 == t || t1 == last || k1 < k0)
-                        return first;
-                    t = t1;
-                }
-                if (db.Names.empty() || params_begin > db.Names.size())
-                    return first;
-                Node* fty = db.make<FunctionType>(
-                    ret_type, db.popTrailingNodeArray(params_begin));
-                if (RefQuals)
-                    fty = db.make<FunctionRefQualType>(fty, RefQuals);
-                db.Names.push_back(fty);
-                first = t;
-            }
-        }
-    }
-    return first;
-}
-
-// <pointer-to-member-type> ::= M <class type> <member type>
-
-const char*
-parse_pointer_to_member_type(const char* first, const char* last, Db& db)
-{
-    if (first != last && *first == 'M')
-    {
-        const char* t = parse_type(first+1, last, db);
-        if (t != first+1)
-        {
-            const char* t2 = parse_type(t, last, db);
-            if (t2 != t)
-            {
-                if (db.Names.size() < 2)
-                    return first;
-                auto func = std::move(db.Names.back());
-                db.Names.pop_back();
-                auto ClassType = std::move(db.Names.back());
-                db.Names.back() =
-                    db.make<PointerToMemberType>(ClassType, func);
-                first = t2;
-            }
-        }
-    }
-    return first;
-}
-
-// <array-type> ::= A <positive dimension number> _ <element type>
-//              ::= A [<dimension expression>] _ <element type>
-
-const char*
-parse_array_type(const char* first, const char* last, Db& db)
-{
-    if (first != last && *first == 'A' && first+1 != last)
-    {
-        if (first[1] == '_')
-        {
-            const char* t = parse_type(first+2, last, db);
-            if (t != first+2)
-            {
-                if (db.Names.empty())
-                    return first;
-                db.Names.back() = db.make<ArrayType>(db.Names.back());
-                first = t;
-            }
-        }
-        else if ('1' <= first[1] && first[1] <= '9')
-        {
-            const char* t = parse_number(first+1, last);
-            if (t != last && *t == '_')
-            {
-                const char* t2 = parse_type(t+1, last, db);
-                if (t2 != t+1)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<ArrayType>(db.Names.back(),
-                                            StringView(first + 1, t));
-                    first = t2;
-                }
-            }
-        }
-        else
-        {
-            const char* t = parse_expression(first+1, last, db);
-            if (t != first+1 && t != last && *t == '_')
-            {
-                const char* t2 = parse_type(++t, last, db);
-                if (t2 != t)
-                {
-                    if (db.Names.size() < 2)
-                        return first;
-                    auto base_type = std::move(db.Names.back());
-                    db.Names.pop_back();
-                    auto dimension_expr = std::move(db.Names.back());
-                    db.Names.back() =
-                        db.make<ArrayType>(base_type, dimension_expr);
-                    first = t2;
-                }
-            }
-        }
-    }
-    return first;
-}
-
-// <decltype>  ::= Dt <expression> E  # decltype of an id-expression or class member access (C++0x)
-//             ::= DT <expression> E  # decltype of an expression (C++0x)
-
-const char*
-parse_decltype(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 4 && first[0] == 'D')
-    {
-        switch (first[1])
-        {
-        case 't':
-        case 'T':
-            {
-                const char* t = parse_expression(first+2, last, db);
-                if (t != first+2 && t != last && *t == 'E')
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() = db.make<EnclosingExpr>(
-                        "decltype(", db.Names.back(), ")");
-                    first = t+1;
-                }
-            }
-            break;
-        }
-    }
-    return first;
-}
-
-// extension:
-// <vector-type>           ::= Dv <positive dimension number> _
-//                                    <extended element type>
-//                         ::= Dv [<dimension expression>] _ <element type>
-// <extended element type> ::= <element type>
-//                         ::= p # AltiVec vector pixel
-
-const char*
-parse_vector_type(const char* first, const char* last, Db& db)
-{
-    if (last - first > 3 && first[0] == 'D' && first[1] == 'v')
-    {
-        if ('1' <= first[2] && first[2] <= '9')
-        {
-            const char* t = parse_number(first+2, last);
-            if (t == last || *t != '_')
-                return first;
-            const char* num = first + 2;
-            size_t sz = static_cast<size_t>(t - num);
-            if (++t != last)
-            {
-                if (*t != 'p')
-                {
-                    const char* t1 = parse_type(t, last, db);
-                    if (t1 != t)
-                    {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<VectorType>(db.Names.back(),
-                                                 StringView(num, num + sz));
-                        first = t1;
-                    }
-                }
-                else
-                {
-                    ++t;
-                    db.Names.push_back(
-                        db.make<VectorType>(StringView(num, num + sz)));
-                    first = t;
-                }
-            }
-        }
-        else
-        {
-            Node* num = nullptr;
-            const char* t1 = first+2;
-            if (*t1 != '_')
-            {
-                const char* t = parse_expression(t1, last, db);
-                if (t != t1)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    num = db.Names.back();
-                    db.Names.pop_back();
-                    t1 = t;
-                }
-            }
-            if (t1 != last && *t1 == '_' && ++t1 != last)
-            {
-                const char* t = parse_type(t1, last, db);
-                if (t != t1)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    if (num)
-                        db.Names.back() =
-                            db.make<VectorType>(db.Names.back(), num);
-                    else
-                        db.Names.back() =
-                            db.make<VectorType>(db.Names.back(), StringView());
-                    first = t;
-                } else if (num)
-                    db.Names.push_back(num);
-            }
-        }
-    }
-    return first;
-}
-
-// <type> ::= <builtin-type>
-//        ::= <function-type>
-//        ::= <class-enum-type>
-//        ::= <array-type>
-//        ::= <pointer-to-member-type>
-//        ::= <template-param>
-//        ::= <template-template-param> <template-args>
-//        ::= <decltype>
-//        ::= <substitution>
-//        ::= <CV-Qualifiers> <type>
-//        ::= P <type>        # pointer-to
-//        ::= R <type>        # reference-to
-//        ::= O <type>        # rvalue reference-to (C++0x)
-//        ::= C <type>        # complex pair (C 2000)
-//        ::= G <type>        # imaginary (C 2000)
-//        ::= Dp <type>       # pack expansion (C++0x)
-//        ::= U <source-name> <type>  # vendor extended type qualifier
-// extension := U <objc-name> <objc-type>  # objc-type<identifier>
-// extension := <vector-type> # <vector-type> starts with Dv
-
-// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier>  # k0 = 9 + <number of digits in k1> + k1
-// <objc-type> := <source-name>  # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name>
-
-const char*
-parse_type(const char* first, const char* last, Db& db)
-{
-    if (first != last)
-    {
-        switch (*first)
-        {
-            case 'r':
-            case 'V':
-            case 'K':
-              {
-                Qualifiers cv = QualNone;
-                const char* t = parse_cv_qualifiers(first, last, cv);
-                if (t != first)
-                {
-                    bool is_function = *t == 'F';
-                    size_t k0 = db.Names.size();
-                    const char* t1 = parse_type(t, last, db);
-                    size_t k1 = db.Names.size();
-                    if (t1 != t && k0 + 1 == k1)
-                    {
-                        if (is_function)
-                            db.Subs.pop_back();
-                        if (cv)
-                        {
-                            if (is_function)
-                                db.Names.back() = db.make<FunctionQualType>(
-                                    db.Names.back(), cv);
-                            else
-                                db.Names.back() =
-                                    db.make<QualType>(db.Names.back(), cv);
-                        }
-                        db.Subs.push_back(db.Names.back());
-                        first = t1;
-                    }
-                }
-              }
-                break;
-            default:
-              {
-                const char* t = parse_builtin_type(first, last, db);
-                if (t != first)
-                {
-                    first = t;
-                }
-                else
-                {
-                    switch (*first)
-                    {
-                    case 'A':
-                        t = parse_array_type(first, last, db);
-                        if (t != first)
-                        {
-                            if (db.Names.empty())
-                                return first;
-                            first = t;
-                            db.Subs.push_back(db.Names.back());
-                        }
-                        break;
-                    case 'C':
-                        t = parse_type(first+1, last, db);
-                        if (t != first+1)
-                        {
-                            if (db.Names.empty())
-                                return first;
-                            db.Names.back() = db.make<PostfixQualifiedType>(
-                                db.Names.back(), " complex");
-                            first = t;
-                            db.Subs.push_back(db.Names.back());
-                        }
-                        break;
-                    case 'F':
-                        t = parse_function_type(first, last, db);
-                        if (t != first)
-                        {
-                            if (db.Names.empty())
-                                return first;
-                            first = t;
-                            db.Subs.push_back(db.Names.back());
-                        }
-                        break;
-                    case 'G':
-                        t = parse_type(first+1, last, db);
-                        if (t != first+1)
-                        {
-                            if (db.Names.empty())
-                                return first;
-                            db.Names.back() = db.make<PostfixQualifiedType>(
-                                db.Names.back(), " imaginary");
-                            first = t;
-                            db.Subs.push_back(db.Names.back());
-                        }
-                        break;
-                    case 'M':
-                        t = parse_pointer_to_member_type(first, last, db);
-                        if (t != first)
-                        {
-                            if (db.Names.empty())
-                                return first;
-                            first = t;
-                            db.Subs.push_back(db.Names.back());
-                        }
-                        break;
-                    case 'O':
-                      {
-                        size_t k0 = db.Names.size();
-                        t = parse_type(first+1, last, db);
-                        size_t k1 = db.Names.size();
-                        if (t != first+1 && k0 + 1 == k1)
-                        {
-                            db.Names.back() =
-                                db.make<RValueReferenceType>(db.Names.back());
-                            db.Subs.push_back(db.Names.back());
-                            first = t;
-                        }
-                        break;
-                      }
-                    case 'P':
-                      {
-                        size_t k0 = db.Names.size();
-                        t = parse_type(first+1, last, db);
-                        size_t k1 = db.Names.size();
-                        if (t != first+1 && k0 + 1 == k1)
-                        {
-                            db.Names.back() = db.make<PointerType>(db.Names.back());
-                            db.Subs.push_back(db.Names.back());
-                            first = t;
-                        }
-                        break;
-                      }
-                    case 'R':
-                      {
-                        size_t k0 = db.Names.size();
-                        t = parse_type(first+1, last, db);
-                        size_t k1 = db.Names.size();
-                        if (t != first+1 && k0 + 1 == k1)
-                        {
-                            db.Names.back() =
-                                db.make<LValueReferenceType>(db.Names.back());
-                            db.Subs.push_back(db.Names.back());
-                            first = t;
-                        }
-                        break;
-                      }
-                    case 'T':
-                      {
-                        size_t k0 = db.Names.size();
-                        t = parse_template_param(first, last, db);
-                        size_t k1 = db.Names.size();
-                        if (t != first && k0 + 1 == k1)
-                        {
-                            db.Subs.push_back(db.Names.back());
-                            if (db.TryToParseTemplateArgs && k1 == k0+1)
-                            {
-                                const char* t1 = parse_template_args(t, last, db);
-                                if (t1 != t)
-                                {
-                                    auto args = db.Names.back();
-                                    db.Names.pop_back();
-                                    db.Names.back() = db.make<
-                                        NameWithTemplateArgs>(
-                                        db.Names.back(), args);
-                                    db.Subs.push_back(db.Names.back());
-                                    t = t1;
-                                }
-                            }
-                            first = t;
-                        }
-                        break;
-                      }
-                    case 'U':
-                        if (first+1 != last)
-                        {
-                            t = parse_source_name(first+1, last, db);
-                            if (t != first+1)
-                            {
-                                const char* t2 = parse_type(t, last, db);
-                                if (t2 != t)
-                                {
-                                    if (db.Names.size() < 2)
-                                        return first;
-                                    auto type = db.Names.back();
-                                    db.Names.pop_back();
-                                    if (db.Names.back()->K != Node::KNameType ||
-                                        !static_cast<NameType*>(db.Names.back())->getName().startsWith("objcproto"))
-                                    {
-                                        db.Names.back() = db.make<VendorExtQualType>(type, db.Names.back());
-                                    }
-                                    else
-                                    {
-                                        auto* proto = static_cast<NameType*>(db.Names.back());
-                                        db.Names.pop_back();
-                                        t = parse_source_name(proto->getName().begin() + 9, proto->getName().end(), db);
-                                        if (t != proto->getName().begin() + 9)
-                                        {
-                                            db.Names.back() = db.make<ObjCProtoName>(type, db.Names.back());
-                                        }
-                                        else
-                                        {
-                                            db.Names.push_back(db.make<VendorExtQualType>(type, proto));
-                                        }
-                                    }
-                                    db.Subs.push_back(db.Names.back());
-                                    first = t2;
-                                }
-                            }
-                        }
-                        break;
-                    case 'S':
-                        if (first+1 != last && first[1] == 't')
-                        {
-                            t = parse_name(first, last, db);
-                            if (t != first)
-                            {
-                                if (db.Names.empty())
-                                    return first;
-                                db.Subs.push_back(db.Names.back());
-                                first = t;
-                            }
-                        }
-                        else
-                        {
-                            t = parse_substitution(first, last, db);
-                            if (t != first)
-                            {
-                                first = t;
-                                // Parsed a substitution.  If the substitution is a
-                                //  <template-param> it might be followed by <template-args>.
-                                if (db.TryToParseTemplateArgs)
-                                {
-                                    t = parse_template_args(first, last, db);
-                                    if (t != first)
-                                    {
-                                        if (db.Names.size() < 2)
-                                            return first;
-                                        auto template_args = db.Names.back();
-                                        db.Names.pop_back();
-                                        db.Names.back() = db.make<
-                                          NameWithTemplateArgs>(
-                                              db.Names.back(), template_args);
-                                        // Need to create substitution for <template-template-param> <template-args>
-                                        db.Subs.push_back(db.Names.back());
-                                        first = t;
-                                    }
-                                }
-                            }
-                        }
-                        break;
-                    case 'D':
-                        if (first+1 != last)
-                        {
-                            switch (first[1])
-                            {
-                            case 'p':
-                              {
-                                size_t k0 = db.Names.size();
-                                t = parse_type(first+2, last, db);
-                                size_t k1 = db.Names.size();
-                                if (t != first+2 && k0 + 1 == k1)
-                                {
-                                    db.Names.back() =
-                                        db.make<ParameterPackExpansion>(
-                                            db.Names.back());
-                                    db.Subs.push_back(db.Names.back());
-                                    first = t;
-                                    return first;
-                                }
-                                break;
-                              }
-                            case 't':
-                            case 'T':
-                                t = parse_decltype(first, last, db);
-                                if (t != first)
-                                {
-                                    if (db.Names.empty())
-                                        return first;
-                                    db.Subs.push_back(db.Names.back());
-                                    first = t;
-                                    return first;
-                                }
-                                break;
-                            case 'v':
-                                t = parse_vector_type(first, last, db);
-                                if (t != first)
-                                {
-                                    if (db.Names.empty())
-                                        return first;
-                                    db.Subs.push_back(db.Names.back());
-                                    first = t;
-                                    return first;
-                                }
-                                break;
-                            }
-                        }
-                        _LIBCPP_FALLTHROUGH();
-                    default:
-                        // must check for builtin-types before class-enum-types to avoid
-                        // ambiguities with operator-names
-                        t = parse_builtin_type(first, last, db);
-                        if (t != first)
-                        {
-                            first = t;
-                        }
-                        else
-                        {
-                            t = parse_name(first, last, db);
-                            if (t != first)
-                            {
-                                if (db.Names.empty())
-                                    return first;
-                                db.Subs.push_back(db.Names.back());
-                                first = t;
-                            }
-                        }
-                        break;
-                    }
-              }
-                break;
-            }
-        }
-    }
-    return first;
-}
-
-//   <operator-name>
-//                   ::= aa    # &&            
-//                   ::= ad    # & (unary)     
-//                   ::= an    # &             
-//                   ::= aN    # &=            
-//                   ::= aS    # =             
-//                   ::= cl    # ()            
-//                   ::= cm    # ,             
-//                   ::= co    # ~             
-//                   ::= cv <type>    # (cast)        
-//                   ::= da    # delete[]      
-//                   ::= de    # * (unary)     
-//                   ::= dl    # delete        
-//                   ::= dv    # /             
-//                   ::= dV    # /=            
-//                   ::= eo    # ^             
-//                   ::= eO    # ^=            
-//                   ::= eq    # ==            
-//                   ::= ge    # >=            
-//                   ::= gt    # >             
-//                   ::= ix    # []            
-//                   ::= le    # <=            
-//                   ::= li <source-name>  # operator ""
-//                   ::= ls    # <<            
-//                   ::= lS    # <<=           
-//                   ::= lt    # <             
-//                   ::= mi    # -             
-//                   ::= mI    # -=            
-//                   ::= ml    # *             
-//                   ::= mL    # *=            
-//                   ::= mm    # -- (postfix in <expression> context)           
-//                   ::= na    # new[]
-//                   ::= ne    # !=            
-//                   ::= ng    # - (unary)     
-//                   ::= nt    # !             
-//                   ::= nw    # new           
-//                   ::= oo    # ||            
-//                   ::= or    # |             
-//                   ::= oR    # |=            
-//                   ::= pm    # ->*           
-//                   ::= pl    # +             
-//                   ::= pL    # +=            
-//                   ::= pp    # ++ (postfix in <expression> context)
-//                   ::= ps    # + (unary)
-//                   ::= pt    # ->            
-//                   ::= qu    # ?             
-//                   ::= rm    # %             
-//                   ::= rM    # %=            
-//                   ::= rs    # >>            
-//                   ::= rS    # >>=           
-//                   ::= v <digit> <source-name>        # vendor extended operator
-//   extension       ::= <operator-name> <abi-tag-seq>
-const char*
-parse_operator_name(const char* first, const char* last, Db& db)
-{
-    const char* original_first = first;
-    if (last - first >= 2)
-    {
-        switch (first[0])
-        {
-        case 'a':
-            switch (first[1])
-            {
-            case 'a':
-                db.Names.push_back(db.make<NameType>("operator&&"));
-                first += 2;
-                break;
-            case 'd':
-            case 'n':
-                db.Names.push_back(db.make<NameType>("operator&"));
-                first += 2;
-                break;
-            case 'N':
-                db.Names.push_back(db.make<NameType>("operator&="));
-                first += 2;
-                break;
-            case 'S':
-                db.Names.push_back(db.make<NameType>("operator="));
-                first += 2;
-                break;
-            }
-            break;
-        case 'c':
-            switch (first[1])
-            {
-            case 'l':
-                db.Names.push_back(db.make<NameType>("operator()"));
-                first += 2;
-                break;
-            case 'm':
-                db.Names.push_back(db.make<NameType>("operator,"));
-                first += 2;
-                break;
-            case 'o':
-                db.Names.push_back(db.make<NameType>("operator~"));
-                first += 2;
-                break;
-            case 'v':
-                {
-                    bool TryToParseTemplateArgs = db.TryToParseTemplateArgs;
-                    db.TryToParseTemplateArgs = false;
-                    const char* t = parse_type(first+2, last, db);
-                    db.TryToParseTemplateArgs = TryToParseTemplateArgs;
-                    if (t != first+2)
-                    {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<ConversionOperatorType>(db.Names.back());
-                        db.ParsedCtorDtorCV = true;
-                        first = t;
-                    }
-                }
-                break;
-            }
-            break;
-        case 'd':
-            switch (first[1])
-            {
-            case 'a':
-                db.Names.push_back(db.make<NameType>("operator delete[]"));
-                first += 2;
-                break;
-            case 'e':
-                db.Names.push_back(db.make<NameType>("operator*"));
-                first += 2;
-                break;
-            case 'l':
-                db.Names.push_back(db.make<NameType>("operator delete"));
-                first += 2;
-                break;
-            case 'v':
-                db.Names.push_back(db.make<NameType>("operator/"));
-                first += 2;
-                break;
-            case 'V':
-                db.Names.push_back(db.make<NameType>("operator/="));
-                first += 2;
-                break;
-            }
-            break;
-        case 'e':
-            switch (first[1])
-            {
-            case 'o':
-                db.Names.push_back(db.make<NameType>("operator^"));
-                first += 2;
-                break;
-            case 'O':
-                db.Names.push_back(db.make<NameType>("operator^="));
-                first += 2;
-                break;
-            case 'q':
-                db.Names.push_back(db.make<NameType>("operator=="));
-                first += 2;
-                break;
-            }
-            break;
-        case 'g':
-            switch (first[1])
-            {
-            case 'e':
-                db.Names.push_back(db.make<NameType>("operator>="));
-                first += 2;
-                break;
-            case 't':
-                db.Names.push_back(db.make<NameType>("operator>"));
-                first += 2;
-                break;
-            }
-            break;
-        case 'i':
-            if (first[1] == 'x')
-            {
-                db.Names.push_back(db.make<NameType>("operator[]"));
-                first += 2;
-            }
-            break;
-        case 'l':
-            switch (first[1])
-            {
-            case 'e':
-                db.Names.push_back(db.make<NameType>("operator<="));
-                first += 2;
-                break;
-            case 'i':
-                {
-                    const char* t = parse_source_name(first+2, last, db);
-                    if (t != first+2)
-                    {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<LiteralOperator>(db.Names.back());
-                        first = t;
-                    }
-                }
-                break;
-            case 's':
-                db.Names.push_back(db.make<NameType>("operator<<"));
-                first += 2;
-                break;
-            case 'S':
-                db.Names.push_back(db.make<NameType>("operator<<="));
-                first += 2;
-                break;
-            case 't':
-                db.Names.push_back(db.make<NameType>("operator<"));
-                first += 2;
-                break;
-            }
-            break;
-        case 'm':
-            switch (first[1])
-            {
-            case 'i':
-                db.Names.push_back(db.make<NameType>("operator-"));
-                first += 2;
-                break;
-            case 'I':
-                db.Names.push_back(db.make<NameType>("operator-="));
-                first += 2;
-                break;
-            case 'l':
-                db.Names.push_back(db.make<NameType>("operator*"));
-                first += 2;
-                break;
-            case 'L':
-                db.Names.push_back(db.make<NameType>("operator*="));
-                first += 2;
-                break;
-            case 'm':
-                db.Names.push_back(db.make<NameType>("operator--"));
-                first += 2;
-                break;
-            }
-            break;
-        case 'n':
-            switch (first[1])
-            {
-            case 'a':
-                db.Names.push_back(db.make<NameType>("operator new[]"));
-                first += 2;
-                break;
-            case 'e':
-                db.Names.push_back(db.make<NameType>("operator!="));
-                first += 2;
-                break;
-            case 'g':
-                db.Names.push_back(db.make<NameType>("operator-"));
-                first += 2;
-                break;
-            case 't':
-                db.Names.push_back(db.make<NameType>("operator!"));
-                first += 2;
-                break;
-            case 'w':
-                db.Names.push_back(db.make<NameType>("operator new"));
-                first += 2;
-                break;
-            }
-            break;
-        case 'o':
-            switch (first[1])
-            {
-            case 'o':
-                db.Names.push_back(db.make<NameType>("operator||"));
-                first += 2;
-                break;
-            case 'r':
-                db.Names.push_back(db.make<NameType>("operator|"));
-                first += 2;
-                break;
-            case 'R':
-                db.Names.push_back(db.make<NameType>("operator|="));
-                first += 2;
-                break;
-            }
-            break;
-        case 'p':
-            switch (first[1])
-            {
-            case 'm':
-                db.Names.push_back(db.make<NameType>("operator->*"));
-                first += 2;
-                break;
-            case 'l':
-                db.Names.push_back(db.make<NameType>("operator+"));
-                first += 2;
-                break;
-            case 'L':
-                db.Names.push_back(db.make<NameType>("operator+="));
-                first += 2;
-                break;
-            case 'p':
-                db.Names.push_back(db.make<NameType>("operator++"));
-                first += 2;
-                break;
-            case 's':
-                db.Names.push_back(db.make<NameType>("operator+"));
-                first += 2;
-                break;
-            case 't':
-                db.Names.push_back(db.make<NameType>("operator->"));
-                first += 2;
-                break;
-            }
-            break;
-        case 'q':
-            if (first[1] == 'u')
-            {
-                db.Names.push_back(db.make<NameType>("operator?"));
-                first += 2;
-            }
-            break;
-        case 'r':
-            switch (first[1])
-            {
-            case 'm':
-                db.Names.push_back(db.make<NameType>("operator%"));
-                first += 2;
-                break;
-            case 'M':
-                db.Names.push_back(db.make<NameType>("operator%="));
-                first += 2;
-                break;
-            case 's':
-                db.Names.push_back(db.make<NameType>("operator>>"));
-                first += 2;
-                break;
-            case 'S':
-                db.Names.push_back(db.make<NameType>("operator>>="));
-                first += 2;
-                break;
-            }
-            break;
-        case 'v':
-            if (std::isdigit(first[1]))
-            {
-                const char* t = parse_source_name(first+2, last, db);
-                if (t != first+2)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<ConversionOperatorType>(db.Names.back());
-                    first = t;
-                }
-            }
-            break;
-        }
-    }
-
-    if (original_first != first)
-        first = parse_abi_tag_seq(first, last, db);
-
-    return first;
-}
-
-const char*
-parse_integer_literal(const char* first, const char* last, StringView lit, Db& db)
-{
-    const char* t = parse_number(first, last);
-    if (t != first && t != last && *t == 'E')
-    {
-        db.Names.push_back(
-            db.make<IntegerExpr>(lit, StringView(first, t)));
-        first = t+1;
-    }
-    return first;
-}
-
-// <expr-primary> ::= L <type> <value number> E                          # integer literal
-//                ::= L <type> <value float> E                           # floating literal
-//                ::= L <string type> E                                  # string literal
-//                ::= L <nullptr type> E                                 # nullptr literal (i.e., "LDnE")
-//                ::= L <type> <real-part float> _ <imag-part float> E   # complex floating point literal (C 2000)
-//                ::= L <mangled-name> E                                 # external name
-
-const char*
-parse_expr_primary(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 4 && *first == 'L')
-    {
-        switch (first[1])
-        {
-        case 'w':
-            {
-            const char* t = parse_integer_literal(first+2, last, "wchar_t", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'b':
-            if (first[3] == 'E')
-            {
-                switch (first[2])
-                {
-                case '0':
-                    db.Names.push_back(db.make<BoolExpr>(0));
-                    first += 4;
-                    break;
-                case '1':
-                    db.Names.push_back(db.make<BoolExpr>(1));
-                    first += 4;
-                    break;
-                }
-            }
-            break;
-        case 'c':
-            {
-            const char* t = parse_integer_literal(first+2, last, "char", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'a':
-            {
-            const char* t = parse_integer_literal(first+2, last, "signed char", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'h':
-            {
-            const char* t = parse_integer_literal(first+2, last, "unsigned char", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 's':
-            {
-            const char* t = parse_integer_literal(first+2, last, "short", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 't':
-            {
-            const char* t = parse_integer_literal(first+2, last, "unsigned short", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'i':
-            {
-            const char* t = parse_integer_literal(first+2, last, "", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'j':
-            {
-            const char* t = parse_integer_literal(first+2, last, "u", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'l':
-            {
-            const char* t = parse_integer_literal(first+2, last, "l", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'm':
-            {
-            const char* t = parse_integer_literal(first+2, last, "ul", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'x':
-            {
-            const char* t = parse_integer_literal(first+2, last, "ll", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'y':
-            {
-            const char* t = parse_integer_literal(first+2, last, "ull", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'n':
-            {
-            const char* t = parse_integer_literal(first+2, last, "__int128", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'o':
-            {
-            const char* t = parse_integer_literal(first+2, last, "unsigned __int128", db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'f':
-            {
-            const char* t = parse_floating_number<float>(first+2, last, db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case 'd':
-            {
-            const char* t = parse_floating_number<double>(first+2, last, db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-         case 'e':
-            {
-            const char* t = parse_floating_number<long double>(first+2, last, db);
-            if (t != first+2)
-                first = t;
-            }
-            break;
-        case '_':
-            if (first[2] == 'Z')
-            {
-                const char* t = parse_encoding(first+3, last, db);
-                if (t != first+3 && t != last && *t == 'E')
-                    first = t+1;
-            }
-            break;
-        case 'T':
-            // Invalid mangled name per
-            //   http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html
-            break;
-        default:
-            {
-                // might be named type
-                const char* t = parse_type(first+1, last, db);
-                if (t != first+1 && t != last)
-                {
-                    if (*t != 'E')
-                    {
-                        const char* n = t;
-                        for (; n != last && isdigit(*n); ++n)
-                            ;
-                        if (n != t && n != last && *n == 'E')
+                    case 'A':
+                        t = parse_array_type(first, last, db);
+                        if (t != first)
                         {
                             if (db.Names.empty())
                                 return first;
-                            db.Names.back() = db.make<IntegerCastExpr>(
-                                db.Names.back(), StringView(t, n));
-                            first = n+1;
-                            break;
+                            first = t;
+                            db.Subs.push_back(db.Names.back());
                         }
-                    }
-                    else
-                    {
-                        first = t+1;
                         break;
-                    }
-                }
-            }
-        }
-    }
-    return first;
-}
-
-Node* maybe_change_special_sub_name(Node* inp, Db& db)
-{
-    if (inp->K != Node::KSpecialSubstitution)
-        return inp;
-    auto Kind = static_cast<SpecialSubstitution*>(inp)->SSK;
-    switch (Kind)
-    {
-    case SpecialSubKind::string:
-    case SpecialSubKind::istream:
-    case SpecialSubKind::ostream:
-    case SpecialSubKind::iostream:
-        return db.make<ExpandedSpecialSubstitution>(Kind);
-    default:
-        break;
-    }
-    return inp;
-}
-
-// <ctor-dtor-name> ::= C1    # complete object constructor
-//                  ::= C2    # base object constructor
-//                  ::= C3    # complete object allocating constructor
-//   extension      ::= C5    # ?
-//                  ::= D0    # deleting destructor
-//                  ::= D1    # complete object destructor
-//                  ::= D2    # base object destructor
-//   extension      ::= D5    # ?
-//   extension      ::= <ctor-dtor-name> <abi-tag-seq>
-const char*
-parse_ctor_dtor_name(const char* first, const char* last, Db& db)
-{
-    if (last-first >= 2 && !db.Names.empty())
-    {
-        switch (first[0])
-        {
-        case 'C':
-            switch (first[1])
-            {
-            case '1':
-            case '2':
-            case '3':
-            case '5':
-                if (db.Names.empty())
-                    return first;
-                db.Names.back() =
-                    maybe_change_special_sub_name(db.Names.back(), db);
-                db.Names.push_back(
-                    db.make<CtorDtorName>(db.Names.back(), false));
-                first += 2;
-                first = parse_abi_tag_seq(first, last, db);
-                db.ParsedCtorDtorCV = true;
-                break;
-            }
-            break;
-        case 'D':
-            switch (first[1])
-            {
-            case '0':
-            case '1':
-            case '2':
-            case '5':
-                if (db.Names.empty())
-                    return first;
-                db.Names.push_back(
-                    db.make<CtorDtorName>(db.Names.back(), true));
-                first += 2;
-                first = parse_abi_tag_seq(first, last, db);
-                db.ParsedCtorDtorCV = true;
-                break;
-            }
-            break;
-        }
-    }
-    return first;
-}
-
-// <unnamed-type-name> ::= Ut [<nonnegative number>] _ [<abi-tag-seq>]
-//                     ::= <closure-type-name>
-// 
-// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ 
-// 
-// <lambda-sig> ::= <parameter type>+  # Parameter types or "v" if the lambda has no parameters
-const char*
-parse_unnamed_type_name(const char* first, const char* last, Db& db)
-{
-    if (last - first > 2 && first[0] == 'U')
-    {
-        char type = first[1];
-        switch (type)
-        {
-        case 't':
-          {
-            const char* t0 = first+2;
-            if (t0 == last)
-                return first;
-            StringView count;
-            if (std::isdigit(*t0))
-            {
-                const char* t1 = t0 + 1;
-                while (t1 != last && std::isdigit(*t1))
-                    ++t1;
-                count = StringView(t0, t1);
-                t0 = t1;
-            }
-            if (t0 == last || *t0 != '_')
-                return first;
-            db.Names.push_back(db.make<UnnamedTypeName>(count));
-            first = t0 + 1;
-            first = parse_abi_tag_seq(first, last, db);
-          }
-            break;
-        case 'l':
-          {
-            size_t begin_pos = db.Names.size();
-            const char* t0 = first+2;
-            NodeArray lambda_params;
-            if (first[2] == 'v')
-            {
-                ++t0;
-            }
-            else
-            {
-                while (true)
-                {
-                    const char* t1 = parse_type(t0, last, db);
-                    if (t1 == t0)
+                    case 'C':
+                        t = parse_type(first+1, last, db);
+                        if (t != first+1)
+                        {
+                            if (db.Names.empty())
+                                return first;
+                            db.Names.back() = db.make<PostfixQualifiedType>(
+                                db.Names.back(), " complex");
+                            first = t;
+                            db.Subs.push_back(db.Names.back());
+                        }
+                        break;
+                    case 'F':
+                        t = parse_function_type(first, last, db);
+                        if (t != first)
+                        {
+                            if (db.Names.empty())
+                                return first;
+                            first = t;
+                            db.Subs.push_back(db.Names.back());
+                        }
+                        break;
+                    case 'G':
+                        t = parse_type(first+1, last, db);
+                        if (t != first+1)
+                        {
+                            if (db.Names.empty())
+                                return first;
+                            db.Names.back() = db.make<PostfixQualifiedType>(
+                                db.Names.back(), " imaginary");
+                            first = t;
+                            db.Subs.push_back(db.Names.back());
+                        }
+                        break;
+                    case 'M':
+                        t = parse_pointer_to_member_type(first, last, db);
+                        if (t != first)
+                        {
+                            if (db.Names.empty())
+                                return first;
+                            first = t;
+                            db.Subs.push_back(db.Names.back());
+                        }
+                        break;
+                    case 'O':
+                      {
+                        size_t k0 = db.Names.size();
+                        t = parse_type(first+1, last, db);
+                        size_t k1 = db.Names.size();
+                        if (t != first+1 && k0 + 1 == k1)
+                        {
+                            db.Names.back() =
+                                db.make<RValueReferenceType>(db.Names.back());
+                            db.Subs.push_back(db.Names.back());
+                            first = t;
+                        }
+                        break;
+                      }
+                    case 'P':
+                      {
+                        size_t k0 = db.Names.size();
+                        t = parse_type(first+1, last, db);
+                        size_t k1 = db.Names.size();
+                        if (t != first+1 && k0 + 1 == k1)
+                        {
+                            db.Names.back() = db.make<PointerType>(db.Names.back());
+                            db.Subs.push_back(db.Names.back());
+                            first = t;
+                        }
+                        break;
+                      }
+                    case 'R':
+                      {
+                        size_t k0 = db.Names.size();
+                        t = parse_type(first+1, last, db);
+                        size_t k1 = db.Names.size();
+                        if (t != first+1 && k0 + 1 == k1)
+                        {
+                            db.Names.back() =
+                                db.make<LValueReferenceType>(db.Names.back());
+                            db.Subs.push_back(db.Names.back());
+                            first = t;
+                        }
+                        break;
+                      }
+                    case 'T':
+                      {
+                        size_t k0 = db.Names.size();
+                        t = parse_template_param(first, last, db);
+                        size_t k1 = db.Names.size();
+                        if (t != first && k0 + 1 == k1)
+                        {
+                            db.Subs.push_back(db.Names.back());
+                            if (db.TryToParseTemplateArgs && k1 == k0+1)
+                            {
+                                const char* t1 = parse_template_args(t, last, db);
+                                if (t1 != t)
+                                {
+                                    auto args = db.Names.back();
+                                    db.Names.pop_back();
+                                    db.Names.back() = db.make<
+                                        NameWithTemplateArgs>(
+                                        db.Names.back(), args);
+                                    db.Subs.push_back(db.Names.back());
+                                    t = t1;
+                                }
+                            }
+                            first = t;
+                        }
                         break;
-                    t0 = t1;
-                }
-                if (db.Names.size() < begin_pos)
-                    return first;
-                lambda_params = db.popTrailingNodeArray(begin_pos);
-            }
-            if (t0 == last || *t0 != 'E')
-                return first;
-            ++t0;
-            if (t0 == last)
-                return first;
-            StringView count;
-            if (std::isdigit(*t0))
-            {
-                const char* t1 = t0 + 1;
-                while (t1 != last && std::isdigit(*t1))
-                    ++t1;
-                count = StringView(t0, t1);
-                t0 = t1;
-            }
-            if (t0 == last || *t0 != '_')
-                return first;
-            db.Names.push_back(db.make<LambdaTypeName>(lambda_params, count));
-            first = t0 + 1;
-          }
-            break;
-        }
-    }
-    return first;
-}
-
-// <unqualified-name> ::= <operator-name>
-//                    ::= <ctor-dtor-name>
-//                    ::= <source-name>   
-//                    ::= <unnamed-type-name>
-
-const char*
-parse_unqualified_name(const char* first, const char* last, Db& db)
-{
-    if (first != last)
-    {
-        const char* t;
-        switch (*first)
-        {
-        case 'C':
-        case 'D':
-            t = parse_ctor_dtor_name(first, last, db);
-            if (t != first)
-                first = t;
-            break;
-        case 'U':
-            t = parse_unnamed_type_name(first, last, db);
-            if (t != first)
-                first = t;
-            break;
-        case '1':
-        case '2':
-        case '3':
-        case '4':
-        case '5':
-        case '6':
-        case '7':
-        case '8':
-        case '9':
-            t = parse_source_name(first, last, db);
-            if (t != first)
-                first = t;
-            break;
-        default:
-            t = parse_operator_name(first, last, db);
-            if (t != first)
-                first = t;
-            break;
-        };
-    }
-    return first;
-}
-
-// <unscoped-name> ::= <unqualified-name>
-//                 ::= St <unqualified-name>   # ::std::
-// extension       ::= StL<unqualified-name>
-
-const char*
-parse_unscoped_name(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 2)
-    {
-        const char* t0 = first;
-        bool St = false;
-        if (first[0] == 'S' && first[1] == 't')
-        {
-            t0 += 2;
-            St = true;
-            if (t0 != last && *t0 == 'L')
-                ++t0;
-        }
-        const char* t1 = parse_unqualified_name(t0, last, db);
-        if (t1 != t0)
-        {
-            if (St)
-            {
-                if (db.Names.empty())
-                    return first;
-                db.Names.back() =
-                    db.make<StdQualifiedName>(db.Names.back());
+                      }
+                    case 'U':
+                        if (first+1 != last)
+                        {
+                            t = parse_source_name(first+1, last, db);
+                            if (t != first+1)
+                            {
+                                const char* t2 = parse_type(t, last, db);
+                                if (t2 != t)
+                                {
+                                    if (db.Names.size() < 2)
+                                        return first;
+                                    auto type = db.Names.back();
+                                    db.Names.pop_back();
+                                    if (db.Names.back()->K != Node::KNameType ||
+                                        !static_cast<NameType*>(db.Names.back())->getName().startsWith("objcproto"))
+                                    {
+                                        db.Names.back() = db.make<VendorExtQualType>(type, db.Names.back());
+                                    }
+                                    else
+                                    {
+                                        auto* proto = static_cast<NameType*>(db.Names.back());
+                                        db.Names.pop_back();
+                                        t = parse_source_name(proto->getName().begin() + 9, proto->getName().end(), db);
+                                        if (t != proto->getName().begin() + 9)
+                                        {
+                                            db.Names.back() = db.make<ObjCProtoName>(type, db.Names.back());
+                                        }
+                                        else
+                                        {
+                                            db.Names.push_back(db.make<VendorExtQualType>(type, proto));
+                                        }
+                                    }
+                                    db.Subs.push_back(db.Names.back());
+                                    first = t2;
+                                }
+                            }
+                        }
+                        break;
+                    case 'S':
+                        if (first+1 != last && first[1] == 't')
+                        {
+                            t = parse_name(first, last, db);
+                            if (t != first)
+                            {
+                                if (db.Names.empty())
+                                    return first;
+                                db.Subs.push_back(db.Names.back());
+                                first = t;
+                            }
+                        }
+                        else
+                        {
+                            t = parse_substitution(first, last, db);
+                            if (t != first)
+                            {
+                                first = t;
+                                // Parsed a substitution.  If the substitution is a
+                                //  <template-param> it might be followed by <template-args>.
+                                if (db.TryToParseTemplateArgs)
+                                {
+                                    t = parse_template_args(first, last, db);
+                                    if (t != first)
+                                    {
+                                        if (db.Names.size() < 2)
+                                            return first;
+                                        auto template_args = db.Names.back();
+                                        db.Names.pop_back();
+                                        db.Names.back() = db.make<
+                                          NameWithTemplateArgs>(
+                                              db.Names.back(), template_args);
+                                        // Need to create substitution for <template-template-param> <template-args>
+                                        db.Subs.push_back(db.Names.back());
+                                        first = t;
+                                    }
+                                }
+                            }
+                        }
+                        break;
+                    case 'D':
+                        if (first+1 != last)
+                        {
+                            switch (first[1])
+                            {
+                            case 'p':
+                              {
+                                size_t k0 = db.Names.size();
+                                t = parse_type(first+2, last, db);
+                                size_t k1 = db.Names.size();
+                                if (t != first+2 && k0 + 1 == k1)
+                                {
+                                    db.Names.back() =
+                                        db.make<ParameterPackExpansion>(
+                                            db.Names.back());
+                                    db.Subs.push_back(db.Names.back());
+                                    first = t;
+                                    return first;
+                                }
+                                break;
+                              }
+                            case 't':
+                            case 'T':
+                                t = parse_decltype(first, last, db);
+                                if (t != first)
+                                {
+                                    if (db.Names.empty())
+                                        return first;
+                                    db.Subs.push_back(db.Names.back());
+                                    first = t;
+                                    return first;
+                                }
+                                break;
+                            case 'v':
+                                t = parse_vector_type(first, last, db);
+                                if (t != first)
+                                {
+                                    if (db.Names.empty())
+                                        return first;
+                                    db.Subs.push_back(db.Names.back());
+                                    first = t;
+                                    return first;
+                                }
+                                break;
+                            }
+                        }
+                        _LIBCPP_FALLTHROUGH();
+                    default:
+                        // must check for builtin-types before class-enum-types to avoid
+                        // ambiguities with operator-names
+                        t = parse_builtin_type(first, last, db);
+                        if (t != first)
+                        {
+                            first = t;
+                        }
+                        else
+                        {
+                            t = parse_name(first, last, db);
+                            if (t != first)
+                            {
+                                if (db.Names.empty())
+                                    return first;
+                                db.Subs.push_back(db.Names.back());
+                                first = t;
+                            }
+                        }
+                        break;
+                    }
+              }
+                break;
             }
-            first = t1;
-        }
-    }
-    return first;
-}
-
-// at <type>                                            # alignof (a type)
-
-const char*
-parse_alignof_type(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 'a' && first[1] == 't')
-    {
-        const char* t = parse_type(first+2, last, db);
-        if (t != first+2)
-        {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() =
-                db.make<EnclosingExpr>("alignof (", db.Names.back(), ")");
-            first = t;
-        }
-    }
-    return first;
-}
-
-// az <expression>                                            # alignof (a expression)
-
-const char*
-parse_alignof_expr(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 3 && first[0] == 'a' && first[1] == 'z')
-    {
-        const char* t = parse_expression(first+2, last, db);
-        if (t != first+2)
-        {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() =
-                db.make<EnclosingExpr>("alignof (", db.Names.back(), ")");
-            first = t;
-        }
-    }
-    return first;
-}
-
-const char*
-parse_noexcept_expression(const char* first, const char* last, Db& db)
-{
-    const char* t1 = parse_expression(first, last, db);
-    if (t1 != first)
-    {
-        if (db.Names.empty())
-            return first;
-        db.Names.back() =
-            db.make<EnclosingExpr>("noexcept (", db.Names.back(), ")");
-        first = t1;
-    }
-    return first;
-}
-
-const char*
-parse_prefix_expression(const char* first, const char* last, StringView op, Db& db)
-{
-    const char* t1 = parse_expression(first, last, db);
-    if (t1 != first)
-    {
-        if (db.Names.empty())
-            return first;
-        db.Names.back() = db.make<PrefixExpr>(op, db.Names.back());
-        first = t1;
-    }
-    return first;
-}
-
-const char*
-parse_binary_expression(const char* first, const char* last, StringView op, Db& db)
-{
-    const char* t1 = parse_expression(first, last, db);
-    if (t1 != first)
-    {
-        const char* t2 = parse_expression(t1, last, db);
-        if (t2 != t1)
-        {
-            if (db.Names.size() < 2)
-                return first;
-            auto op2 = db.Names.back();
-            db.Names.pop_back();
-            auto op1 = db.Names.back();
-            db.Names.back() = db.make<BinaryExpr>(op1, op, op2);
-            first = t2;
         }
     }
     return first;
 }
 
-// <expression> ::= <unary operator-name> <expression>
-//              ::= <binary operator-name> <expression> <expression>
-//              ::= <ternary operator-name> <expression> <expression> <expression>
-//              ::= cl <expression>+ E                                   # call
-//              ::= cv <type> <expression>                               # conversion with one argument
-//              ::= cv <type> _ <expression>* E                          # conversion with a different number of arguments
-//              ::= [gs] nw <expression>* _ <type> E                     # new (expr-list) type
-//              ::= [gs] nw <expression>* _ <type> <initializer>         # new (expr-list) type (init)
-//              ::= [gs] na <expression>* _ <type> E                     # new[] (expr-list) type
-//              ::= [gs] na <expression>* _ <type> <initializer>         # new[] (expr-list) type (init)
-//              ::= [gs] dl <expression>                                 # delete expression
-//              ::= [gs] da <expression>                                 # delete[] expression
-//              ::= pp_ <expression>                                     # prefix ++
-//              ::= mm_ <expression>                                     # prefix --
-//              ::= ti <type>                                            # typeid (type)
-//              ::= te <expression>                                      # typeid (expression)
-//              ::= dc <type> <expression>                               # dynamic_cast<type> (expression)
-//              ::= sc <type> <expression>                               # static_cast<type> (expression)
-//              ::= cc <type> <expression>                               # const_cast<type> (expression)
-//              ::= rc <type> <expression>                               # reinterpret_cast<type> (expression)
-//              ::= st <type>                                            # sizeof (a type)
-//              ::= sz <expression>                                      # sizeof (an expression)
-//              ::= at <type>                                            # alignof (a type)
-//              ::= az <expression>                                      # alignof (an expression)
-//              ::= nx <expression>                                      # noexcept (expression)
-//              ::= <template-param>
-//              ::= <function-param>
-//              ::= dt <expression> <unresolved-name>                    # expr.name
-//              ::= pt <expression> <unresolved-name>                    # expr->name
-//              ::= ds <expression> <expression>                         # expr.*expr
-//              ::= sZ <template-param>                                  # size of a parameter pack
-//              ::= sZ <function-param>                                  # size of a function parameter pack
-//              ::= sp <expression>                                      # pack expansion
-//              ::= tw <expression>                                      # throw expression
-//              ::= tr                                                   # throw with no operand (rethrow)
-//              ::= <unresolved-name>                                    # f(p), N::f(p), ::f(p),
-//                                                                       # freestanding dependent name (e.g., T::x),
-//                                                                       # objectless nonstatic member reference
-//              ::= <expr-primary>
-
+//   <operator-name>
+//                   ::= aa    # &&            
+//                   ::= ad    # & (unary)     
+//                   ::= an    # &             
+//                   ::= aN    # &=            
+//                   ::= aS    # =             
+//                   ::= cl    # ()            
+//                   ::= cm    # ,             
+//                   ::= co    # ~             
+//                   ::= cv <type>    # (cast)        
+//                   ::= da    # delete[]      
+//                   ::= de    # * (unary)     
+//                   ::= dl    # delete        
+//                   ::= dv    # /             
+//                   ::= dV    # /=            
+//                   ::= eo    # ^             
+//                   ::= eO    # ^=            
+//                   ::= eq    # ==            
+//                   ::= ge    # >=            
+//                   ::= gt    # >             
+//                   ::= ix    # []            
+//                   ::= le    # <=            
+//                   ::= li <source-name>  # operator ""
+//                   ::= ls    # <<            
+//                   ::= lS    # <<=           
+//                   ::= lt    # <             
+//                   ::= mi    # -             
+//                   ::= mI    # -=            
+//                   ::= ml    # *             
+//                   ::= mL    # *=            
+//                   ::= mm    # -- (postfix in <expression> context)           
+//                   ::= na    # new[]
+//                   ::= ne    # !=            
+//                   ::= ng    # - (unary)     
+//                   ::= nt    # !             
+//                   ::= nw    # new           
+//                   ::= oo    # ||            
+//                   ::= or    # |             
+//                   ::= oR    # |=            
+//                   ::= pm    # ->*           
+//                   ::= pl    # +             
+//                   ::= pL    # +=            
+//                   ::= pp    # ++ (postfix in <expression> context)
+//                   ::= ps    # + (unary)
+//                   ::= pt    # ->            
+//                   ::= qu    # ?             
+//                   ::= rm    # %             
+//                   ::= rM    # %=            
+//                   ::= rs    # >>            
+//                   ::= rS    # >>=           
+//                   ::= v <digit> <source-name>        # vendor extended operator
+//   extension       ::= <operator-name> <abi-tag-seq>
 const char*
-parse_expression(const char* first, const char* last, Db& db)
+parse_operator_name(const char* first, const char* last, Db& db)
 {
+    const char* original_first = first;
     if (last - first >= 2)
     {
-        const char* t = first;
-        bool parsed_gs = false;
-        if (last - first >= 4 && t[0] == 'g' && t[1] == 's')
-        {
-            t += 2;
-            parsed_gs = true;
-        }
-        switch (*t)
+        switch (first[0])
         {
-        case 'L':
-            first = parse_expr_primary(first, last, db);
-            break;
-        case 'T':
-            first = parse_template_param(first, last, db);
-            break;
-        case 'f':
-            first = parse_function_param(first, last, db);
-            break;
         case 'a':
-            switch (t[1])
+            switch (first[1])
             {
             case 'a':
-                t = parse_binary_expression(first+2, last, "&&", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator&&"));
+                first += 2;
                 break;
             case 'd':
-                t = parse_prefix_expression(first+2, last, "&", db);
-                if (t != first+2)
-                    first = t;
-                break;
             case 'n':
-                t = parse_binary_expression(first+2, last, "&", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator&"));
+                first += 2;
                 break;
             case 'N':
-                t = parse_binary_expression(first+2, last, "&=", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator&="));
+                first += 2;
                 break;
             case 'S':
-                t = parse_binary_expression(first+2, last, "=", db);
-                if (t != first+2)
-                    first = t;
-                break;
-            case 't':
-                first = parse_alignof_type(first, last, db);
-                break;
-            case 'z':
-                first = parse_alignof_expr(first, last, db);
+                db.Names.push_back(db.make<NameType>("operator="));
+                first += 2;
                 break;
             }
             break;
         case 'c':
-            switch (t[1])
+            switch (first[1])
             {
-            case 'c':
-                first = parse_const_cast_expr(first, last, db);
-                break;
             case 'l':
-                first = parse_call_expr(first, last, db);
+                db.Names.push_back(db.make<NameType>("operator()"));
+                first += 2;
                 break;
             case 'm':
-                t = parse_binary_expression(first+2, last, ",", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator,"));
+                first += 2;
                 break;
             case 'o':
-                t = parse_prefix_expression(first+2, last, "~", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator~"));
+                first += 2;
                 break;
             case 'v':
-                first = parse_conversion_expr(first, last, db);
-                break;
-            }
-            break;
-        case 'd':
-            switch (t[1])
-            {
-            case 'a':
                 {
-                    const char* t1 = parse_expression(t+2, last, db);
-                    if (t1 != t+2)
+                    bool TryToParseTemplateArgs = db.TryToParseTemplateArgs;
+                    db.TryToParseTemplateArgs = false;
+                    const char* t = parse_type(first+2, last, db);
+                    db.TryToParseTemplateArgs = TryToParseTemplateArgs;
+                    if (t != first+2)
                     {
                         if (db.Names.empty())
                             return first;
-                        db.Names.back() = db.make<DeleteExpr>(
-                            db.Names.back(), parsed_gs, /*is_array=*/true);
-                        first = t1;
+                        db.Names.back() =
+                            db.make<ConversionOperatorType>(db.Names.back());
+                        db.ParsedCtorDtorCV = true;
+                        first = t;
                     }
                 }
                 break;
-            case 'c':
-                first = parse_dynamic_cast_expr(first, last, db);
+            }
+            break;
+        case 'd':
+            switch (first[1])
+            {
+            case 'a':
+                db.Names.push_back(db.make<NameType>("operator delete[]"));
+                first += 2;
                 break;
             case 'e':
-                t = parse_prefix_expression(first+2, last, "*", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator*"));
+                first += 2;
                 break;
             case 'l':
-                {
-                    const char* t1 = parse_expression(t+2, last, db);
-                    if (t1 != t+2)
-                    {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() = db.make<DeleteExpr>(
-                            db.Names.back(), parsed_gs, /*is_array=*/false);
-                        first = t1;
-                    }
-                }
-                break;
-            case 'n':
-                return parse_unresolved_name(first, last, db);
-            case 's':
-                first = parse_dot_star_expr(first, last, db);
-                break;
-            case 't':
-                first = parse_dot_expr(first, last, db);
+                db.Names.push_back(db.make<NameType>("operator delete"));
+                first += 2;
                 break;
             case 'v':
-                t = parse_binary_expression(first+2, last, "/", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator/"));
+                first += 2;
                 break;
             case 'V':
-                t = parse_binary_expression(first+2, last, "/=", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator/="));
+                first += 2;
                 break;
             }
             break;
         case 'e':
-            switch (t[1])
+            switch (first[1])
             {
             case 'o':
-                t = parse_binary_expression(first+2, last, "^", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator^"));
+                first += 2;
                 break;
             case 'O':
-                t = parse_binary_expression(first+2, last, "^=", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator^="));
+                first += 2;
                 break;
             case 'q':
-                t = parse_binary_expression(first+2, last, "==", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator=="));
+                first += 2;
                 break;
             }
             break;
         case 'g':
-            switch (t[1])
+            switch (first[1])
             {
             case 'e':
-                t = parse_binary_expression(first+2, last, ">=", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator>="));
+                first += 2;
                 break;
             case 't':
-                t = parse_binary_expression(first+2, last, ">", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator>"));
+                first += 2;
                 break;
             }
             break;
         case 'i':
-            if (t[1] == 'x')
+            if (first[1] == 'x')
+            {
+                db.Names.push_back(db.make<NameType>("operator[]"));
+                first += 2;
+            }
+            break;
+        case 'l':
+            switch (first[1])
             {
-                const char* t1 = parse_expression(first+2, last, db);
-                if (t1 != first+2)
+            case 'e':
+                db.Names.push_back(db.make<NameType>("operator<="));
+                first += 2;
+                break;
+            case 'i':
                 {
-                    const char* t2 = parse_expression(t1, last, db);
-                    if (t2 != t1)
+                    const char* t = parse_source_name(first+2, last, db);
+                    if (t != first+2)
                     {
-                        if (db.Names.size() < 2)
+                        if (db.Names.empty())
                             return first;
-                        auto op2 = db.Names.back();
-                        db.Names.pop_back();
-                        auto op1 = db.Names.back();
                         db.Names.back() =
-                            db.make<ArraySubscriptExpr>(op1, op2);
-                        first = t2;
+                            db.make<LiteralOperator>(db.Names.back());
+                        first = t;
                     }
-                    else if (!db.Names.empty())
-                        db.Names.pop_back();
                 }
-            }
-            break;
-        case 'l':
-            switch (t[1])
-            {
-            case 'e':
-                t = parse_binary_expression(first+2, last, "<=", db);
-                if (t != first+2)
-                    first = t;
                 break;
             case 's':
-                t = parse_binary_expression(first+2, last, "<<", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator<<"));
+                first += 2;
                 break;
             case 'S':
-                t = parse_binary_expression(first+2, last, "<<=", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator<<="));
+                first += 2;
                 break;
             case 't':
-                t = parse_binary_expression(first+2, last, "<", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator<"));
+                first += 2;
                 break;
             }
             break;
         case 'm':
-            switch (t[1])
+            switch (first[1])
             {
             case 'i':
-                t = parse_binary_expression(first+2, last, "-", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator-"));
+                first += 2;
                 break;
             case 'I':
-                t = parse_binary_expression(first+2, last, "-=", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator-="));
+                first += 2;
                 break;
             case 'l':
-                t = parse_binary_expression(first+2, last, "*", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator*"));
+                first += 2;
                 break;
             case 'L':
-                t = parse_binary_expression(first+2, last, "*=", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator*="));
+                first += 2;
                 break;
             case 'm':
-                if (first+2 != last && first[2] == '_')
-                {
-                    t = parse_prefix_expression(first+3, last, "--", db);
-                    if (t != first+3)
-                        first = t;
-                }
-                else
-                {
-                    const char* t1 = parse_expression(first+2, last, db);
-                    if (t1 != first+2)
-                    {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<PostfixExpr>(db.Names.back(), "--");
-                        first = t1;
-                    }
-                }
+                db.Names.push_back(db.make<NameType>("operator--"));
+                first += 2;
                 break;
             }
             break;
         case 'n':
-            switch (t[1])
+            switch (first[1])
             {
             case 'a':
-            case 'w':
-                first = parse_new_expr(first, last, db);
+                db.Names.push_back(db.make<NameType>("operator new[]"));
+                first += 2;
                 break;
             case 'e':
-                t = parse_binary_expression(first+2, last, "!=", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator!="));
+                first += 2;
                 break;
             case 'g':
-                t = parse_prefix_expression(first+2, last, "-", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator-"));
+                first += 2;
                 break;
             case 't':
-                t = parse_prefix_expression(first+2, last, "!", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator!"));
+                first += 2;
                 break;
-            case 'x':
-                t = parse_noexcept_expression(first+2, last, db);
-                if (t != first+2)
-                    first = t;
+            case 'w':
+                db.Names.push_back(db.make<NameType>("operator new"));
+                first += 2;
                 break;
             }
             break;
         case 'o':
-            switch (t[1])
+            switch (first[1])
             {
-            case 'n':
-                return parse_unresolved_name(first, last, db);
             case 'o':
-                t = parse_binary_expression(first+2, last, "||", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator||"));
+                first += 2;
                 break;
             case 'r':
-                t = parse_binary_expression(first+2, last, "|", db);
-                if (t != first+2)
-                    first = t;
+                db.Names.push_back(db.make<NameType>("operator|"));
+                first += 2;
                 break;
             case 'R':
-                t = parse_binary_expression(first+2, last, "|=", db);
+                db.Names.push_back(db.make<NameType>("operator|="));
+                first += 2;
+                break;
+            }
+            break;
+        case 'p':
+            switch (first[1])
+            {
+            case 'm':
+                db.Names.push_back(db.make<NameType>("operator->*"));
+                first += 2;
+                break;
+            case 'l':
+                db.Names.push_back(db.make<NameType>("operator+"));
+                first += 2;
+                break;
+            case 'L':
+                db.Names.push_back(db.make<NameType>("operator+="));
+                first += 2;
+                break;
+            case 'p':
+                db.Names.push_back(db.make<NameType>("operator++"));
+                first += 2;
+                break;
+            case 's':
+                db.Names.push_back(db.make<NameType>("operator+"));
+                first += 2;
+                break;
+            case 't':
+                db.Names.push_back(db.make<NameType>("operator->"));
+                first += 2;
+                break;
+            }
+            break;
+        case 'q':
+            if (first[1] == 'u')
+            {
+                db.Names.push_back(db.make<NameType>("operator?"));
+                first += 2;
+            }
+            break;
+        case 'r':
+            switch (first[1])
+            {
+            case 'm':
+                db.Names.push_back(db.make<NameType>("operator%"));
+                first += 2;
+                break;
+            case 'M':
+                db.Names.push_back(db.make<NameType>("operator%="));
+                first += 2;
+                break;
+            case 's':
+                db.Names.push_back(db.make<NameType>("operator>>"));
+                first += 2;
+                break;
+            case 'S':
+                db.Names.push_back(db.make<NameType>("operator>>="));
+                first += 2;
+                break;
+            }
+            break;
+        case 'v':
+            if (std::isdigit(first[1]))
+            {
+                const char* t = parse_source_name(first+2, last, db);
                 if (t != first+2)
+                {
+                    if (db.Names.empty())
+                        return first;
+                    db.Names.back() =
+                        db.make<ConversionOperatorType>(db.Names.back());
                     first = t;
+                }
+            }
+            break;
+        }
+    }
+
+    if (original_first != first)
+        first = parse_abi_tag_seq(first, last, db);
+
+    return first;
+}
+
+Node* maybe_change_special_sub_name(Node* inp, Db& db)
+{
+    if (inp->K != Node::KSpecialSubstitution)
+        return inp;
+    auto Kind = static_cast<SpecialSubstitution*>(inp)->SSK;
+    switch (Kind)
+    {
+    case SpecialSubKind::string:
+    case SpecialSubKind::istream:
+    case SpecialSubKind::ostream:
+    case SpecialSubKind::iostream:
+        return db.make<ExpandedSpecialSubstitution>(Kind);
+    default:
+        break;
+    }
+    return inp;
+}
+
+// <ctor-dtor-name> ::= C1    # complete object constructor
+//                  ::= C2    # base object constructor
+//                  ::= C3    # complete object allocating constructor
+//   extension      ::= C5    # ?
+//                  ::= D0    # deleting destructor
+//                  ::= D1    # complete object destructor
+//                  ::= D2    # base object destructor
+//   extension      ::= D5    # ?
+//   extension      ::= <ctor-dtor-name> <abi-tag-seq>
+const char*
+parse_ctor_dtor_name(const char* first, const char* last, Db& db)
+{
+    if (last-first >= 2 && !db.Names.empty())
+    {
+        switch (first[0])
+        {
+        case 'C':
+            switch (first[1])
+            {
+            case '1':
+            case '2':
+            case '3':
+            case '5':
+                if (db.Names.empty())
+                    return first;
+                db.Names.back() =
+                    maybe_change_special_sub_name(db.Names.back(), db);
+                db.Names.push_back(
+                    db.make<CtorDtorName>(db.Names.back(), false));
+                first += 2;
+                first = parse_abi_tag_seq(first, last, db);
+                db.ParsedCtorDtorCV = true;
                 break;
             }
             break;
-        case 'p':
-            switch (t[1])
+        case 'D':
+            switch (first[1])
             {
-            case 'm':
-                t = parse_binary_expression(first+2, last, "->*", db);
-                if (t != first+2)
-                    first = t;
-                break;
-            case 'l':
-                t = parse_binary_expression(first+2, last, "+", db);
-                if (t != first+2)
-                    first = t;
-                break;
-            case 'L':
-                t = parse_binary_expression(first+2, last, "+=", db);
-                if (t != first+2)
-                    first = t;
-                break;
-            case 'p':
-                if (first+2 != last && first[2] == '_')
-                {
-                    t = parse_prefix_expression(first+3, last, "++", db);
-                    if (t != first+3)
-                        first = t;
-                }
-                else
-                {
-                    const char* t1 = parse_expression(first+2, last, db);
-                    if (t1 != first+2)
-                    {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<PostfixExpr>(db.Names.back(), "++");
-                        first = t1;
-                    }
-                }
-                break;
-            case 's':
-                t = parse_prefix_expression(first+2, last, "+", db);
-                if (t != first+2)
-                    first = t;
-                break;
-            case 't':
-                first = parse_arrow_expr(first, last, db);
+            case '0':
+            case '1':
+            case '2':
+            case '5':
+                if (db.Names.empty())
+                    return first;
+                db.Names.push_back(
+                    db.make<CtorDtorName>(db.Names.back(), true));
+                first += 2;
+                first = parse_abi_tag_seq(first, last, db);
+                db.ParsedCtorDtorCV = true;
                 break;
             }
             break;
-        case 'q':
-            if (t[1] == 'u')
+        }
+    }
+    return first;
+}
+
+// <unnamed-type-name> ::= Ut [<nonnegative number>] _ [<abi-tag-seq>]
+//                     ::= <closure-type-name>
+// 
+// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ 
+// 
+// <lambda-sig> ::= <parameter type>+  # Parameter types or "v" if the lambda has no parameters
+const char*
+parse_unnamed_type_name(const char* first, const char* last, Db& db)
+{
+    if (last - first > 2 && first[0] == 'U')
+    {
+        char type = first[1];
+        switch (type)
+        {
+        case 't':
+          {
+            const char* t0 = first+2;
+            if (t0 == last)
+                return first;
+            StringView count;
+            if (std::isdigit(*t0))
             {
-                const char* t1 = parse_expression(first+2, last, db);
-                if (t1 != first+2)
-                {
-                    const char* t2 = parse_expression(t1, last, db);
-                    if (t2 != t1)
-                    {
-                        const char* t3 = parse_expression(t2, last, db);
-                        if (t3 != t2)
-                        {
-                            if (db.Names.size() < 3)
-                                return first;
-                            auto op3 = db.Names.back();
-                            db.Names.pop_back();
-                            auto op2 = db.Names.back();
-                            db.Names.pop_back();
-                            auto op1 = db.Names.back();
-                            db.Names.back() =
-                                db.make<ConditionalExpr>(op1, op2, op3);
-                            first = t3;
-                        }
-                        else
-                        {
-                            if (db.Names.size() < 2)
-                              return first;
-                            db.Names.pop_back();
-                            db.Names.pop_back();
-                        }
-                    }
-                    else if (!db.Names.empty())
-                        db.Names.pop_back();
-                }
+                const char* t1 = t0 + 1;
+                while (t1 != last && std::isdigit(*t1))
+                    ++t1;
+                count = StringView(t0, t1);
+                t0 = t1;
             }
+            if (t0 == last || *t0 != '_')
+                return first;
+            db.Names.push_back(db.make<UnnamedTypeName>(count));
+            first = t0 + 1;
+            first = parse_abi_tag_seq(first, last, db);
+          }
             break;
-        case 'r':
-            switch (t[1])
+        case 'l':
+          {
+            size_t begin_pos = db.Names.size();
+            const char* t0 = first+2;
+            NodeArray lambda_params;
+            if (first[2] == 'v')
             {
-            case 'c':
-                first = parse_reinterpret_cast_expr(first, last, db);
-                break;
-            case 'm':
-                t = parse_binary_expression(first+2, last, "%", db);
-                if (t != first+2)
-                    first = t;
-                break;
-            case 'M':
-                t = parse_binary_expression(first+2, last, "%=", db);
-                if (t != first+2)
-                    first = t;
-                break;
-            case 's':
-                t = parse_binary_expression(first+2, last, ">>", db);
-                if (t != first+2)
-                    first = t;
-                break;
-            case 'S':
-                t = parse_binary_expression(first+2, last, ">>=", db);
-                if (t != first+2)
-                    first = t;
-                break;
+                ++t0;
             }
-            break;
-        case 's':
-            switch (t[1])
+            else
             {
-            case 'c':
-                first = parse_static_cast_expr(first, last, db);
-                break;
-            case 'p':
-                first = parse_pack_expansion(first, last, db);
-                break;
-            case 'r':
-                return parse_unresolved_name(first, last, db);
-            case 't':
-                first = parse_sizeof_type_expr(first, last, db);
-                break;
-            case 'z':
-                first = parse_sizeof_expr_expr(first, last, db);
-                break;
-            case 'Z':
-                if (last - t >= 3)
+                while (true)
                 {
-                    switch (t[2])
-                    {
-                    case 'T':
-                        first = parse_sizeof_param_pack_expr(first, last, db);
-                        break;
-                    case 'f':
-                        first = parse_sizeof_function_param_pack_expr(first, last, db);
+                    const char* t1 = parse_type(t0, last, db);
+                    if (t1 == t0)
                         break;
-                    }
+                    t0 = t1;
                 }
-                break;
+                if (db.Names.size() < begin_pos)
+                    return first;
+                lambda_params = db.popTrailingNodeArray(begin_pos);
             }
-            break;
-        case 't':
-            switch (t[1])
+            if (t0 == last || *t0 != 'E')
+                return first;
+            ++t0;
+            if (t0 == last)
+                return first;
+            StringView count;
+            if (std::isdigit(*t0))
             {
-            case 'e':
-            case 'i':
-                first = parse_typeid_expr(first, last, db);
-                break;
-            case 'r':
-                db.Names.push_back(db.make<NameType>("throw"));
-                first += 2;
-                break;
-            case 'w':
-                first = parse_throw_expr(first, last, db);
-                break;
+                const char* t1 = t0 + 1;
+                while (t1 != last && std::isdigit(*t1))
+                    ++t1;
+                count = StringView(t0, t1);
+                t0 = t1;
             }
+            if (t0 == last || *t0 != '_')
+                return first;
+            db.Names.push_back(db.make<LambdaTypeName>(lambda_params, count));
+            first = t0 + 1;
+          }
+            break;
+        }
+    }
+    return first;
+}
+
+// <unqualified-name> ::= <operator-name>
+//                    ::= <ctor-dtor-name>
+//                    ::= <source-name>   
+//                    ::= <unnamed-type-name>
+
+const char*
+parse_unqualified_name(const char* first, const char* last, Db& db)
+{
+    if (first != last)
+    {
+        const char* t;
+        switch (*first)
+        {
+        case 'C':
+        case 'D':
+            t = parse_ctor_dtor_name(first, last, db);
+            if (t != first)
+                first = t;
+            break;
+        case 'U':
+            t = parse_unnamed_type_name(first, last, db);
+            if (t != first)
+                first = t;
             break;
         case '1':
         case '2':
@@ -5381,7 +4741,49 @@ parse_expression(const char* first, cons
         case '7':
         case '8':
         case '9':
-            return parse_unresolved_name(first, last, db);
+            t = parse_source_name(first, last, db);
+            if (t != first)
+                first = t;
+            break;
+        default:
+            t = parse_operator_name(first, last, db);
+            if (t != first)
+                first = t;
+            break;
+        };
+    }
+    return first;
+}
+
+// <unscoped-name> ::= <unqualified-name>
+//                 ::= St <unqualified-name>   # ::std::
+// extension       ::= StL<unqualified-name>
+
+const char*
+parse_unscoped_name(const char* first, const char* last, Db& db)
+{
+    if (last - first >= 2)
+    {
+        const char* t0 = first;
+        bool St = false;
+        if (first[0] == 'S' && first[1] == 't')
+        {
+            t0 += 2;
+            St = true;
+            if (t0 != last && *t0 == 'L')
+                ++t0;
+        }
+        const char* t1 = parse_unqualified_name(t0, last, db);
+        if (t1 != t0)
+        {
+            if (St)
+            {
+                if (db.Names.empty())
+                    return first;
+                db.Names.back() =
+                    db.make<StdQualifiedName>(db.Names.back());
+            }
+            first = t1;
         }
     }
     return first;

Modified: libcxxabi/trunk/test/test_demangle.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/test_demangle.pass.cpp?rev=324111&r1=324110&r2=324111&view=diff
==============================================================================
--- libcxxabi/trunk/test/test_demangle.pass.cpp (original)
+++ libcxxabi/trunk/test/test_demangle.pass.cpp Fri Feb  2 10:04:32 2018
@@ -29617,6 +29617,45 @@ const char* cases[][2] =
     {"_Z1fIJicEEvDp7MuncherIAstT__S1_E", "void f<int, char>(Muncher<int [sizeof (int)]>, Muncher<char [sizeof (char)]>)"},
     {"_ZN1SIJifcEE1fIJdjEEEiDp4MerpIJifcT_EE", "int S<int, float, char>::f<double, unsigned int>(Merp<int, float, char, double>, Merp<int, float, char, unsigned int>)"},
     {"_Z1pIJicEEiDp4MerpIXsZT_EJT_EE", "int p<int, char>(Merp<sizeof...(int, char), int>, Merp<sizeof...(int, char), char>)"},
+
+    // Some expression symbols found in clang's test/CodeGenCXX/mangle-exprs.cpp
+    {"_ZN5Casts8implicitILj4EEEvPN9enable_ifIXleT_Li4EEvE4typeE", "void Casts::implicit<4u>(enable_if<(4u) <= (4), void>::type*)"},
+    {"_ZN5Casts6cstyleILj4EEEvPN9enable_ifIXleT_cvjLi4EEvE4typeE", "void Casts::cstyle<4u>(enable_if<(4u) <= ((unsigned int)(4)), void>::type*)"},
+    {"_ZN5Casts10functionalILj4EEEvPN9enable_ifIXleT_cvjLi4EEvE4typeE", "void Casts::functional<4u>(enable_if<(4u) <= ((unsigned int)(4)), void>::type*)"},
+    {"_ZN5Casts7static_ILj4EEEvPN9enable_ifIXleT_scjLi4EEvE4typeE", "void Casts::static_<4u>(enable_if<(4u) <= (static_cast<unsigned int>(4)), void>::type*)"},
+    {"_ZN5Casts12reinterpret_ILj4EiEEvPN9enable_ifIXleT_szrcPT0_Li0EEvE4typeE", "void Casts::reinterpret_<4u, int>(enable_if<(4u) <= (sizeof (reinterpret_cast<int*>(0))), void>::type*)"},
+    {"_ZN5Casts6const_IiXadL_ZNS_1iEEEEEvPN9enable_ifIXleLi0EszccPT_T0_EvE4typeE", "void Casts::const_<int, &(Casts::i)>(enable_if<(0) <= (sizeof (const_cast<int*>(&(Casts::i)))), void>::type*)"},
+    {"_ZN5Casts8dynamic_INS_1SEXadL_ZNS_1sEEEEEvPN9enable_ifIXleLi0EszdcPT_T0_EvE4typeE", "void Casts::dynamic_<Casts::S, &(Casts::s)>(enable_if<(0) <= (sizeof (dynamic_cast<Casts::S*>(&(Casts::s)))), void>::type*)"},
+    {"_ZN5Casts1fILi6EEENS_1TIXT_EEEv", "Casts::T<6> Casts::f<6>()"},
+    {"_ZN5Casts5auto_IiEEvDTnw_DapicvT__EEE", "void Casts::auto_<int>(decltype(new auto((int)())))"},
+    {"_ZN5Casts7scalar_IiEEvDTcmcvT__Ecvi_EE", "void Casts::scalar_<int>(decltype(((int)()) , ((int)())))"},
+    {"_ZN5test11aIsEEDTcl3foocvT__EEES1_", "decltype(foo((short)())) test1::a<short>(short)"},
+    {"_ZN5test21aIPFfvEEEvT_DTclfL0p_EE", "void test2::a<float (*)()>(float (*)(), decltype(fp()))"},
+    {"_ZN5test21bIPFfvEEEDTclfp_EET_", "decltype(fp()) test2::b<float (*)()>(float (*)())"},
+    {"_ZN5test21cIPFfvEEEvT_PFvDTclfL1p_EEE", "void test2::c<float (*)()>(float (*)(), void (*)(decltype(fp())))"},
+    {"_ZN5test21dIPFfvEEEvT_PFDTclfL0p_EEvE", "void test2::d<float (*)()>(float (*)(), decltype(fp()) (*)())"},
+    {"_ZN5test21eIPFfvEEEvPFDTclfp_EET_E", "void test2::e<float (*)()>(decltype(fp()) (*)(float (*)()))"},
+    {"_ZN5test21fIPFfvEEEvPFvT_DTclfL0p_EEE", "void test2::f<float (*)()>(void (*)(float (*)(), decltype(fp())))"},
+    {"_ZN5test21gIPFfvEEEvT_DTclfL0p_EE", "void test2::g<float (*)()>(float (*)(), decltype(fp()))"},
+    {"_ZN5test21hIPFfvEEEvT_DTcvPFDTclfL0p_EEvELi0EE", "void test2::h<float (*)()>(float (*)(), decltype((decltype(fp()) (*)())(0)))"},
+    {"_ZN5test21iIPFfvEEEvDTcvPFDTclfp_EET_ELi0EE", "void test2::i<float (*)()>(decltype((decltype(fp()) (*)(float (*)()))(0)))"},
+    {"_ZZN5test21gIPFfvEEEvT_DTclfL0p_EEE8variable", "void test2::g<float (*)()>(float (*)(), decltype(fp()))::variable"},
+    {"_ZN5test31aINS_1XEMS1_PiEEvT_T0_DTdsfL0p_fL0p0_E", "void test3::a<test3::X, int* test3::X::*>(test3::X, int* test3::X::*, decltype(fp.*fp0))"},
+    {"_ZN5test43tf1INS_1XEEEvDTnw_T_piLi1EEE", "void test4::tf1<test4::X>(decltype(new test4::X(1)))"},
+    {"_ZN5test51aIiEEvDTnxcvT__EE", "void test5::a<int>(decltype(noexcept ((int)())))"},
+    {"_ZN5test62f1IiEEvDTcvT_dtdtL_ZNS_1zEE2ua1iE", "void test6::f1<int>(decltype((int)(test6::z.ua.i)))"},
+    {"_ZN5test62f2IiEEvDTcvT_dtdtL_ZNS_1zEE2ub1iE", "void test6::f2<int>(decltype((int)(test6::z.ub.i)))"},
+    {"_ZN5test62f3IiEEvDTcvT_dtdtL_ZNS_1zEE1s1iE", "void test6::f3<int>(decltype((int)(test6::z.s.i)))"},
+    {"_ZN5test62f4IiEEvDTcvT_dtdtL_ZNS_1zEE4uuss1iE", "void test6::f4<int>(decltype((int)(test6::z.uuss.i)))"},
+    {"_ZN5test62f5IiEEvDTcvT_dtptL_ZNS_2zpEE2ua1iE", "void test6::f5<int>(decltype((int)(test6::zp->ua.i)))"},
+    {"_ZN5test62f6IiEEvDTcvT_dtptL_ZNS_2zpEE2ub1iE", "void test6::f6<int>(decltype((int)(test6::zp->ub.i)))"},
+    {"_ZN5test62f7IiEEvDTcvT_dtptL_ZNS_2zpEE1s1iE", "void test6::f7<int>(decltype((int)(test6::zp->s.i)))"},
+    {"_ZN5test62f8IiEEvDTcvT_dtptL_ZNS_2zpEE4uuss1iE", "void test6::f8<int>(decltype((int)(test6::zp->uuss.i)))"},
+    {"_ZN5test73fD2IiEEDTcmcvNS_1DEL_ZNS_1bEEcvT__EES2_", "decltype(((test7::D)(test7::b)) , ((int)())) test7::fD2<int>(int)"},
+    {"_ZN5test73fT2IiEEDTcvT__EES1_", "decltype((int)()) test7::fT2<int>(int)"},
+    {"_ZN5test73fT4IiEEDTcvT_Li1EES1_", "decltype((int)(1)) test7::fT4<int>(int)"},
+    {"_ZN5test73fT6INS_1BEEEDTcvT__Li1ELi2EEES2_", "decltype((test7::B)(1, 2)) test7::fT6<test7::B>(test7::B)"},
+    {"_ZNK5test81XIiE3barIiEEDTcl3fooIT_EEEv", "decltype(foo<int>()) test8::X<int>::bar<int>() const"},
 };
 
 const unsigned N = sizeof(cases) / sizeof(cases[0]);
@@ -29749,6 +29788,36 @@ const char *xfail_cases[] = {
     "_Z1fUa9enable_ifIXLi1EEEv", // enable_if attribute
     "_ZDC2a12a2E", // decomposition decl
     "_ZW6FooBarE2f3v", // C++ modules TS
+
+    // FIXME: Why does clang generate the "cp" expr?
+    "_ZN5test11bIsEEDTcp3foocvT__EEES1_",
+
+    // Initializer list expressions:
+    "_ZN5test43tf2INS_1XEEEvDTnw_T_piilLi1EEEE",
+    "_ZN5test43tf3INS_1XEEEvDTnw_T_ilLi1EEE",
+    "_ZN5test73fA1IiEEDTcmtlNS_1AELi1ELi2EEcvT__EES2_",
+    "_ZN5test73fA2IiEEDTcmcvNS_1AEilLi1ELi2EEcvT__EES2_",
+    "_ZN5test73fB1IiEEDTcmtlNS_1BELi1ELi2EEcvT__EES2_",
+    "_ZN5test73fB2IiEEDTcmcvNS_1BEilLi1ELi2EEcvT__EES2_",
+    "_ZN5test73fC1IiEEDTcmtlNS_1CEilLi1ELi2EEEcvT__EES2_",
+    "_ZN5test73fC2IiEEDTcmcvNS_1CEilLi1ELi2EEcvT__EES2_",
+    "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_",
+    "_ZN5test73fE1IiEEDTcmtlNS_1EELi1ELi2EEcvT__EES2_",
+    "_ZN5test73fE2IiEEDTcmcvNS_1EEilLi1ELi2EEcvT__EES2_",
+    "_ZN5test73fF1IiEEDTcmtlNS_1FEilLi1ELi2EEEcvT__EES2_",
+    "_ZN5test73fF2IiEEDTcmcvNS_1FEilLi1ELi2EEcvT__EES2_",
+    "_ZN5test73fT1IiEEDTtlT_EES1_",
+    "_ZN5test73fT3IiEEDTtlT_Li1EEES1_",
+    "_ZN5test73fT5INS_1BEEEDTtlT_Li1ELi2EEES2_",
+    "_ZN5test73fT7INS_1AEEEDTtlT_ilEEES2_",
+    "_ZN5test73fT8INS_1AEEEDTcvT_ilEES2_",
+    "_ZN5test73fT9INS_1AEEEDTtlT_ilLi1EEEES2_",
+    "_ZN5test73fTAINS_1AEEEDTcvT_ilLi1EEES2_",
+    "_ZN5test73fTBINS_1CEEEDTtlT_ilLi1ELi2EEEES2_",
+    "_ZN5test73fTCINS_1CEEEDTcvT_ilLi1ELi2EEES2_",
+
+    // Designated init expressions
+    "_ZN15designated_init1fINS_1AEEEvDTtlT_di1adi1bdxLi3EdXLi1ELi4ELi9EEE",
 };
 
 const size_t num_xfails = sizeof(xfail_cases) / sizeof(xfail_cases[0]);




More information about the cfe-commits mailing list