[libcxxabi] r326717 - [demangler] Modernize parse_name.

Erik Pilkington via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 5 08:35:06 PST 2018


Author: epilk
Date: Mon Mar  5 08:35:06 2018
New Revision: 326717

URL: http://llvm.org/viewvc/llvm-project?rev=326717&view=rev
Log:
[demangler] Modernize parse_name.

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=326717&r1=326716&r2=326717&view=diff
==============================================================================
--- libcxxabi/trunk/src/cxa_demangle.cpp (original)
+++ libcxxabi/trunk/src/cxa_demangle.cpp Mon Mar  5 08:35:06 2018
@@ -196,7 +196,7 @@ public:
     KCtorDtorName,
     KDtorName,
     KUnnamedTypeName,
-    KLambdaTypeName,
+    KClosureTypeName,
     KExpr,
     KBracedExpr,
     KBracedRangeExpr,
@@ -1317,13 +1317,13 @@ public:
   }
 };
 
-class LambdaTypeName : public Node {
+class ClosureTypeName : public Node {
   NodeArray Params;
   StringView Count;
 
 public:
-  LambdaTypeName(NodeArray Params_, StringView Count_)
-      : Node(KLambdaTypeName), Params(Params_), Count(Count_) {
+  ClosureTypeName(NodeArray Params_, StringView Count_)
+      : Node(KClosureTypeName), Params(Params_), Count(Count_) {
     for (Node *P : Params)
       ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
   }
@@ -1995,32 +1995,13 @@ struct Db {
   // stored on the stack.
   PODSmallVector<Node *, 8> TemplateParams;
 
-  Qualifiers CV = QualNone;
-  FunctionRefQual RefQuals = FrefQualNone;
   unsigned EncodingDepth = 0;
-  bool ParsedCtorDtorCV = false;
-  bool EndsWithTemplateArgs = false;
   bool TagTemplates = true;
   bool FixForwardReferences = false;
   bool TryToParseTemplateArgs = true;
 
   BumpPointerAllocator ASTAllocator;
 
-  // A couple of members of Db are local to a specific name. When recursively
-  // parsing names we need to swap and restore them all.
-  struct SwapAndRestoreNameState {
-    SwapAndRestore<Qualifiers> SaveQualifiers;
-    SwapAndRestore<FunctionRefQual> SaveRefQualifiers;
-    SwapAndRestore<bool> SaveEndsWithTemplateArgs;
-    SwapAndRestore<bool> SaveParsedCtorDtorCV;
-
-    SwapAndRestoreNameState(Db &Parser)
-        : SaveQualifiers(Parser.CV, QualNone),
-          SaveRefQualifiers(Parser.RefQuals, FrefQualNone),
-          SaveEndsWithTemplateArgs(Parser.EndsWithTemplateArgs, false),
-          SaveParsedCtorDtorCV(Parser.ParsedCtorDtorCV, false) {}
-  };
-
   template <class T, class... Args> T *make(Args &&... args) {
     return new (ASTAllocator.allocate(sizeof(T)))
         T(std::forward<Args>(args)...);
@@ -2095,8 +2076,30 @@ struct Db {
   Node *parseClassEnumType();
   Node *parseQualifiedType();
 
-  Node *parseNestedName();
-  Node *parseCtorDtorName(Node *&SoFar);
+  Node *parseEncoding();
+  bool parseCallOffset();
+  Node *parseSpecialName();
+
+  /// Holds some extra information about a <name> that is being parsed. This
+  /// information is only pertinent if the <name> refers to an <encoding>.
+  struct NameState {
+    bool CtorDtorConversion = false;
+    bool EndsWithTemplateArgs = false;
+    Qualifiers CVQualifiers = QualNone;
+    FunctionRefQual ReferenceQualifier = FrefQualNone;
+  };
+
+  /// Parse the <name> production>
+  Node *parseName(NameState *State = nullptr);
+  Node *parseLocalName(NameState *State);
+  Node *parseOperatorName(NameState *State);
+  Node *parseUnqualifiedName(NameState *State);
+  Node *parseUnnamedTypeName(NameState *State);
+  Node *parseSourceName(NameState *State);
+  Node *parseUnscopedName(NameState *State);
+  Node *parseNestedName(NameState *State);
+  Node *parseCtorDtorName(Node *&SoFar, NameState *State);
+
   Node *parseAbiTags(Node *N);
 
   // FIXME: remove this when all the parse_* functions have been rewritten.
@@ -2112,33 +2115,8 @@ struct Db {
     Names.pop_back();
     return R;
   }
-  template <const char *(*parse_fn)(const char *, const char *, Db &, bool *)>
-  Node *legacyParse() {
-    size_t BeforeType = Names.size();
-    const char *OrigFirst = First;
-    const char *T = parse_fn(First, Last, *this, nullptr);
-    if (T == OrigFirst || BeforeType + 1 != Names.size())
-      return nullptr;
-    First = T;
-    Node *R = Names.back();
-    Names.pop_back();
-    return R;
-  }
 };
 
-const char *parse_nested_name(const char *first, const char *last, Db &db,
-                              bool *endsWithTemplateArgs) {
-  db.First = first;
-  db.Last = last;
-  Node *R = db.parseNestedName();
-  if (endsWithTemplateArgs)
-    *endsWithTemplateArgs = db.EndsWithTemplateArgs;
-  if (R == nullptr)
-    return first;
-  db.Names.push_back(R);
-  return db.First;
-}
-
 const char *parse_expression(const char *first, const char *last, Db &db) {
   db.First = first;
   db.Last = last;
@@ -2179,1385 +2157,1849 @@ const char *parse_decltype(const char *f
   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_source_name(const char *first, const char *last, Db &db) {
+  db.First = first;
+  db.Last = last;
+  Node *R = db.parseSourceName(/*NameState=*/nullptr);
+  if (R == nullptr)
+    return first;
+  db.Names.push_back(R);
+  return db.First;
+}
+
+const char *parse_operator_name(const char *first, const char *last, Db &db) {
+  db.First = first;
+  db.Last = last;
+  Node *R = db.parseOperatorName(/*NameState=*/nullptr);
+  if (R == nullptr)
+    return first;
+  db.Names.push_back(R);
+  return db.First;
+}
+
+const char *parse_unqualified_name(const char *first, const char *last, Db &db) {
+  db.First = first;
+  db.Last = last;
+  Node *R = db.parseUnqualifiedName(/*NameState=*/nullptr);
+  if (R == nullptr)
+    return first;
+  db.Names.push_back(R);
+  return db.First;
+}
+
+const char *parse_encoding(const char *first, const char *last, Db &db) {
+  db.First = first;
+  db.Last = last;
+  Node *R = db.parseEncoding();
+  if (R == nullptr)
+    return first;
+  db.Names.push_back(R);
+  return db.First;
+}
+
+const char* parse_discriminator(const char* first, const char* last);
 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_source_name(const char *, const char *, 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 &);
 const char *parse_substitution(const char *, const char *, Db &);
 
 
-// <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    # ?
-Node *Db::parseCtorDtorName(Node *&SoFar) {
-  if (SoFar->K == Node::KSpecialSubstitution) {
-    auto SSK = static_cast<SpecialSubstitution *>(SoFar)->SSK;
-    switch (SSK) {
-    case SpecialSubKind::string:
-    case SpecialSubKind::istream:
-    case SpecialSubKind::ostream:
-    case SpecialSubKind::iostream:
-      SoFar = make<ExpandedSpecialSubstitution>(SSK);
-    default:
-      break;
-    }
-  }
+// <name> ::= <nested-name> // N
+//        ::= <local-name> # See Scope Encoding below  // Z
+//        ::= <unscoped-template-name> <template-args>
+//        ::= <unscoped-name>
+//
+// <unscoped-template-name> ::= <unscoped-name>
+//                          ::= <substitution>
+Node *Db::parseName(NameState *State) {
+  consumeIf('L'); // extension
 
-  if (consumeIf('C')) {
-    bool IsInherited = consumeIf('I');
-    if (look() != '1' && look() != '2' && look() != '3' && look() != '5')
+  if (look() == 'N')
+    return parseNestedName(State);
+  if (look() == 'Z')
+    return parseLocalName(State);
+
+  //        ::= <unscoped-template-name> <template-args>
+  if (look() == 'S' && look(1) != 't') {
+    Node *S = legacyParse<parse_substitution>();
+    if (look() != 'I')
       return nullptr;
-    ++First;
-    ParsedCtorDtorCV = true;
-    if (IsInherited) {
-      if (legacyParse<parse_name>() == nullptr)
-        return nullptr;
-    }
-    return make<CtorDtorName>(SoFar, false);
+    Node *TA = legacyParse<parse_template_args>();
+    if (TA == nullptr)
+      return nullptr;
+    if (State) State->EndsWithTemplateArgs = true;
+    return make<NameWithTemplateArgs>(S, TA);
   }
 
-  if (look() == 'D' &&
-      (look(1) == '0' || look(1) == '1' || look(1) == '2' || look(1) == '5')) {
-    First += 2;
-    ParsedCtorDtorCV = true;
-    return make<CtorDtorName>(SoFar, true);
+  Node *N = parseUnscopedName(State);
+  if (N == nullptr)
+    return nullptr;
+  //        ::= <unscoped-template-name> <template-args>
+  if (look() == 'I') {
+    Subs.push_back(N);
+    Node *TA = legacyParse<parse_template_args>();
+    if (TA == nullptr)
+      return nullptr;
+    if (State) State->EndsWithTemplateArgs = true;
+    return make<NameWithTemplateArgs>(N, TA);
   }
-
-  return nullptr;
+  //        ::= <unscoped-name>
+  return N;
 }
 
-// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
-//               ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
-//
-// <prefix> ::= <prefix> <unqualified-name>
-//          ::= <template-prefix> <template-args>
-//          ::= <template-param>
-//          ::= <decltype>
-//          ::= # empty
-//          ::= <substitution>
-//          ::= <prefix> <data-member-prefix>
-//  extension ::= L
-//
-// <template-prefix> ::= <prefix> <template unqualified-name>
-//                   ::= <template-param>
-//                   ::= <substitution>
-Node *Db::parseNestedName() {
-  if (!consumeIf('N'))
+// <local-name> := Z <function encoding> E <entity name> [<discriminator>]
+//              := Z <function encoding> E s [<discriminator>]
+//              := Z <function encoding> Ed [ <parameter number> ] _ <entity name>
+Node *Db::parseLocalName(NameState *State) {
+  if (!consumeIf('Z'))
+    return nullptr;
+  Node *Encoding = parseEncoding();
+  if (Encoding == nullptr || !consumeIf('E'))
     return nullptr;
 
-  CV = parseCVQualifiers();
-  if      (consumeIf('O')) RefQuals = FrefQualRValue;
-  else if (consumeIf('R')) RefQuals = FrefQualLValue;
-  else                     RefQuals = FrefQualNone;
-
-  Node *SoFar = nullptr;
-  auto PushComponent = [&](Node *Comp) {
-    if (SoFar) SoFar = make<QualifiedName>(SoFar, Comp);
-    else       SoFar = Comp;
-    EndsWithTemplateArgs = false;
-  };
-
-  if (consumeIf("St"))
-    SoFar = make<NameType>("std");
-
-  while (!consumeIf('E')) {
-    consumeIf('L'); // extension
-
-    //          ::= <template-param>
-    if (look() == 'T') {
-      Node *TP = legacyParse<parse_template_param>();
-      if (TP == nullptr)
-        return nullptr;
-      PushComponent(TP);
-      Subs.push_back(SoFar);
-      continue;
-    }
-
-    //          ::= <template-prefix> <template-args>
-    if (look() == 'I') {
-      Node *TA;
-      {
-        SwapAndRestoreNameState SaveState(*this);
-        TA = legacyParse<parse_template_args>();
-      }
-      if (TA == nullptr || SoFar == nullptr)
-        return nullptr;
-      SoFar = make<NameWithTemplateArgs>(SoFar, TA);
-      EndsWithTemplateArgs = true;
-      Subs.push_back(SoFar);
-      continue;
-    }
-
-    //          ::= <decltype>
-    if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {
-      Node *DT;
-      {
-        SwapAndRestoreNameState SaveState(*this);
-        DT = parseDecltype();
-      }
-      if (DT == nullptr)
-        return nullptr;
-      PushComponent(DT);
-      Subs.push_back(SoFar);
-      continue;
-    }
-
-    //          ::= <substitution>
-    if (look() == 'S' && look(1) != 't') {
-      Node *S = legacyParse<parse_substitution>();
-      if (S == nullptr)
-        return nullptr;
-      PushComponent(S);
-      if (SoFar != S)
-        Subs.push_back(S);
-      continue;
-    }
-
-    // Parse an <unqualified-name> thats actually a <ctor-dtor-name>.
-    if (look() == 'C' || look() == 'D') {
-      if (SoFar == nullptr)
-        return nullptr;
-      Node *CtorDtor = parseCtorDtorName(SoFar);
-      if (CtorDtor == nullptr)
-        return nullptr;
-      PushComponent(CtorDtor);
-      SoFar = parseAbiTags(SoFar);
-      if (SoFar == nullptr)
-        return nullptr;
-      Subs.push_back(SoFar);
-      continue;
-    }
+  if (consumeIf('s')) {
+    First = parse_discriminator(First, Last);
+    return make<QualifiedName>(Encoding, make<NameType>("string literal"));
+  }
 
-    //          ::= <prefix> <unqualified-name>
-    Node *N = legacyParse<parse_unqualified_name>();
+  if (consumeIf('d')) {
+    parseNumber(true);
+    if (!consumeIf('_'))
+      return nullptr;
+    Node *N = parseName(State);
     if (N == nullptr)
       return nullptr;
-    PushComponent(N);
-    Subs.push_back(SoFar);
+    return make<QualifiedName>(Encoding, N);
   }
 
-  if (SoFar == nullptr || Subs.empty())
+  Node *Entity = parseName(State);
+  if (Entity == nullptr)
     return nullptr;
-
-  Subs.pop_back();
-  return SoFar;
+  First = parse_discriminator(First, Last);
+  return make<QualifiedName>(Encoding, Entity);
 }
 
-// <abi-tags> ::= <abi-tag> [<abi-tags>]
-// <abi-tag> ::= B <source-name>
-Node *Db::parseAbiTags(Node *N) {
-  while (consumeIf('B')) {
-    StringView SN = parseBareSourceName();
-    if (SN.empty())
-      return nullptr;
-    N = make<AbiTagAttr>(N, SN);
-  }
-  return N;
+// <unscoped-name> ::= <unqualified-name>
+//                 ::= St <unqualified-name>   # ::std::
+// extension       ::= StL<unqualified-name>
+Node *Db::parseUnscopedName(NameState *State) {
+ if (consumeIf("StL") || consumeIf("St")) {
+   Node *R = parseUnqualifiedName(State);
+   if (R == nullptr)
+     return nullptr;
+   return make<StdQualifiedName>(R);
+ }
+ return parseUnqualifiedName(State);
 }
 
-// <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);
+// <unqualified-name> ::= <operator-name> [abi-tags]
+//                    ::= <ctor-dtor-name>
+//                    ::= <source-name>
+//                    ::= <unnamed-type-name>
+// FIXME:             ::= DC <source-name>+ E      # structured binding declaration
+Node *Db::parseUnqualifiedName(NameState *State) {
+ // <ctor-dtor-name>s are special-cased in parseNestedName().
+ Node *Result;
+ if (look() == 'U')
+   Result = parseUnnamedTypeName(State);
+ else if (look() >= '1' && look() <= '9')
+   Result = parseSourceName(State);
+ else
+   Result = parseOperatorName(State);
+ if (Result != nullptr)
+   Result = parseAbiTags(Result);
+ return Result;
 }
 
-// <positive length number> ::= [0-9]*
-bool Db::parsePositiveInteger(size_t *Out) {
-  *Out = 0;
-  if (look() < '0' || look() > '9')
-    return true;
-  while (look() >= '0' && look() <= '9') {
-    *Out *= 10;
-    *Out += static_cast<size_t>(consume() - '0');
+// <unnamed-type-name> ::= Ut [<nonnegative number>] _
+//                     ::= <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
+Node *Db::parseUnnamedTypeName(NameState *) {
+  if (consumeIf("Ut")) {
+    StringView Count = parseNumber();
+    if (!consumeIf('_'))
+      return nullptr;
+    return make<UnnamedTypeName>(Count);
   }
-  return false;
-}
-
-StringView Db::parseBareSourceName() {
-  size_t Int = 0;
-  if (parsePositiveInteger(&Int) || numLeft() < Int)
-    return StringView();
-  StringView R(First, First + Int);
-  First += Int;
-  return R;
+  if (consumeIf("Ul")) {
+    NodeArray Params;
+    if (!consumeIf("vE")) {
+      size_t ParamsBegin = Names.size();
+      do {
+        Node *P = parseType();
+        if (P == nullptr)
+          return nullptr;
+        Names.push_back(P);
+      } while (!consumeIf('E'));
+      Params = popTrailingNodeArray(ParamsBegin);
+    }
+    StringView Count = parseNumber();
+    if (!consumeIf('_'))
+      return nullptr;
+    return make<ClosureTypeName>(Params, Count);
+  }
+  return nullptr;
 }
 
-// <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E
-//
-// <exception-spec> ::= Do                # non-throwing exception-specification (e.g., noexcept, throw())
-//                  ::= DO <expression> E # computed (instantiation-dependent) noexcept
-//                  ::= Dw <type>+ E      # dynamic exception specification with instantiation-dependent types
-//
-// <ref-qualifier> ::= R                   # & ref-qualifier
-// <ref-qualifier> ::= O                   # && ref-qualifier
-Node *Db::parseFunctionType() {
-  Qualifiers CVQuals = parseCVQualifiers();
-
-  Node *ExceptionSpec = nullptr;
-  if (consumeIf("Do")) {
-    ExceptionSpec = make<NameType>("noexcept");
-  } else if (consumeIf("DO")) {
-    Node *E = parseExpr();
-    if (E == nullptr || !consumeIf('E'))
-      return nullptr;
-    ExceptionSpec = make<NoexceptSpec>(E);
-  } else if (consumeIf("Dw")) {
-    size_t SpecsBegin = Names.size();
-    while (!consumeIf('E')) {
-      Node *T = parseType();
-      if (T == nullptr)
-        return nullptr;
-      Names.push_back(T);
+// <source-name> ::= <positive length number> <identifier>
+Node *Db::parseSourceName(NameState *) {
+  size_t Length = 0;
+  if (parsePositiveInteger(&Length))
+    return nullptr;
+  if (numLeft() < Length || Length == 0)
+    return nullptr;
+  StringView Name(First, First + Length);
+  First += Length;
+  if (Name.startsWith("_GLOBAL__N"))
+    return make<NameType>("(anonymous namespace)");
+  return make<NameType>(Name);
+}
+
+//   <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    # >>=
+//                   ::= ss    # <=> C++2a
+//                   ::= v <digit> <source-name>        # vendor extended operator
+Node *Db::parseOperatorName(NameState *State) {
+  switch (look()) {
+  case 'a':
+    switch (look(1)) {
+    case 'a':
+      First += 2;
+      return make<NameType>("operator&&");
+    case 'd':
+    case 'n':
+      First += 2;
+      return make<NameType>("operator&");
+    case 'N':
+      First += 2;
+      return make<NameType>("operator&=");
+    case 'S':
+      First += 2;
+      return make<NameType>("operator=");
     }
-    ExceptionSpec =
-      make<DynamicExceptionSpec>(popTrailingNodeArray(SpecsBegin));
-  }
-
-  consumeIf("Dx"); // transaction safe
-
-  if (!consumeIf('F'))
     return nullptr;
-  consumeIf('Y'); // extern "C"
-  Node *ReturnType = parseType();
-  if (ReturnType == nullptr)
-    return nullptr;
-
-  FunctionRefQual ReferenceQualifier = FrefQualNone;
-  size_t ParamsBegin = Names.size();
-  while (true) {
-    if (consumeIf('E'))
-      break;
-    if (consumeIf('v'))
-      continue;
-    if (consumeIf("RE")) {
-      ReferenceQualifier = FrefQualLValue;
-      break;
+  case 'c':
+    switch (look(1)) {
+    case 'l':
+      First += 2;
+      return make<NameType>("operator()");
+    case 'm':
+      First += 2;
+      return make<NameType>("operator,");
+    case 'o':
+      First += 2;
+      return make<NameType>("operator~");
+    //                   ::= cv <type>    # (cast)
+    case 'v': {
+      SwapAndRestore<bool> SaveTemplate(TryToParseTemplateArgs, false);
+      First += 2;
+      Node *Ty = parseType();
+      if (Ty == nullptr)
+        return nullptr;
+      if (State) State->CtorDtorConversion = true;
+      return make<ConversionOperatorType>(Ty);
     }
-    if (consumeIf("OE")) {
-      ReferenceQualifier = FrefQualRValue;
-      break;
     }
-    Node *T = parseType();
-    if (T == nullptr)
-      return nullptr;
-    Names.push_back(T);
-  }
-
-  NodeArray Params = popTrailingNodeArray(ParamsBegin);
-  return make<FunctionType>(ReturnType, Params, CVQuals,
-                            ReferenceQualifier, ExceptionSpec);
-}
-
-// extension:
-// <vector-type>           ::= Dv <positive dimension number> _ <extended element type>
-//                         ::= Dv [<dimension expression>] _ <element type>
-// <extended element type> ::= <element type>
-//                         ::= p # AltiVec vector pixel
-Node *Db::parseVectorType() {
-  if (!consumeIf("Dv"))
-    return nullptr;
-  if (look() >= '1' && look() <= '9') {
-    StringView DimensionNumber = parseNumber();
-    if (!consumeIf('_'))
-      return nullptr;
-    if (consumeIf('p'))
-      return make<VectorType>(DimensionNumber);
-    Node *ElemType = parseType();
-    if (ElemType == nullptr)
-      return nullptr;
-    return make<VectorType>(ElemType, DimensionNumber);
-  }
-
-  if (!consumeIf('_')) {
-    Node *DimExpr = parseExpr();
-    if (!DimExpr)
-      return nullptr;
-    if (!consumeIf('_'))
-      return nullptr;
-    Node *ElemType = parseType();
-    if (!ElemType)
-      return nullptr;
-    return make<VectorType>(ElemType, DimExpr);
-  }
-  Node *ElemType = parseType();
-  if (!ElemType)
     return nullptr;
-  return make<VectorType>(ElemType, StringView());
-}
-
-// <decltype>  ::= Dt <expression> E  # decltype of an id-expression or class member access (C++0x)
-//             ::= DT <expression> E  # decltype of an expression (C++0x)
-Node *Db::parseDecltype() {
-  if (!consumeIf('D'))
+  case 'd':
+    switch (look(1)) {
+    case 'a':
+      First += 2;
+      return make<NameType>("operator delete[]");
+    case 'e':
+      First += 2;
+      return make<NameType>("operator*");
+    case 'l':
+      First += 2;
+      return make<NameType>("operator delete");
+    case 'v':
+      First += 2;
+      return make<NameType>("operator/");
+    case 'V':
+      First += 2;
+      return make<NameType>("operator/=");
+    }
     return nullptr;
-  if (!consumeIf('t') && !consumeIf('T'))
+  case 'e':
+    switch (look(1)) {
+    case 'o':
+      First += 2;
+      return make<NameType>("operator^");
+    case 'O':
+      First += 2;
+      return make<NameType>("operator^=");
+    case 'q':
+      First += 2;
+      return make<NameType>("operator==");
+    }
     return nullptr;
-  Node *E = parseExpr();
-  if (E == nullptr)
+  case 'g':
+    switch (look(1)) {
+    case 'e':
+      First += 2;
+      return make<NameType>("operator>=");
+    case 't':
+      First += 2;
+      return make<NameType>("operator>");
+    }
     return nullptr;
-  if (!consumeIf('E'))
+  case 'i':
+    if (look(1) == 'x') {
+      First += 2;
+      return make<NameType>("operator[]");
+    }
     return nullptr;
-  return make<EnclosingExpr>("decltype(", E, ")");
-}
-
-// <array-type> ::= A <positive dimension number> _ <element type>
-//              ::= A [<dimension expression>] _ <element type>
-Node *Db::parseArrayType() {
-  if (!consumeIf('A'))
+  case 'l':
+    switch (look(1)) {
+    case 'e':
+      First += 2;
+      return make<NameType>("operator<=");
+    //                   ::= li <source-name>  # operator ""
+    case 'i': {
+      First += 2;
+      Node *SN = parseSourceName(State);
+      if (SN == nullptr)
+        return nullptr;
+      return make<LiteralOperator>(SN);
+    }
+    case 's':
+      First += 2;
+      return make<NameType>("operator<<");
+    case 'S':
+      First += 2;
+      return make<NameType>("operator<<=");
+    case 't':
+      First += 2;
+      return make<NameType>("operator<");
+    }
     return nullptr;
-
-  if (std::isdigit(look())) {
-    StringView Dimension = parseNumber();
-    if (!consumeIf('_'))
-      return nullptr;
-    Node *Ty = parseType();
-    if (Ty == nullptr)
-      return nullptr;
-    return make<ArrayType>(Ty, Dimension);
-  }
-
-  if (!consumeIf('_')) {
-    Node *DimExpr = parseExpr();
-    if (DimExpr == nullptr)
-      return nullptr;
-    if (!consumeIf('_'))
-      return nullptr;
-    Node *ElementType = parseType();
-    if (ElementType == nullptr)
-      return nullptr;
-    return make<ArrayType>(ElementType, DimExpr);
-  }
-
-  Node *Ty = parseType();
-  if (Ty == nullptr)
+  case 'm':
+    switch (look(1)) {
+    case 'i':
+      First += 2;
+      return make<NameType>("operator-");
+    case 'I':
+      First += 2;
+      return make<NameType>("operator-=");
+    case 'l':
+      First += 2;
+      return make<NameType>("operator*");
+    case 'L':
+      First += 2;
+      return make<NameType>("operator*=");
+    case 'm':
+      First += 2;
+      return make<NameType>("operator--");
+    }
     return nullptr;
-  return make<ArrayType>(Ty);
-}
-
-// <pointer-to-member-type> ::= M <class type> <member type>
-Node *Db::parsePointerToMemberType() {
-  if (!consumeIf('M'))
+  case 'n':
+    switch (look(1)) {
+    case 'a':
+      First += 2;
+      return make<NameType>("operator new[]");
+    case 'e':
+      First += 2;
+      return make<NameType>("operator!=");
+    case 'g':
+      First += 2;
+      return make<NameType>("operator-");
+    case 't':
+      First += 2;
+      return make<NameType>("operator!");
+    case 'w':
+      First += 2;
+      return make<NameType>("operator new");
+    }
     return nullptr;
-  Node *ClassType = parseType();
-  if (ClassType == nullptr)
+  case 'o':
+    switch (look(1)) {
+    case 'o':
+      First += 2;
+      return make<NameType>("operator||");
+    case 'r':
+      First += 2;
+      return make<NameType>("operator|");
+    case 'R':
+      First += 2;
+      return make<NameType>("operator|=");
+    }
     return nullptr;
-  Node *MemberType = parseType();
-  if (MemberType == nullptr)
+  case 'p':
+    switch (look(1)) {
+    case 'm':
+      First += 2;
+      return make<NameType>("operator->*");
+    case 'l':
+      First += 2;
+      return make<NameType>("operator+");
+    case 'L':
+      First += 2;
+      return make<NameType>("operator+=");
+    case 'p':
+      First += 2;
+      return make<NameType>("operator++");
+    case 's':
+      First += 2;
+      return make<NameType>("operator+");
+    case 't':
+      First += 2;
+      return make<NameType>("operator->");
+    }
     return nullptr;
-  return make<PointerToMemberType>(ClassType, MemberType);
+  case 'q':
+    if (look(1) == 'u') {
+      First += 2;
+      return make<NameType>("operator?");
+    }
+    return nullptr;
+  case 'r':
+    switch (look(1)) {
+    case 'm':
+      First += 2;
+      return make<NameType>("operator%");
+    case 'M':
+      First += 2;
+      return make<NameType>("operator%=");
+    case 's':
+      First += 2;
+      return make<NameType>("operator>>");
+    case 'S':
+      First += 2;
+      return make<NameType>("operator>>=");
+    }
+    return nullptr;
+  case 's':
+    if (look(1) == 's') {
+      First += 2;
+      return make<NameType>("operator<=>");
+    }
+    return nullptr;
+  // ::= v <digit> <source-name>        # vendor extended operator
+  case 'v':
+    if (std::isdigit(look(1))) {
+      First += 2;
+      Node *SN = parseSourceName(State);
+      if (SN == nullptr)
+        return nullptr;
+      return make<ConversionOperatorType>(SN);
+    }
+    return nullptr;
+  }
+  return nullptr;
 }
 
-// <class-enum-type> ::= <name>     # non-dependent type name, dependent type name, or dependent typename-specifier
-//                   ::= Ts <name>  # dependent elaborated type specifier using 'struct' or 'class'
-//                   ::= Tu <name>  # dependent elaborated type specifier using 'union'
-//                   ::= Te <name>  # dependent elaborated type specifier using 'enum'
-Node *Db::parseClassEnumType() {
-  StringView ElabSpef;
-  if (consumeIf("Ts"))
-    ElabSpef = "struct";
-  else if (consumeIf("Tu"))
-    ElabSpef = "union";
-  else if (consumeIf("Te"))
-    ElabSpef = "enum";
+// <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    # ?
+Node *Db::parseCtorDtorName(Node *&SoFar, NameState *State) {
+  if (SoFar->K == Node::KSpecialSubstitution) {
+    auto SSK = static_cast<SpecialSubstitution *>(SoFar)->SSK;
+    switch (SSK) {
+    case SpecialSubKind::string:
+    case SpecialSubKind::istream:
+    case SpecialSubKind::ostream:
+    case SpecialSubKind::iostream:
+      SoFar = make<ExpandedSpecialSubstitution>(SSK);
+    default:
+      break;
+    }
+  }
 
-  Node *Name = legacyParse<parse_name>();
-  if (Name == nullptr)
-    return nullptr;
+  if (consumeIf('C')) {
+    bool IsInherited = consumeIf('I');
+    if (look() != '1' && look() != '2' && look() != '3' && look() != '5')
+      return nullptr;
+    ++First;
+    if (State) State->CtorDtorConversion = true;
+    if (IsInherited) {
+      if (parseName() == nullptr)
+        return nullptr;
+    }
+    return make<CtorDtorName>(SoFar, false);
+  }
 
-  if (!ElabSpef.empty())
-    return make<ElaboratedTypeSpefType>(ElabSpef, Name);
+  if (look() == 'D' &&
+      (look(1) == '0' || look(1) == '1' || look(1) == '2' || look(1) == '5')) {
+    First += 2;
+    if (State) State->CtorDtorConversion = true;
+    return make<CtorDtorName>(SoFar, true);
+  }
 
-  return Name;
+  return nullptr;
 }
 
-// <qualified-type>     ::= <qualifiers> <type>
-// <qualifiers> ::= <extended-qualifier>* <CV-qualifiers>
-// <extended-qualifier> ::= U <source-name> [<template-args>] # vendor extended type qualifier
-Node *Db::parseQualifiedType() {
-  if (consumeIf('U')) {
-    StringView Qual = parseBareSourceName();
-    if (Qual.empty())
-      return nullptr;
+// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
+//               ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
+//
+// <prefix> ::= <prefix> <unqualified-name>
+//          ::= <template-prefix> <template-args>
+//          ::= <template-param>
+//          ::= <decltype>
+//          ::= # empty
+//          ::= <substitution>
+//          ::= <prefix> <data-member-prefix>
+//  extension ::= L
+//
+// <template-prefix> ::= <prefix> <template unqualified-name>
+//                   ::= <template-param>
+//                   ::= <substitution>
+Node *Db::parseNestedName(NameState *State) {
+  if (!consumeIf('N'))
+    return nullptr;
 
-    // FIXME parse the optional <template-args> here!
+  Qualifiers CVTmp = parseCVQualifiers();
+  if (State) State->CVQualifiers = CVTmp;
 
-    // extension            ::= U <objc-name> <objc-type>  # objc-type<identifier>
-    if (Qual.startsWith("objcproto")) {
-      StringView ProtoSourceName = Qual.dropFront(std::strlen("objcproto"));
-      StringView Proto;
-      {
-        SwapAndRestore<const char *> SaveFirst(First, ProtoSourceName.begin()),
-                                     SaveLast(Last, ProtoSourceName.end());
-        Proto = parseBareSourceName();
-      }
-      if (Proto.empty())
+  if (consumeIf('O')) {
+    if (State) State->ReferenceQualifier = FrefQualRValue;
+  } else if (consumeIf('R')) {
+    if (State) State->ReferenceQualifier = FrefQualLValue;
+  } else
+    if (State) State->ReferenceQualifier = FrefQualNone;
+
+  Node *SoFar = nullptr;
+  auto PushComponent = [&](Node *Comp) {
+    if (SoFar) SoFar = make<QualifiedName>(SoFar, Comp);
+    else       SoFar = Comp;
+    if (State) State->EndsWithTemplateArgs = false;
+  };
+
+  if (consumeIf("St"))
+    SoFar = make<NameType>("std");
+
+  while (!consumeIf('E')) {
+    consumeIf('L'); // extension
+
+    //          ::= <template-param>
+    if (look() == 'T') {
+      Node *TP = legacyParse<parse_template_param>();
+      if (TP == nullptr)
         return nullptr;
-      Node *Child = parseQualifiedType();
-      if (Child == nullptr)
+      PushComponent(TP);
+      Subs.push_back(SoFar);
+      continue;
+    }
+
+    //          ::= <template-prefix> <template-args>
+    if (look() == 'I') {
+      Node *TA = legacyParse<parse_template_args>();
+      if (TA == nullptr || SoFar == nullptr)
         return nullptr;
-      return make<ObjCProtoName>(Child, Proto);
+      SoFar = make<NameWithTemplateArgs>(SoFar, TA);
+      if (State) State->EndsWithTemplateArgs = true;
+      Subs.push_back(SoFar);
+      continue;
     }
 
-    Node *Child = parseQualifiedType();
-    if (Child == nullptr)
+    //          ::= <decltype>
+    if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {
+      Node *DT = parseDecltype();
+      if (DT == nullptr)
+        return nullptr;
+      PushComponent(DT);
+      Subs.push_back(SoFar);
+      continue;
+    }
+
+    //          ::= <substitution>
+    if (look() == 'S' && look(1) != 't') {
+      Node *S = legacyParse<parse_substitution>();
+      if (S == nullptr)
+        return nullptr;
+      PushComponent(S);
+      if (SoFar != S)
+        Subs.push_back(S);
+      continue;
+    }
+
+    // Parse an <unqualified-name> thats actually a <ctor-dtor-name>.
+    if (look() == 'C' || look() == 'D') {
+      if (SoFar == nullptr)
+        return nullptr;
+      Node *CtorDtor = parseCtorDtorName(SoFar, State);
+      if (CtorDtor == nullptr)
+        return nullptr;
+      PushComponent(CtorDtor);
+      SoFar = parseAbiTags(SoFar);
+      if (SoFar == nullptr)
+        return nullptr;
+      Subs.push_back(SoFar);
+      continue;
+    }
+
+    //          ::= <prefix> <unqualified-name>
+    Node *N = parseUnqualifiedName(State);
+    if (N == nullptr)
       return nullptr;
-    return make<VendorExtQualType>(Child, Qual);
+    PushComponent(N);
+    Subs.push_back(SoFar);
   }
 
-  Qualifiers Quals = parseCVQualifiers();
-  Node *Ty = parseType();
-  if (Ty == nullptr)
+  if (SoFar == nullptr || Subs.empty())
     return nullptr;
-  if (Quals != QualNone)
-    Ty = make<QualType>(Ty, Quals);
-  return Ty;
+
+  Subs.pop_back();
+  return SoFar;
 }
 
-// <type>      ::= <builtin-type>
-//             ::= <qualified-type>
-//             ::= <function-type>
-//             ::= <class-enum-type>
-//             ::= <array-type>
-//             ::= <pointer-to-member-type>
-//             ::= <template-param>
-//             ::= <template-template-param> <template-args>
-//             ::= <decltype>
-//             ::= P <type>        # pointer
-//             ::= R <type>        # l-value reference
-//             ::= O <type>        # r-value reference (C++11)
-//             ::= C <type>        # complex pair (C99)
-//             ::= G <type>        # imaginary (C99)
-//             ::= <substitution>  # See Compression below
-// 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>
-Node *Db::parseType() {
-  Node *Result = nullptr;
+// <abi-tags> ::= <abi-tag> [<abi-tags>]
+// <abi-tag> ::= B <source-name>
+Node *Db::parseAbiTags(Node *N) {
+  while (consumeIf('B')) {
+    StringView SN = parseBareSourceName();
+    if (SN.empty())
+      return nullptr;
+    N = make<AbiTagAttr>(N, SN);
+  }
+  return N;
+}
 
-  switch (look()) {
-  //             ::= <qualified-type>
-  case 'r':
-  case 'V':
-  case 'K': {
-    unsigned AfterQuals = 0;
-    if (look(AfterQuals) == 'r') ++AfterQuals;
-    if (look(AfterQuals) == 'V') ++AfterQuals;
-    if (look(AfterQuals) == 'K') ++AfterQuals;
+// <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);
+}
 
-    if (look(AfterQuals) == 'F' ||
-        (look(AfterQuals) == 'D' &&
-         (look(AfterQuals + 1) == 'o' || look(AfterQuals + 1) == 'O' ||
-          look(AfterQuals + 1) == 'w' || look(AfterQuals + 1) == 'x'))) {
-      Result = parseFunctionType();
-      break;
-    }
-    _LIBCPP_FALLTHROUGH();
-  }
-  case 'U': {
-    Result = parseQualifiedType();
-    break;
+// <positive length number> ::= [0-9]*
+bool Db::parsePositiveInteger(size_t *Out) {
+  *Out = 0;
+  if (look() < '0' || look() > '9')
+    return true;
+  while (look() >= '0' && look() <= '9') {
+    *Out *= 10;
+    *Out += static_cast<size_t>(consume() - '0');
   }
-  // <builtin-type> ::= v    # void
-  case 'v':
-    ++First;
-    return make<NameType>("void");
-  //                ::= w    # wchar_t
-  case 'w':
-    ++First;
-    return make<NameType>("wchar_t");
-  //                ::= b    # bool
-  case 'b':
-    ++First;
-    return make<NameType>("bool");
-  //                ::= c    # char
-  case 'c':
-    ++First;
-    return make<NameType>("char");
-  //                ::= a    # signed char
-  case 'a':
-    ++First;
-    return make<NameType>("signed char");
-  //                ::= h    # unsigned char
-  case 'h':
-    ++First;
-    return make<NameType>("unsigned char");
-  //                ::= s    # short
-  case 's':
-    ++First;
-    return make<NameType>("short");
-  //                ::= t    # unsigned short
-  case 't':
-    ++First;
-    return make<NameType>("unsigned short");
-  //                ::= i    # int
-  case 'i':
-    ++First;
-    return make<NameType>("int");
-  //                ::= j    # unsigned int
-  case 'j':
-    ++First;
-    return make<NameType>("unsigned int");
-  //                ::= l    # long
-  case 'l':
-    ++First;
-    return make<NameType>("long");
-  //                ::= m    # unsigned long
-  case 'm':
-    ++First;
-    return make<NameType>("unsigned long");
-  //                ::= x    # long long, __int64
-  case 'x':
-    ++First;
-    return make<NameType>("long long");
-  //                ::= y    # unsigned long long, __int64
-  case 'y':
-    ++First;
-    return make<NameType>("unsigned long long");
-  //                ::= n    # __int128
-  case 'n':
-    ++First;
-    return make<NameType>("__int128");
-  //                ::= o    # unsigned __int128
-  case 'o':
-    ++First;
-    return make<NameType>("unsigned __int128");
-  //                ::= f    # float
-  case 'f':
-    ++First;
-    return make<NameType>("float");
-  //                ::= d    # double
-  case 'd':
-    ++First;
-    return make<NameType>("double");
-  //                ::= e    # long double, __float80
-  case 'e':
-    ++First;
-    return make<NameType>("long double");
-  //                ::= g    # __float128
-  case 'g':
-    ++First;
-    return make<NameType>("__float128");
-  //                ::= z    # ellipsis
-  case 'z':
-    ++First;
-    return make<NameType>("...");
+  return false;
+}
 
-  // <builtin-type> ::= u <source-name>    # vendor extended type
-  case 'u': {
-    ++First;
-    StringView Res = parseBareSourceName();
-    if (Res.empty())
+StringView Db::parseBareSourceName() {
+  size_t Int = 0;
+  if (parsePositiveInteger(&Int) || numLeft() < Int)
+    return StringView();
+  StringView R(First, First + Int);
+  First += Int;
+  return R;
+}
+
+// <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E
+//
+// <exception-spec> ::= Do                # non-throwing exception-specification (e.g., noexcept, throw())
+//                  ::= DO <expression> E # computed (instantiation-dependent) noexcept
+//                  ::= Dw <type>+ E      # dynamic exception specification with instantiation-dependent types
+//
+// <ref-qualifier> ::= R                   # & ref-qualifier
+// <ref-qualifier> ::= O                   # && ref-qualifier
+Node *Db::parseFunctionType() {
+  Qualifiers CVQuals = parseCVQualifiers();
+
+  Node *ExceptionSpec = nullptr;
+  if (consumeIf("Do")) {
+    ExceptionSpec = make<NameType>("noexcept");
+  } else if (consumeIf("DO")) {
+    Node *E = parseExpr();
+    if (E == nullptr || !consumeIf('E'))
       return nullptr;
-    return make<NameType>(Res);
+    ExceptionSpec = make<NoexceptSpec>(E);
+  } else if (consumeIf("Dw")) {
+    size_t SpecsBegin = Names.size();
+    while (!consumeIf('E')) {
+      Node *T = parseType();
+      if (T == nullptr)
+        return nullptr;
+      Names.push_back(T);
+    }
+    ExceptionSpec =
+      make<DynamicExceptionSpec>(popTrailingNodeArray(SpecsBegin));
   }
-  case 'D':
-    switch (look(1)) {
-    //                ::= Dd   # IEEE 754r decimal floating point (64 bits)
-    case 'd':
-      First += 2;
-      return make<NameType>("decimal64");
-    //                ::= De   # IEEE 754r decimal floating point (128 bits)
-    case 'e':
-      First += 2;
-      return make<NameType>("decimal128");
-    //                ::= Df   # IEEE 754r decimal floating point (32 bits)
-    case 'f':
-      First += 2;
-      return make<NameType>("decimal32");
-    //                ::= Dh   # IEEE 754r half-precision floating point (16 bits)
-    case 'h':
-      First += 2;
-      return make<NameType>("decimal16");
-    //                ::= Di   # char32_t
-    case 'i':
-      First += 2;
-      return make<NameType>("char32_t");
-    //                ::= Ds   # char16_t
-    case 's':
-      First += 2;
-      return make<NameType>("char16_t");
-    //                ::= Da   # auto (in dependent new-expressions)
-    case 'a':
-      First += 2;
-      return make<NameType>("auto");
-    //                ::= Dc   # decltype(auto)
-    case 'c':
-      First += 2;
-      return make<NameType>("decltype(auto)");
-    //                ::= Dn   # std::nullptr_t (i.e., decltype(nullptr))
-    case 'n':
-      First += 2;
-      return make<NameType>("std::nullptr_t");
 
-    //             ::= <decltype>
-    case 't':
-    case 'T': {
-      Result = parseDecltype();
-      break;
-    }
-    // extension   ::= <vector-type> # <vector-type> starts with Dv
-    case 'v': {
-      Result = parseVectorType();
+  consumeIf("Dx"); // transaction safe
+
+  if (!consumeIf('F'))
+    return nullptr;
+  consumeIf('Y'); // extern "C"
+  Node *ReturnType = parseType();
+  if (ReturnType == nullptr)
+    return nullptr;
+
+  FunctionRefQual ReferenceQualifier = FrefQualNone;
+  size_t ParamsBegin = Names.size();
+  while (true) {
+    if (consumeIf('E'))
       break;
-    }
-    //           ::= Dp <type>       # pack expansion (C++0x)
-    case 'p': {
-      First += 2;
-      Node *Child = parseType();
-      if (!Child)
-        return nullptr;
-      Result = make<ParameterPackExpansion>(Child);
+    if (consumeIf('v'))
+      continue;
+    if (consumeIf("RE")) {
+      ReferenceQualifier = FrefQualLValue;
       break;
     }
-    // Exception specifier on a function type.
-    case 'o':
-    case 'O':
-    case 'w':
-    // Transaction safe function type.
-    case 'x':
-      Result = parseFunctionType();
+    if (consumeIf("OE")) {
+      ReferenceQualifier = FrefQualRValue;
       break;
     }
-    break;
-  //             ::= <function-type>
-  case 'F': {
-    Result = parseFunctionType();
-    break;
+    Node *T = parseType();
+    if (T == nullptr)
+      return nullptr;
+    Names.push_back(T);
   }
-  //             ::= <array-type>
-  case 'A': {
-    Result = parseArrayType();
-    break;
-  }
-  //             ::= <pointer-to-member-type>
-  case 'M': {
-    Result = parsePointerToMemberType();
-    break;
-  }
-  //             ::= <template-param>
-  case 'T': {
-    // This could be an elaborate type specifier on a <class-enum-type>.
-    if (look(1) == 's' || look(1) == 'u' || look(1) == 'e') {
-      Result = parseClassEnumType();
-      break;
-    }
-
-    Result = legacyParse<parse_template_param>();
-    if (Result == nullptr)
-      return nullptr;
 
-    // Result could be either of:
-    //   <type>        ::= <template-param>
-    //   <type>        ::= <template-template-param> <template-args>
-    //
-    //   <template-template-param> ::= <template-param>
-    //                             ::= <substitution>
-    //
-    // If this is followed by some <template-args>, and we're permitted to
-    // parse them, take the second production.
+  NodeArray Params = popTrailingNodeArray(ParamsBegin);
+  return make<FunctionType>(ReturnType, Params, CVQuals,
+                            ReferenceQualifier, ExceptionSpec);
+}
 
-    if (TryToParseTemplateArgs && look() == 'I') {
-      Node *TA = legacyParse<parse_template_args>();
-      if (TA == nullptr)
-        return nullptr;
-      Result = make<NameWithTemplateArgs>(Result, TA);
-    }
-    break;
-  }
-  //             ::= P <type>        # pointer
-  case 'P': {
-    ++First;
-    Node *Ptr = parseType();
-    if (Ptr == nullptr)
+// extension:
+// <vector-type>           ::= Dv <positive dimension number> _ <extended element type>
+//                         ::= Dv [<dimension expression>] _ <element type>
+// <extended element type> ::= <element type>
+//                         ::= p # AltiVec vector pixel
+Node *Db::parseVectorType() {
+  if (!consumeIf("Dv"))
+    return nullptr;
+  if (look() >= '1' && look() <= '9') {
+    StringView DimensionNumber = parseNumber();
+    if (!consumeIf('_'))
       return nullptr;
-    Result = make<PointerType>(Ptr);
-    break;
-  }
-  //             ::= R <type>        # l-value reference
-  case 'R': {
-    ++First;
-    Node *Ref = parseType();
-    if (Ref == nullptr)
+    if (consumeIf('p'))
+      return make<VectorType>(DimensionNumber);
+    Node *ElemType = parseType();
+    if (ElemType == nullptr)
       return nullptr;
-    Result = make<LValueReferenceType>(Ref);
-    break;
+    return make<VectorType>(ElemType, DimensionNumber);
   }
-  //             ::= O <type>        # r-value reference (C++11)
-  case 'O': {
-    ++First;
-    Node *Ref = parseType();
-    if (Ref == nullptr)
+
+  if (!consumeIf('_')) {
+    Node *DimExpr = parseExpr();
+    if (!DimExpr)
       return nullptr;
-    Result = make<RValueReferenceType>(Ref);
-    break;
-  }
-  //             ::= C <type>        # complex pair (C99)
-  case 'C': {
-    ++First;
-    Node *P = parseType();
-    if (P == nullptr)
+    if (!consumeIf('_'))
       return nullptr;
-    Result = make<PostfixQualifiedType>(P, " complex");
-    break;
-  }
-  //             ::= G <type>        # imaginary (C99)
-  case 'G': {
-    ++First;
-    Node *P = parseType();
-    if (P == nullptr)
-      return P;
-    Result = make<PostfixQualifiedType>(P, " imaginary");
-    break;
-  }
-  //             ::= <substitution>  # See Compression below
-  case 'S': {
-    if (look(1) && look(1) != 't') {
-      Node *Sub = legacyParse<parse_substitution>();
-      if (Sub == nullptr)
-        return nullptr;
-
-      // Sub could be either of:
-      //   <type>        ::= <substitution>
-      //   <type>        ::= <template-template-param> <template-args>
-      //
-      //   <template-template-param> ::= <template-param>
-      //                             ::= <substitution>
-      //
-      // If this is followed by some <template-args>, and we're permitted to
-      // parse them, take the second production.
-
-      if (TryToParseTemplateArgs && look() == 'I') {
-        Node *TA = legacyParse<parse_template_args>();
-        if (TA == nullptr)
-          return nullptr;
-        Result = make<NameWithTemplateArgs>(Sub, TA);
-        break;
-      }
-
-      // If all we parsed was a substitution, don't re-insert into the
-      // substitution table.
-      return Sub;
-    }
-    _LIBCPP_FALLTHROUGH();
-  }
-  //        ::= <class-enum-type>
-  default: {
-    Result = parseClassEnumType();
-    break;
-  }
+    Node *ElemType = parseType();
+    if (!ElemType)
+      return nullptr;
+    return make<VectorType>(ElemType, DimExpr);
   }
-
-  // If we parsed a type, insert it into the substitution table. Note that all
-  // <builtin-type>s and <substitution>s have already bailed out, because they
-  // don't get substitutions.
-  if (Result != nullptr)
-    Subs.push_back(Result);
-  return Result;
+  Node *ElemType = parseType();
+  if (!ElemType)
+    return nullptr;
+  return make<VectorType>(ElemType, StringView());
 }
 
-Node *Db::parsePrefixExpr(StringView Kind) {
+// <decltype>  ::= Dt <expression> E  # decltype of an id-expression or class member access (C++0x)
+//             ::= DT <expression> E  # decltype of an expression (C++0x)
+Node *Db::parseDecltype() {
+  if (!consumeIf('D'))
+    return nullptr;
+  if (!consumeIf('t') && !consumeIf('T'))
+    return nullptr;
   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)
+  if (!consumeIf('E'))
     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;
+  return make<EnclosingExpr>("decltype(", E, ")");
 }
 
-// <CV-Qualifiers> ::= [r] [V] [K]
-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;
-}
+// <array-type> ::= A <positive dimension number> _ <element type>
+//              ::= A [<dimension expression>] _ <element type>
+Node *Db::parseArrayType() {
+  if (!consumeIf('A'))
+    return nullptr;
 
-// <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 (std::isdigit(look())) {
+    StringView Dimension = parseNumber();
     if (!consumeIf('_'))
       return nullptr;
-    return make<FunctionParam>(Num);
-  }
-  if (consumeIf("fL")) {
-    if (parseNumber().empty())
+    Node *Ty = parseType();
+    if (Ty == nullptr)
       return nullptr;
-    if (!consumeIf('p'))
+    return make<ArrayType>(Ty, Dimension);
+  }
+
+  if (!consumeIf('_')) {
+    Node *DimExpr = parseExpr();
+    if (DimExpr == nullptr)
       return nullptr;
-    parseCVQualifiers();
-    StringView Num = parseNumber();
     if (!consumeIf('_'))
       return nullptr;
-    return make<FunctionParam>(Num);
-  }
-  return nullptr;
-}
-
-// [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)
+    Node *ElementType = parseType();
+    if (ElementType == nullptr)
       return nullptr;
-    Names.push_back(Ex);
+    return make<ArrayType>(ElementType, DimExpr);
   }
-  NodeArray ExprList = popTrailingNodeArray(Exprs);
+
   Node *Ty = parseType();
   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);
+  return make<ArrayType>(Ty);
 }
 
-// cv <type> <expression>                               # conversion with one argument
-// cv <type> _ <expression>* E                          # conversion with a different number of arguments
-Node *Db::parseConversionExpr() {
-  if (!consumeIf("cv"))
+// <pointer-to-member-type> ::= M <class type> <member type>
+Node *Db::parsePointerToMemberType() {
+  if (!consumeIf('M'))
     return nullptr;
-  Node *Ty;
-  {
-    SwapAndRestore<bool> SaveTemp(TryToParseTemplateArgs, false);
-    Ty = parseType();
-  }
+  Node *ClassType = parseType();
+  if (ClassType == nullptr)
+    return nullptr;
+  Node *MemberType = parseType();
+  if (MemberType == nullptr)
+    return nullptr;
+  return make<PointerToMemberType>(ClassType, MemberType);
+}
 
-  if (Ty == nullptr)
+// <class-enum-type> ::= <name>     # non-dependent type name, dependent type name, or dependent typename-specifier
+//                   ::= Ts <name>  # dependent elaborated type specifier using 'struct' or 'class'
+//                   ::= Tu <name>  # dependent elaborated type specifier using 'union'
+//                   ::= Te <name>  # dependent elaborated type specifier using 'enum'
+Node *Db::parseClassEnumType() {
+  StringView ElabSpef;
+  if (consumeIf("Ts"))
+    ElabSpef = "struct";
+  else if (consumeIf("Tu"))
+    ElabSpef = "union";
+  else if (consumeIf("Te"))
+    ElabSpef = "enum";
+
+  Node *Name = parseName();
+  if (Name == 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);
+  if (!ElabSpef.empty())
+    return make<ElaboratedTypeSpefType>(ElabSpef, Name);
+
+  return Name;
+}
+
+// <qualified-type>     ::= <qualifiers> <type>
+// <qualifiers> ::= <extended-qualifier>* <CV-qualifiers>
+// <extended-qualifier> ::= U <source-name> [<template-args>] # vendor extended type qualifier
+Node *Db::parseQualifiedType() {
+  if (consumeIf('U')) {
+    StringView Qual = parseBareSourceName();
+    if (Qual.empty())
+      return nullptr;
+
+    // FIXME parse the optional <template-args> here!
+
+    // extension            ::= U <objc-name> <objc-type>  # objc-type<identifier>
+    if (Qual.startsWith("objcproto")) {
+      StringView ProtoSourceName = Qual.dropFront(std::strlen("objcproto"));
+      StringView Proto;
+      {
+        SwapAndRestore<const char *> SaveFirst(First, ProtoSourceName.begin()),
+                                     SaveLast(Last, ProtoSourceName.end());
+        Proto = parseBareSourceName();
+      }
+      if (Proto.empty())
+        return nullptr;
+      Node *Child = parseQualifiedType();
+      if (Child == nullptr)
+        return nullptr;
+      return make<ObjCProtoName>(Child, Proto);
     }
-    NodeArray Exprs = popTrailingNodeArray(ExprsBegin);
-    return make<ConversionExpr>(Ty, Exprs);
+
+    Node *Child = parseQualifiedType();
+    if (Child == nullptr)
+      return nullptr;
+    return make<VendorExtQualType>(Child, Qual);
   }
 
-  Node *E[1] = {parseExpr()};
-  if (E[0] == nullptr)
+  Qualifiers Quals = parseCVQualifiers();
+  Node *Ty = parseType();
+  if (Ty == nullptr)
     return nullptr;
-  return make<ConversionExpr>(Ty, makeNodeArray(E, E + 1));
+  if (Quals != QualNone)
+    Ty = make<QualType>(Ty, Quals);
+  return Ty;
 }
 
-// <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;
+// <type>      ::= <builtin-type>
+//             ::= <qualified-type>
+//             ::= <function-type>
+//             ::= <class-enum-type>
+//             ::= <array-type>
+//             ::= <pointer-to-member-type>
+//             ::= <template-param>
+//             ::= <template-template-param> <template-args>
+//             ::= <decltype>
+//             ::= P <type>        # pointer
+//             ::= R <type>        # l-value reference
+//             ::= O <type>        # r-value reference (C++11)
+//             ::= C <type>        # complex pair (C99)
+//             ::= G <type>        # imaginary (C99)
+//             ::= <substitution>  # See Compression below
+// 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>
+Node *Db::parseType() {
+  Node *Result = nullptr;
+
   switch (look()) {
+  //             ::= <qualified-type>
+  case 'r':
+  case 'V':
+  case 'K': {
+    unsigned AfterQuals = 0;
+    if (look(AfterQuals) == 'r') ++AfterQuals;
+    if (look(AfterQuals) == 'V') ++AfterQuals;
+    if (look(AfterQuals) == 'K') ++AfterQuals;
+
+    if (look(AfterQuals) == 'F' ||
+        (look(AfterQuals) == 'D' &&
+         (look(AfterQuals + 1) == 'o' || look(AfterQuals + 1) == 'O' ||
+          look(AfterQuals + 1) == 'w' || look(AfterQuals + 1) == 'x'))) {
+      Result = parseFunctionType();
+      break;
+    }
+    _LIBCPP_FALLTHROUGH();
+  }
+  case 'U': {
+    Result = parseQualifiedType();
+    break;
+  }
+  // <builtin-type> ::= v    # void
+  case 'v':
+    ++First;
+    return make<NameType>("void");
+  //                ::= w    # wchar_t
   case 'w':
     ++First;
-    return parseIntegerLiteral("wchar_t");
+    return make<NameType>("wchar_t");
+  //                ::= b    # bool
   case 'b':
-    if (consumeIf("b0E"))
-      return make<BoolExpr>(0);
-    if (consumeIf("b1E"))
-      return make<BoolExpr>(1);
-    return nullptr;
+    ++First;
+    return make<NameType>("bool");
+  //                ::= c    # char
   case 'c':
     ++First;
-    return parseIntegerLiteral("char");
+    return make<NameType>("char");
+  //                ::= a    # signed char
   case 'a':
     ++First;
-    return parseIntegerLiteral("signed char");
+    return make<NameType>("signed char");
+  //                ::= h    # unsigned char
   case 'h':
     ++First;
-    return parseIntegerLiteral("unsigned char");
+    return make<NameType>("unsigned char");
+  //                ::= s    # short
   case 's':
     ++First;
-    return parseIntegerLiteral("short");
+    return make<NameType>("short");
+  //                ::= t    # unsigned short
   case 't':
     ++First;
-    return parseIntegerLiteral("unsigned short");
+    return make<NameType>("unsigned short");
+  //                ::= i    # int
   case 'i':
     ++First;
-    return parseIntegerLiteral("");
+    return make<NameType>("int");
+  //                ::= j    # unsigned int
   case 'j':
     ++First;
-    return parseIntegerLiteral("u");
+    return make<NameType>("unsigned int");
+  //                ::= l    # long
   case 'l':
     ++First;
-    return parseIntegerLiteral("l");
+    return make<NameType>("long");
+  //                ::= m    # unsigned long
   case 'm':
     ++First;
-    return parseIntegerLiteral("ul");
+    return make<NameType>("unsigned long");
+  //                ::= x    # long long, __int64
   case 'x':
     ++First;
-    return parseIntegerLiteral("ll");
+    return make<NameType>("long long");
+  //                ::= y    # unsigned long long, __int64
   case 'y':
     ++First;
-    return parseIntegerLiteral("ull");
+    return make<NameType>("unsigned long long");
+  //                ::= n    # __int128
   case 'n':
     ++First;
-    return parseIntegerLiteral("__int128");
+    return make<NameType>("__int128");
+  //                ::= o    # unsigned __int128
   case 'o':
     ++First;
-    return parseIntegerLiteral("unsigned __int128");
+    return make<NameType>("unsigned __int128");
+  //                ::= f    # float
   case 'f':
     ++First;
-    return parseFloatingLiteral<float>();
+    return make<NameType>("float");
+  //                ::= d    # double
   case 'd':
     ++First;
-    return parseFloatingLiteral<double>();
+    return make<NameType>("double");
+  //                ::= e    # long double, __float80
   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 = parseType();
-    if (T == nullptr)
+    return make<NameType>("long double");
+  //                ::= g    # __float128
+  case 'g':
+    ++First;
+    return make<NameType>("__float128");
+  //                ::= z    # ellipsis
+  case 'z':
+    ++First;
+    return make<NameType>("...");
+
+  // <builtin-type> ::= u <source-name>    # vendor extended type
+  case 'u': {
+    ++First;
+    StringView Res = parseBareSourceName();
+    if (Res.empty())
       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;
-  }
+    return make<NameType>(Res);
   }
-}
-
-// <braced-expression> ::= <expression>
-//                     ::= di <field source-name> <braced-expression>    # .name = expr
-//                     ::= dx <index expression> <braced-expression>     # [expr] = expr
-//                     ::= dX <range begin expression> <range end expression> <braced-expression>
-Node *Db::parseBracedExpr() {
-  if (look() == 'd') {
+  case 'D':
     switch (look(1)) {
-    case 'i': {
-      First += 2;
-      Node *Field = legacyParse<parse_source_name>();
-      if (Field == nullptr)
-        return nullptr;
-      Node *Init = parseBracedExpr();
-      if (Init == nullptr)
-        return nullptr;
-      return make<BracedExpr>(Field, Init, /*isArray=*/false);
-    }
-    case 'x': {
-      First += 2;
-      Node *Index = parseExpr();
-      if (Index == nullptr)
-        return nullptr;
-      Node *Init = parseBracedExpr();
-      if (Init == nullptr)
-        return nullptr;
-      return make<BracedExpr>(Index, Init, /*isArray=*/true);
-    }
-    case 'X': {
-      First += 2;
-      Node *RangeBegin = parseExpr();
-      if (RangeBegin == nullptr)
-        return nullptr;
-      Node *RangeEnd = parseExpr();
-      if (RangeEnd == nullptr)
-        return nullptr;
-      Node *Init = parseBracedExpr();
-      if (Init == nullptr)
-        return nullptr;
-      return make<BracedRangeExpr>(RangeBegin, RangeEnd, Init);
-    }
-    }
-  }
-  return parseExpr();
-}
-
-// <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("&&");
+    //                ::= Dd   # IEEE 754r decimal floating point (64 bits)
     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 = parseType();
-      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 = parseType();
-      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': {
+      return make<NameType>("decimal64");
+    //                ::= De   # IEEE 754r decimal floating point (128 bits)
+    case 'e':
       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));
-    }
-    case 'm':
+      return make<NameType>("decimal128");
+    //                ::= Df   # IEEE 754r decimal floating point (32 bits)
+    case 'f':
       First += 2;
-      return parseBinaryExpr(",");
-    case 'o':
+      return make<NameType>("decimal32");
+    //                ::= Dh   # IEEE 754r half-precision floating point (16 bits)
+    case 'h':
       First += 2;
-      return parsePrefixExpr("~");
-    case 'v':
-      return parseConversionExpr();
-    }
-    return nullptr;
-  case 'd':
-    switch (First[1]) {
-    case 'a': {
+      return make<NameType>("decimal16");
+    //                ::= Di   # char32_t
+    case 'i':
       First += 2;
-      Node *Ex = parseExpr();
-      if (Ex == nullptr)
-        return Ex;
-      return make<DeleteExpr>(Ex, Global, /*is_array=*/true);
-    }
-    case 'c': {
+      return make<NameType>("char32_t");
+    //                ::= Ds   # char16_t
+    case 's':
       First += 2;
-      Node *T = parseType();
-      if (T == nullptr)
-        return T;
-      Node *Ex = parseExpr();
-      if (Ex == nullptr)
-        return Ex;
-      return make<CastExpr>("dynamic_cast", T, Ex);
-    }
-    case 'e':
+      return make<NameType>("char16_t");
+    //                ::= Da   # auto (in dependent new-expressions)
+    case 'a':
       First += 2;
-      return parsePrefixExpr("*");
-    case 'l': {
+      return make<NameType>("auto");
+    //                ::= Dc   # decltype(auto)
+    case 'c':
       First += 2;
-      Node *E = parseExpr();
-      if (E == nullptr)
-        return E;
-      return make<DeleteExpr>(E, Global, /*is_array=*/false);
-    }
+      return make<NameType>("decltype(auto)");
+    //                ::= Dn   # std::nullptr_t (i.e., decltype(nullptr))
     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);
+      return make<NameType>("std::nullptr_t");
+
+    //             ::= <decltype>
+    case 't':
+    case 'T': {
+      Result = parseDecltype();
+      break;
     }
-    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);
+    // extension   ::= <vector-type> # <vector-type> starts with Dv
+    case 'v': {
+      Result = parseVectorType();
+      break;
     }
-    case 'v':
-      First += 2;
-      return parseBinaryExpr("/");
-    case 'V':
+    //           ::= Dp <type>       # pack expansion (C++0x)
+    case 'p': {
       First += 2;
-      return parseBinaryExpr("/=");
+      Node *Child = parseType();
+      if (!Child)
+        return nullptr;
+      Result = make<ParameterPackExpansion>(Child);
+      break;
     }
-    return nullptr;
-  case 'e':
-    switch (First[1]) {
+    // Exception specifier on a function type.
     case 'o':
-      First += 2;
-      return parseBinaryExpr("^");
     case 'O':
-      First += 2;
-      return parseBinaryExpr("^=");
-    case 'q':
-      First += 2;
-      return parseBinaryExpr("==");
+    case 'w':
+    // Transaction safe function type.
+    case 'x':
+      Result = parseFunctionType();
+      break;
     }
-    return nullptr;
-  case 'g':
-    switch (First[1]) {
-    case 'e':
-      First += 2;
-      return parseBinaryExpr(">=");
-    case 't':
-      First += 2;
-      return parseBinaryExpr(">");
+    break;
+  //             ::= <function-type>
+  case 'F': {
+    Result = parseFunctionType();
+    break;
+  }
+  //             ::= <array-type>
+  case 'A': {
+    Result = parseArrayType();
+    break;
+  }
+  //             ::= <pointer-to-member-type>
+  case 'M': {
+    Result = parsePointerToMemberType();
+    break;
+  }
+  //             ::= <template-param>
+  case 'T': {
+    // This could be an elaborate type specifier on a <class-enum-type>.
+    if (look(1) == 's' || look(1) == 'u' || look(1) == 'e') {
+      Result = parseClassEnumType();
+      break;
     }
-    return nullptr;
-  case 'i':
-    switch (First[1]) {
-    case 'x': {
-      First += 2;
-      Node *Base = parseExpr();
-      if (Base == nullptr)
+
+    Result = legacyParse<parse_template_param>();
+    if (Result == nullptr)
+      return nullptr;
+
+    // Result could be either of:
+    //   <type>        ::= <template-param>
+    //   <type>        ::= <template-template-param> <template-args>
+    //
+    //   <template-template-param> ::= <template-param>
+    //                             ::= <substitution>
+    //
+    // If this is followed by some <template-args>, and we're permitted to
+    // parse them, take the second production.
+
+    if (TryToParseTemplateArgs && look() == 'I') {
+      Node *TA = legacyParse<parse_template_args>();
+      if (TA == nullptr)
         return nullptr;
-      Node *Index = parseExpr();
-      if (Index == nullptr)
-        return Index;
-      return make<ArraySubscriptExpr>(Base, Index);
+      Result = make<NameWithTemplateArgs>(Result, TA);
     }
-    case 'l': {
-      First += 2;
-      size_t InitsBegin = Names.size();
-      while (!consumeIf('E')) {
-        Node *E = parseBracedExpr();
-        if (E == nullptr)
+    break;
+  }
+  //             ::= P <type>        # pointer
+  case 'P': {
+    ++First;
+    Node *Ptr = parseType();
+    if (Ptr == nullptr)
+      return nullptr;
+    Result = make<PointerType>(Ptr);
+    break;
+  }
+  //             ::= R <type>        # l-value reference
+  case 'R': {
+    ++First;
+    Node *Ref = parseType();
+    if (Ref == nullptr)
+      return nullptr;
+    Result = make<LValueReferenceType>(Ref);
+    break;
+  }
+  //             ::= O <type>        # r-value reference (C++11)
+  case 'O': {
+    ++First;
+    Node *Ref = parseType();
+    if (Ref == nullptr)
+      return nullptr;
+    Result = make<RValueReferenceType>(Ref);
+    break;
+  }
+  //             ::= C <type>        # complex pair (C99)
+  case 'C': {
+    ++First;
+    Node *P = parseType();
+    if (P == nullptr)
+      return nullptr;
+    Result = make<PostfixQualifiedType>(P, " complex");
+    break;
+  }
+  //             ::= G <type>        # imaginary (C99)
+  case 'G': {
+    ++First;
+    Node *P = parseType();
+    if (P == nullptr)
+      return P;
+    Result = make<PostfixQualifiedType>(P, " imaginary");
+    break;
+  }
+  //             ::= <substitution>  # See Compression below
+  case 'S': {
+    if (look(1) && look(1) != 't') {
+      Node *Sub = legacyParse<parse_substitution>();
+      if (Sub == nullptr)
+        return nullptr;
+
+      // Sub could be either of:
+      //   <type>        ::= <substitution>
+      //   <type>        ::= <template-template-param> <template-args>
+      //
+      //   <template-template-param> ::= <template-param>
+      //                             ::= <substitution>
+      //
+      // If this is followed by some <template-args>, and we're permitted to
+      // parse them, take the second production.
+
+      if (TryToParseTemplateArgs && look() == 'I') {
+        Node *TA = legacyParse<parse_template_args>();
+        if (TA == nullptr)
           return nullptr;
-        Names.push_back(E);
+        Result = make<NameWithTemplateArgs>(Sub, TA);
+        break;
       }
-      return make<InitListExpr>(nullptr, popTrailingNodeArray(InitsBegin));
-    }
-    }
-    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, "--");
+
+      // If all we parsed was a substitution, don't re-insert into the
+      // substitution table.
+      return Sub;
     }
+    _LIBCPP_FALLTHROUGH();
+  }
+  //        ::= <class-enum-type>
+  default: {
+    Result = parseClassEnumType();
+    break;
+  }
+  }
+
+  // If we parsed a type, insert it into the substitution table. Note that all
+  // <builtin-type>s and <substitution>s have already bailed out, because they
+  // don't get substitutions.
+  if (Result != nullptr)
+    Subs.push_back(Result);
+  return Result;
+}
+
+Node *Db::parsePrefixExpr(StringView Kind) {
+  Node *E = parseExpr();
+  if (E == nullptr)
     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 make<PrefixExpr>(Kind, E);
+}
+
+Node *Db::parseBinaryExpr(StringView Kind) {
+  Node *LHS = parseExpr();
+  if (LHS == nullptr)
     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("|=");
-    }
+  Node *RHS = parseExpr();
+  if (RHS == nullptr)
     return nullptr;
-  case 'p':
-    switch (First[1]) {
+  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;
+}
+
+// <CV-Qualifiers> ::= [r] [V] [K]
+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;
+}
+
+// <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;
+}
+
+// [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 = parseType();
+  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);
+}
+
+// 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 = parseType();
+  }
+
+  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));
+}
+
+// <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 = parseEncoding();
+      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 = parseType();
+    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;
+  }
+  }
+}
+
+// <braced-expression> ::= <expression>
+//                     ::= di <field source-name> <braced-expression>    # .name = expr
+//                     ::= dx <index expression> <braced-expression>     # [expr] = expr
+//                     ::= dX <range begin expression> <range end expression> <braced-expression>
+Node *Db::parseBracedExpr() {
+  if (look() == 'd') {
+    switch (look(1)) {
+    case 'i': {
+      First += 2;
+      Node *Field = parseSourceName(/*NameState=*/nullptr);
+      if (Field == nullptr)
+        return nullptr;
+      Node *Init = parseBracedExpr();
+      if (Init == nullptr)
+        return nullptr;
+      return make<BracedExpr>(Field, Init, /*isArray=*/false);
+    }
+    case 'x': {
+      First += 2;
+      Node *Index = parseExpr();
+      if (Index == nullptr)
+        return nullptr;
+      Node *Init = parseBracedExpr();
+      if (Init == nullptr)
+        return nullptr;
+      return make<BracedExpr>(Index, Init, /*isArray=*/true);
+    }
+    case 'X': {
+      First += 2;
+      Node *RangeBegin = parseExpr();
+      if (RangeBegin == nullptr)
+        return nullptr;
+      Node *RangeEnd = parseExpr();
+      if (RangeEnd == nullptr)
+        return nullptr;
+      Node *Init = parseBracedExpr();
+      if (Init == nullptr)
+        return nullptr;
+      return make<BracedRangeExpr>(RangeBegin, RangeEnd, Init);
+    }
+    }
+  }
+  return parseExpr();
+}
+
+// <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 = parseType();
+      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 = parseType();
+      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));
+    }
+    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 = parseType();
+      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':
+    switch (First[1]) {
+    case 'x': {
+      First += 2;
+      Node *Base = parseExpr();
+      if (Base == nullptr)
+        return nullptr;
+      Node *Index = parseExpr();
+      if (Index == nullptr)
+        return Index;
+      return make<ArraySubscriptExpr>(Base, Index);
+    }
+    case 'l': {
+      First += 2;
+      size_t InitsBegin = Names.size();
+      while (!consumeIf('E')) {
+        Node *E = parseBracedExpr();
+        if (E == nullptr)
+          return nullptr;
+        Names.push_back(E);
+      }
+      return make<InitListExpr>(nullptr, popTrailingNodeArray(InitsBegin));
+    }
+    }
+    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("->*");
@@ -3720,1877 +4162,924 @@ Node *Db::parseExpr() {
       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>
-
-const char*
-parse_number(const char* first, const char* last)
-{
-    if (first != last)
-    {
-        const char* t = first;
-        if (*t == 'n')
-            ++t;
-        if (t != last)
-        {
-            if (*t == '0')
-            {
-                first = t+1;
-            }
-            else if ('1' <= *t && *t <= '9')
-            {
-                first = t+1;
-                while (first != last && std::isdigit(*first))
-                    ++first;
-            }
-        }
-    }
-    return first;
-}
-
-template <class Float>
-struct FloatData;
-
-template <>
-struct FloatData<float>
-{
-    static const size_t mangled_size = 8;
-    static const size_t max_demangled_size = 24;
-    static constexpr const char* spec = "%af";
-};
-
-constexpr const char* FloatData<float>::spec;
-
-template <>
-struct FloatData<double>
-{
-    static const size_t mangled_size = 16;
-    static const size_t max_demangled_size = 32;
-    static constexpr const char* spec = "%a";
-};
-
-constexpr const char* FloatData<double>::spec;
-
-template <>
-struct FloatData<long double>
-{
-#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \
-    defined(__wasm__)
-    static const size_t mangled_size = 32;
-#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__)
-    static const size_t mangled_size = 16;
-#else
-    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";
-};
-
-constexpr const char *FloatData<long double>::spec;
-
-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]*
-const char*
-parse_positive_integer(const char* first, const char* last, size_t* out)
-{
-    if (first != last)
-    {
-        char c = *first;
-        if (isdigit(c) && first+1 != last)
-        {
-            const char* t = first+1;
-            size_t n = static_cast<size_t>(c - '0');
-            for (c = *t; isdigit(c); c = *t)
-            {
-                n = n * 10 + static_cast<size_t>(c - '0');
-                if (++t == last)
-                    return first;
-            }
-            *out = n;
-            first = t;
-        }
-    }
-    return first;
-}
-
-// extension
-// <abi-tag-seq> ::= <abi-tag>*
-// <abi-tag>     ::= B <positive length number> <identifier>
-const char*
-parse_abi_tag_seq(const char* first, const char* last, Db& db)
-{
-    while (first != last && *first == 'B' && first+1 != last)
-    {
-        size_t length;
-        const char* t = parse_positive_integer(first+1, last, &length);
-        if (t == first+1)
-            return first;
-        if (static_cast<size_t>(last - t) < length || db.Names.empty())
-            return first;
-        db.Names.back() = db.make<AbiTagAttr>(
-            db.Names.back(), StringView(t, t + length));
-        first = t + length;
-    }
-    return first;
-}
-
-// <source-name> ::= <positive length number> <identifier> [<abi-tag-seq>]
-const char*
-parse_source_name(const char* first, const char* last, Db& db)
-{
-    if (first != last)
-    {
-        size_t length;
-        const char* t = parse_positive_integer(first, last, &length);
-        if (t == first)
-            return first;
-        if (static_cast<size_t>(last - t) >= length)
-        {
-            StringView r(t, t + length);
-            if (r.substr(0, 10) == "_GLOBAL__N")
-                db.Names.push_back(db.make<NameType>("(anonymous namespace)"));
-            else
-                db.Names.push_back(db.make<NameType>(r));
-            first = t + length;
-            first = parse_abi_tag_seq(first, last, db);
-        }
-    }
-    return first;
-}
-
-// <substitution> ::= S <seq-id> _
-//                ::= S_
-// <substitution> ::= Sa # ::std::allocator
-// <substitution> ::= Sb # ::std::basic_string
-// <substitution> ::= Ss # ::std::basic_string < char,
-//                                               ::std::char_traits<char>,
-//                                               ::std::allocator<char> >
-// <substitution> ::= Si # ::std::basic_istream<char,  std::char_traits<char> >
-// <substitution> ::= So # ::std::basic_ostream<char,  std::char_traits<char> >
-// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
-
-const char*
-parse_substitution(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 2)
-    {
-        if (*first == 'S')
-        {
-            switch (first[1])
-            {
-            case 'a':
-                db.Names.push_back(
-                    db.make<SpecialSubstitution>(
-                        SpecialSubKind::allocator));
-                first += 2;
-                break;
-            case 'b':
-                db.Names.push_back(
-                    db.make<SpecialSubstitution>(SpecialSubKind::basic_string));
-                first += 2;
-                break;
-            case 's':
-                db.Names.push_back(
-                    db.make<SpecialSubstitution>(
-                        SpecialSubKind::string));
-                first += 2;
-                break;
-            case 'i':
-                db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::istream));
-                first += 2;
-                break;
-            case 'o':
-                db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::ostream));
-                first += 2;
-                break;
-            case 'd':
-                db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::iostream));
-                first += 2;
-                break;
-            case '_':
-                if (!db.Subs.empty())
-                {
-                    db.Names.push_back(db.Subs[0]);
-                    first += 2;
-                }
-                break;
-            default:
-                if (std::isdigit(first[1]) || std::isupper(first[1]))
-                {
-                    size_t sub = 0;
-                    const char* t = first+1;
-                    if (std::isdigit(*t))
-                        sub = static_cast<size_t>(*t - '0');
-                    else
-                        sub = static_cast<size_t>(*t - 'A') + 10;
-                    for (++t; t != last && (std::isdigit(*t) || std::isupper(*t)); ++t)
-                    {
-                        sub *= 36;
-                        if (std::isdigit(*t))
-                            sub += static_cast<size_t>(*t - '0');
-                        else
-                            sub += static_cast<size_t>(*t - 'A') + 10;
-                    }
-                    if (t == last || *t != '_')
-                        return first;
-                    ++sub;
-                    if (sub < db.Subs.size())
-                    {
-                        db.Names.push_back(db.Subs[sub]);
-                        first = t+1;
-                    }
-                }
-                break;
-            }
-        }
+        return nullptr;
+      return make<ThrowExpr>(Ex);
     }
-    return first;
-}
-
-// <template-param> ::= T_    # first template parameter
-//                  ::= T <parameter-2 non-negative number> _
-
-const char*
-parse_template_param(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 2)
-    {
-        if (*first == 'T')
-        {
-            if (first[1] == '_')
-            {
-                if (!db.TemplateParams.empty())
-                {
-                    db.Names.push_back(db.TemplateParams[0]);
-                    first += 2;
-                }
-                else
-                {
-                    db.Names.push_back(db.make<NameType>("T_"));
-                    first += 2;
-                    db.FixForwardReferences = true;
-                }
-            }
-            else if (isdigit(first[1]))
-            {
-                const char* t = first+1;
-                size_t sub = static_cast<size_t>(*t - '0');
-                for (++t; t != last && isdigit(*t); ++t)
-                {
-                    sub *= 10;
-                    sub += static_cast<size_t>(*t - '0');
-                }
-                if (t == last || *t != '_')
-                    return first;
-                ++sub;
-                if (sub < db.TemplateParams.size())
-                {
-                    db.Names.push_back(db.TemplateParams[sub]);
-                    first = t+1;
-                }
-                else
-                {
-                    db.Names.push_back(
-                        db.make<NameType>(StringView(first, t + 1)));
-                    first = t+1;
-                    db.FixForwardReferences = true;
-                }
-            }
-        }
     }
-    return first;
+    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;
 }
 
-// <simple-id> ::= <source-name> [ <template-args> ]
-
-const char*
-parse_simple_id(const char* first, const char* last, Db& db)
-{
-    if (first != last)
-    {
-        const char* t = parse_source_name(first, last, db);
-        if (t != first)
-        {
-            const char* 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);
-            }
-            first = t1;
-        }
-        else
-            first = t;
-    }
-    return first;
+// <call-offset> ::= h <nv-offset> _
+//               ::= v <v-offset> _
+//
+// <nv-offset> ::= <offset number>
+//               # non-virtual base override
+//
+// <v-offset>  ::= <offset number> _ <virtual offset number>
+//               # virtual base override, with vcall offset
+bool Db::parseCallOffset() {
+  // Just scan through the call offset, we never add this information into the
+  // output.
+  if (consumeIf('h'))
+    return parseNumber(true).empty() || !consumeIf('_');
+  if (consumeIf('v'))
+    return parseNumber(true).empty() || !consumeIf('_') ||
+           parseNumber(true).empty() || !consumeIf('_');
+  return true;
 }
 
-// <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)
-        {
-        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.empty())
-                    return first;
-                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;
-       }
+// <special-name> ::= TV <type>    # virtual table
+//                ::= TT <type>    # VTT structure (construction vtable index)
+//                ::= TI <type>    # typeinfo structure
+//                ::= TS <type>    # typeinfo name (null-terminated byte string)
+//                ::= Tc <call-offset> <call-offset> <base encoding>
+//                    # base is the nominal target function of thunk
+//                    # first call-offset is 'this' adjustment
+//                    # second call-offset is result adjustment
+//                ::= T <call-offset> <base encoding>
+//                    # base is the nominal target function of thunk
+//                ::= GV <object name> # Guard variable for one-time initialization
+//                                     # No <type>
+//                ::= TW <object name> # Thread-local wrapper
+//                ::= TH <object name> # Thread-local initialization
+//      extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
+//      extension ::= GR <object name> # reference temporary for object
+Node *Db::parseSpecialName() {
+  switch (look()) {
+  case 'T':
+    switch (look(1)) {
+    // TV <type>    # virtual table
+    case 'V': {
+      First += 2;
+      Node *Ty = parseType();
+      if (Ty == nullptr)
+        return nullptr;
+      return make<SpecialName>("vtable for ", Ty);
     }
-    return first;
-}
-
-// <destructor-name> ::= <unresolved-type>                               # e.g., ~T or ~decltype(f())
-//                   ::= <simple-id>                                     # e.g., ~A<2*N>
-
-const char*
-parse_destructor_name(const char* first, const char* last, Db& db)
-{
-    if (first != last)
-    {
-        const char* t = parse_unresolved_type(first, last, db);
-        if (t == first)
-            t = parse_simple_id(first, last, db);
-        if (t != first)
-        {
-            if (db.Names.empty())
-                return first;
-            db.Names.back() = db.make<DtorName>(db.Names.back());
-            first = t;
-        }
+    // TT <type>    # VTT structure (construction vtable index)
+    case 'T': {
+      First += 2;
+      Node *Ty = parseType();
+      if (Ty == nullptr)
+        return nullptr;
+      return make<SpecialName>("VTT for ", Ty);
     }
-    return first;
+    // TI <type>    # typeinfo structure
+    case 'I': {
+      First += 2;
+      Node *Ty = parseType();
+      if (Ty == nullptr)
+        return nullptr;
+      return make<SpecialName>("typeinfo for ", Ty);
+    }
+    // TS <type>    # typeinfo name (null-terminated byte string)
+    case 'S': {
+      First += 2;
+      Node *Ty = parseType();
+      if (Ty == nullptr)
+        return nullptr;
+      return make<SpecialName>("typeinfo name for ", Ty);
+    }
+    // Tc <call-offset> <call-offset> <base encoding>
+    case 'c': {
+      First += 2;
+      if (parseCallOffset() || parseCallOffset())
+        return nullptr;
+      Node *Encoding = parseEncoding();
+      if (Encoding == nullptr)
+        return nullptr;
+      return make<SpecialName>("covariant return thunk to ", Encoding);
+    }
+    // extension ::= TC <first type> <number> _ <second type>
+    //               # construction vtable for second-in-first
+    case 'C': {
+      First += 2;
+      Node *FirstType = parseType();
+      if (FirstType == nullptr)
+        return nullptr;
+      if (parseNumber(true).empty() || !consumeIf('_'))
+        return nullptr;
+      Node *SecondType = parseType();
+      if (SecondType == nullptr)
+        return nullptr;
+      return make<CtorVtableSpecialName>(SecondType, FirstType);
+    }
+    // TW <object name> # Thread-local wrapper
+    case 'W': {
+      First += 2;
+      Node *Name = parseName();
+      if (Name == nullptr)
+        return nullptr;
+      return make<SpecialName>("thread-local wrapper routine for ", Name);
+    }
+    // TH <object name> # Thread-local initialization
+    case 'H': {
+      First += 2;
+      Node *Name = parseName();
+      if (Name == nullptr)
+        return nullptr;
+      return make<SpecialName>("thread-local initialization routine for ", Name);
+    }
+    // T <call-offset> <base encoding>
+    default: {
+      ++First;
+      bool IsVirt = look() == 'v';
+      if (parseCallOffset())
+        return nullptr;
+      Node *BaseEncoding = parseEncoding();
+      if (BaseEncoding == nullptr)
+        return nullptr;
+      if (IsVirt)
+        return make<SpecialName>("virtual thunk to ", BaseEncoding);
+      else
+        return make<SpecialName>("non-virtual thunk to ", BaseEncoding);
+    }
+    }
+  case 'G':
+    switch (look(1)) {
+    // GV <object name> # Guard variable for one-time initialization
+    case 'V': {
+      First += 2;
+      Node *Name = parseName();
+      if (Name == nullptr)
+        return nullptr;
+      return make<SpecialName>("guard variable for ", Name);
+    }
+    // GR <object name> # reference temporary for object
+    case 'R': {
+      First += 2;
+      Node *Name = parseName();
+      if (Name == nullptr)
+        return nullptr;
+      return make<SpecialName>("reference temporary for ", Name);
+    }
+    }
+  }
+  return nullptr;
 }
 
-// <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>
+// <encoding> ::= <function name> <bare-function-type>
+//            ::= <data name>
+//            ::= <special-name>
+Node *Db::parseEncoding() {
+  // Always "tag" templates (insert them into Db::TemplateParams) unless we're
+  // doing a second parse to resolve a forward template reference, in which case
+  // we only tag templates if EncodingDepth > 1.
+  // FIXME: This is kinda broken; it would be better to make a forward reference
+  // and patch it all in one pass.
+  SwapAndRestore<bool> SaveTagTemplates(TagTemplates,
+                                        TagTemplates || EncodingDepth);
+  SwapAndRestore<unsigned> SaveEncodingDepth(EncodingDepth, EncodingDepth + 1);
+
+  if (look() == 'G' || look() == 'T')
+    return parseSpecialName();
+
+  auto IsEndOfEncoding = [&] {
+    // The set of chars that can potentially follow an <encoding> (none of which
+    // can start a <type>). Enumerating these allows us to avoid speculative
+    // parsing.
+    return numLeft() == 0 || look() == 'E' || look() == '.' || look() == '_';
+  };
+
+  NameState NameInfo;
+  Node *Name = parseName(&NameInfo);
+  if (Name == nullptr || IsEndOfEncoding())
+    return Name;
+
+  TagTemplates = false;
+
+  Node *ReturnType = nullptr;
+  if (!NameInfo.CtorDtorConversion && NameInfo.EndsWithTemplateArgs) {
+    ReturnType = parseType();
+    if (ReturnType == nullptr)
+      return nullptr;
+  }
 
-const char*
-parse_base_unresolved_name(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 2)
-    {
-        if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n')
-        {
-            if (first[0] == 'o')
-            {
-                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;
+  if (consumeIf('v'))
+    return make<FunctionEncoding>(ReturnType, Name, NodeArray(),
+                                  NameInfo.CVQualifiers,
+                                  NameInfo.ReferenceQualifier);
+
+  size_t ParamsBegin = Names.size();
+  do {
+    Node *Ty = parseType();
+    if (Ty == nullptr)
+      return nullptr;
+    Names.push_back(Ty);
+  } while (!IsEndOfEncoding());
+
+  return make<FunctionEncoding>(ReturnType, Name,
+                                popTrailingNodeArray(ParamsBegin),
+                                NameInfo.CVQualifiers,
+                                NameInfo.ReferenceQualifier);
 }
 
-// <unresolved-qualifier-level> ::= <simple-id>
+template <class Float>
+struct FloatData;
 
-const char*
-parse_unresolved_qualifier_level(const char* first, const char* last, Db& db)
+template <>
+struct FloatData<float>
 {
-    return parse_simple_id(first, last, db);
-}
+    static const size_t mangled_size = 8;
+    static const size_t max_demangled_size = 24;
+    static constexpr const char* spec = "%af";
+};
 
-// <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>
+constexpr const char* FloatData<float>::spec;
 
-const char*
-parse_unresolved_name(const char* first, const char* last, Db& db)
+template <>
+struct FloatData<double>
 {
-    if (last - first > 2)
-    {
-        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')
-        {
-            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;
+    static const size_t mangled_size = 16;
+    static const size_t max_demangled_size = 32;
+    static constexpr const char* spec = "%a";
+};
+
+constexpr const char* FloatData<double>::spec;
+
+template <>
+struct FloatData<long double>
+{
+#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \
+    defined(__wasm__)
+    static const size_t mangled_size = 32;
+#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__)
+    static const size_t mangled_size = 16;
+#else
+    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";
+};
+
+constexpr const char *FloatData<long double>::spec;
+
+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);
 }
 
-//   <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>
+// <substitution> ::= S <seq-id> _
+//                ::= S_
+// <substitution> ::= Sa # ::std::allocator
+// <substitution> ::= Sb # ::std::basic_string
+// <substitution> ::= Ss # ::std::basic_string < char,
+//                                               ::std::char_traits<char>,
+//                                               ::std::allocator<char> >
+// <substitution> ::= Si # ::std::basic_istream<char,  std::char_traits<char> >
+// <substitution> ::= So # ::std::basic_ostream<char,  std::char_traits<char> >
+// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
+
 const char*
-parse_operator_name(const char* first, const char* last, Db& db)
+parse_substitution(const char* first, const char* last, Db& db)
 {
-    const char* original_first = first;
     if (last - first >= 2)
     {
-        switch (first[0])
+        if (*first == 'S')
         {
-        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&"));
+                db.Names.push_back(
+                    db.make<SpecialSubstitution>(
+                        SpecialSubKind::allocator));
                 first += 2;
                 break;
-            case 'N':
-                db.Names.push_back(db.make<NameType>("operator&="));
+            case 'b':
+                db.Names.push_back(
+                    db.make<SpecialSubstitution>(SpecialSubKind::basic_string));
                 first += 2;
                 break;
-            case 'S':
-                db.Names.push_back(db.make<NameType>("operator="));
+            case 's':
+                db.Names.push_back(
+                    db.make<SpecialSubstitution>(
+                        SpecialSubKind::string));
                 first += 2;
                 break;
-            }
-            break;
-        case 'c':
-            switch (first[1])
-            {
-            case 'l':
-                db.Names.push_back(db.make<NameType>("operator()"));
+            case 'i':
+                db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::istream));
                 first += 2;
                 break;
-            case 'm':
-                db.Names.push_back(db.make<NameType>("operator,"));
+            case 'o':
+                db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::ostream));
                 first += 2;
                 break;
-            case 'o':
-                db.Names.push_back(db.make<NameType>("operator~"));
+            case 'd':
+                db.Names.push_back(db.make<SpecialSubstitution>(SpecialSubKind::iostream));
                 first += 2;
                 break;
-            case 'v':
+            case '_':
+                if (!db.Subs.empty())
                 {
-                    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;
-                    }
+                    db.Names.push_back(db.Subs[0]);
+                    first += 2;
                 }
                 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':
+            default:
+                if (std::isdigit(first[1]) || std::isupper(first[1]))
                 {
-                    const char* t = parse_source_name(first+2, last, db);
-                    if (t != first+2)
+                    size_t sub = 0;
+                    const char* t = first+1;
+                    if (std::isdigit(*t))
+                        sub = static_cast<size_t>(*t - '0');
+                    else
+                        sub = static_cast<size_t>(*t - 'A') + 10;
+                    for (++t; t != last && (std::isdigit(*t) || std::isupper(*t)); ++t)
                     {
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<LiteralOperator>(db.Names.back());
-                        first = t;
+                        sub *= 36;
+                        if (std::isdigit(*t))
+                            sub += static_cast<size_t>(*t - '0');
+                        else
+                            sub += static_cast<size_t>(*t - 'A') + 10;
                     }
-                }
-                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())
+                    if (t == last || *t != '_')
                         return first;
-                    db.Names.back() =
-                        db.make<ConversionOperatorType>(db.Names.back());
-                    first = t;
+                    ++sub;
+                    if (sub < db.Subs.size())
+                    {
+                        db.Names.push_back(db.Subs[sub]);
+                        first = t+1;
+                    }
                 }
+                break;
             }
-            break;
         }
     }
-
-    if (original_first != first)
-        first = parse_abi_tag_seq(first, last, db);
-
     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
+// <template-param> ::= T_    # first template parameter
+//                  ::= T <parameter-2 non-negative number> _
+
 const char*
-parse_unnamed_type_name(const char* first, const char* last, Db& db)
+parse_template_param(const char* first, const char* last, Db& db)
 {
-    if (last - first > 2 && first[0] == 'U')
+    if (last - first >= 2)
     {
-        char type = first[1];
-        switch (type)
+        if (*first == 'T')
         {
-        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')
+            if (first[1] == '_')
             {
-                ++t0;
+                if (!db.TemplateParams.empty())
+                {
+                    db.Names.push_back(db.TemplateParams[0]);
+                    first += 2;
+                }
+                else
+                {
+                    db.Names.push_back(db.make<NameType>("T_"));
+                    first += 2;
+                    db.FixForwardReferences = true;
+                }
             }
-            else
+            else if (isdigit(first[1]))
             {
-                while (true)
+                const char* t = first+1;
+                size_t sub = static_cast<size_t>(*t - '0');
+                for (++t; t != last && isdigit(*t); ++t)
                 {
-                    const char* t1 = parse_type(t0, last, db);
-                    if (t1 == t0)
-                        break;
-                    t0 = t1;
+                    sub *= 10;
+                    sub += static_cast<size_t>(*t - '0');
                 }
-                if (db.Names.size() < begin_pos)
+                if (t == last || *t != '_')
                     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;
+                ++sub;
+                if (sub < db.TemplateParams.size())
+                {
+                    db.Names.push_back(db.TemplateParams[sub]);
+                    first = t+1;
+                }
+                else
+                {
+                    db.Names.push_back(
+                        db.make<NameType>(StringView(first, t + 1)));
+                    first = t+1;
+                    db.FixForwardReferences = true;
+                }
             }
-            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>
+// <simple-id> ::= <source-name> [ <template-args> ]
 
 const char*
-parse_unqualified_name(const char* first, const char* last, Db& db)
+parse_simple_id(const char* first, const char* last, Db& db)
 {
-    // <ctor-dtor-name>s are special-cased in parseNestedName().
-
     if (first != last)
     {
-        const char* t;
-        switch (*first)
-        {
-        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)
+        const char* t = parse_source_name(first, last, db);
+        if (t != first)
         {
-            if (St)
+            const char* t1 = parse_template_args(t, last, db);
+            if (t1 != t)
             {
-                if (db.Names.empty())
+                if (db.Names.size() < 2)
                     return first;
+                auto args = db.Names.back();
+                db.Names.pop_back();
                 db.Names.back() =
-                    db.make<StdQualifiedName>(db.Names.back());
+                    db.make<NameWithTemplateArgs>(db.Names.back(), args);
             }
             first = t1;
         }
+        else
+            first = t;
     }
     return first;
 }
 
-// <template-arg> ::= <type>                                             # type or template
-//                ::= X <expression> E                                   # expression
-//                ::= <expr-primary>                                     # simple expressions
-//                ::= J <template-arg>* E                                # argument pack
-//                ::= LZ <encoding> E                                    # extension
+// <unresolved-type> ::= <template-param>
+//                   ::= <decltype>
+//                   ::= <substitution>
+
 const char*
-parse_template_arg(const char* first, const char* last, Db& db)
+parse_unresolved_type(const char* first, const char* last, Db& db)
 {
     if (first != last)
     {
-        const char* t;
+        const char* t = first;
         switch (*first)
         {
-        case 'X':
-            t = parse_expression(first+1, last, db);
-            if (t != first+1)
+        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)
             {
-                if (t != last && *t == 'E')
-                    first = t+1;
+                db.Subs.push_back(db.Names.back());
+                first = t;
             }
-            break;
-        case 'J': {
-            t = first+1;
-            if (t == last)
-                return first;
-            size_t ArgsBegin = db.Names.size();
-            while (*t != 'E')
+            else
             {
-                const char* t1 = parse_template_arg(t, last, db);
-                if (t1 == t)
-                    return first;
-                t = t1;
+                for (; k1 != k0; --k1)
+                    db.Names.pop_back();
             }
-            NodeArray Args = db.popTrailingNodeArray(ArgsBegin);
-            db.Names.push_back(db.make<TemplateArgumentPack>(Args));
-            first = t+1;
             break;
-        }
-        case 'L':
-            // <expr-primary> or LZ <encoding> E
-            if (first+1 != last && first[1] == 'Z')
+          }
+        case 'D':
+            t = parse_decltype(first, last, db);
+            if (t != first)
             {
-                t = parse_encoding(first+2, last, db);
-                if (t != first+2 && t != last && *t == 'E')
-                    first = t+1;
+                if (db.Names.empty())
+                    return first;
+                db.Subs.push_back(db.Names.back());
+                first = t;
             }
-            else
-                first = parse_expr_primary(first, last, db);
-            break;
-        default:
-            // <type>
-            first = parse_type(first, last, db);
             break;
-        }
-    }
-    return first;
-}
-
-// <template-args> ::= I <template-arg>* E
-//     extension, the abi says <template-arg>+
-const char*
-parse_template_args(const char* first, const char* last, Db& db)
-{
-    if (last - first >= 2 && *first == 'I')
-    {
-        if (db.TagTemplates)
-            db.TemplateParams.clear();
-        const char* t = first+1;
-        size_t begin_idx = db.Names.size();
-        while (*t != 'E')
-        {
-            if (db.TagTemplates)
+        case 'S':
+            t = parse_substitution(first, last, db);
+            if (t != first)
+                first = t;
+            else
             {
-                auto TmpParams = std::move(db.TemplateParams);
-                size_t k0 = db.Names.size();
-                const char* t1 = parse_template_arg(t, last, db);
-                size_t k1 = db.Names.size();
-                db.TemplateParams = std::move(TmpParams);
-                if (t1 == t || t1 == last || k0 + 1 != k1)
-                    return first;
-                Node *TableEntry = db.Names.back();
-                if (TableEntry->getKind() == Node::KTemplateArgumentPack)
-                  TableEntry = db.make<ParameterPack>(
-                      static_cast<TemplateArgumentPack*>(TableEntry)
-                          ->getElements());
-                db.TemplateParams.push_back(TableEntry);
-                t = t1;
-                continue;
+                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;
+                    }
+                }
             }
-            size_t k0 = db.Names.size();
-            const char* t1 = parse_template_arg(t, last, db);
-            size_t k1 = db.Names.size();
-            if (t1 == t || t1 == last || k0 > k1)
-              return first;
-            t = t1;
-        }
-        if (begin_idx > db.Names.size())
-            return first;
-        first = t + 1;
-        auto *tp = db.make<TemplateArgs>(
-            db.popTrailingNodeArray(begin_idx));
-        db.Names.push_back(tp);
+            break;
+       }
     }
     return first;
 }
 
-// <discriminator> := _ <non-negative number>      # when number < 10
-//                 := __ <non-negative number> _   # when number >= 10
-//  extension      := decimal-digit+               # at the end of string
+// <destructor-name> ::= <unresolved-type>                               # e.g., ~T or ~decltype(f())
+//                   ::= <simple-id>                                     # e.g., ~A<2*N>
 
 const char*
-parse_discriminator(const char* first, const char* last)
+parse_destructor_name(const char* first, const char* last, Db& db)
 {
-    // parse but ignore discriminator
-    if (first != last)
-    {
-        if (*first == '_')
-        {
-            const char* t1 = first+1;
-            if (t1 != last)
-            {
-                if (std::isdigit(*t1))
-                    first = t1+1;
-                else if (*t1 == '_')
-                {
-                    for (++t1; t1 != last && std::isdigit(*t1); ++t1)
-                        ;
-                    if (t1 != last && *t1 == '_')
-                        first = t1 + 1;
-                }
-            }
-        }
-        else if (std::isdigit(*first))
+    if (first != last)
+    {
+        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 = first+1;
-            for (; t1 != last && std::isdigit(*t1); ++t1)
-                ;
-            if (t1 == last)
-                first = last;
+            if (db.Names.empty())
+                return first;
+            db.Names.back() = db.make<DtorName>(db.Names.back());
+            first = t;
         }
     }
     return first;
 }
 
-// <local-name> := Z <function encoding> E <entity name> [<discriminator>]
-//              := Z <function encoding> E s [<discriminator>]
-//              := Z <function encoding> Ed [ <parameter number> ] _ <entity name>
+// <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_local_name(const char* first, const char* last, Db& db,
-                 bool* ends_with_template_args)
+parse_base_unresolved_name(const char* first, const char* last, Db& db)
 {
-    if (first != last && *first == 'Z')
+    if (last - first >= 2)
     {
-        const char* t = parse_encoding(first+1, last, db);
-        if (t != first+1 && t != last && *t == 'E' && ++t != last)
+        if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n')
         {
-            switch (*t)
+            if (first[0] == 'o')
             {
-            case 's':
-                first = parse_discriminator(t+1, last);
-                if (db.Names.empty())
-                    return first;
-                db.Names.back() = db.make<QualifiedName>(
-                    db.Names.back(), db.make<NameType>("string literal"));
-                break;
-            case 'd':
-                if (++t != last)
+                const char* t = parse_operator_name(first+2, last, db);
+                if (t != first+2)
                 {
-                    const char* t1 = parse_number(t, last);
-                    if (t1 != last && *t1 == '_')
+                    first = parse_template_args(t, last, db);
+                    if (first != t)
                     {
-                        t = t1 + 1;
-                        t1 = parse_name(t, last, db,
-                                        ends_with_template_args);
-                        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<QualifiedName>(db.Names.back(), name);
-                            first = t1;
-                        }
-                        else if (!db.Names.empty())
-                            db.Names.pop_back();
+                        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);
                     }
                 }
-                break;
-            default:
+            }
+            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)
                 {
-                    const char* t1 = parse_name(t, last, db,
-                                                ends_with_template_args);
-                    if (t1 != t)
+                    first = parse_template_args(t, last, db);
+                    if (first != t)
                     {
-                        // parse but ignore discriminator
-                        first = parse_discriminator(t1, last);
                         if (db.Names.size() < 2)
                             return first;
-                        auto name = db.Names.back();
+                        auto args = db.Names.back();
                         db.Names.pop_back();
-                        if (db.Names.empty())
-                            return first;
                         db.Names.back() =
-                            db.make<QualifiedName>(db.Names.back(), name);
+                            db.make<NameWithTemplateArgs>(
+                                db.Names.back(), args);
                     }
-                    else if (!db.Names.empty())
-                        db.Names.pop_back();
                 }
-                break;
             }
+            else
+                first = t;
         }
     }
     return first;
 }
 
-// <name> ::= <nested-name> // N
-//        ::= <local-name> # See Scope Encoding below  // Z
-//        ::= <unscoped-template-name> <template-args>
-//        ::= <unscoped-name>
+// <unresolved-qualifier-level> ::= <simple-id>
 
-// <unscoped-template-name> ::= <unscoped-name>
-//                          ::= <substitution>
+const char*
+parse_unresolved_qualifier_level(const char* first, const char* last, Db& db)
+{
+    return parse_simple_id(first, last, db);
+}
+
+// <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_name(const char* first, const char* last, Db& db,
-           bool* ends_with_template_args)
+parse_unresolved_name(const char* first, const char* last, Db& db)
 {
-    if (last - first >= 2)
+    if (last - first > 2)
     {
-        const char* t0 = first;
-        // extension: ignore L here
-        if (*t0 == 'L')
-            ++t0;
-        switch (*t0)
+        const char* t = first;
+        bool global = false;
+        if (t[0] == 'g' && t[1] == 's')
         {
-        case 'N':
-          {
-            const char* t1 = parse_nested_name(t0, last, db,
-                                               ends_with_template_args);
-            if (t1 != t0)
-                first = t1;
-            break;
-          }
-        case 'Z':
-          {
-            const char* t1 = parse_local_name(t0, last, db,
-                                              ends_with_template_args);
-            if (t1 != t0)
-                first = t1;
-            break;
-          }
-        default:
-          {
-            const char* t1 = parse_unscoped_name(t0, last, db);
-            if (t1 != t0)
+            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')
+        {
+            if (t[2] == 'N')
             {
-                if (t1 != last && *t1 == 'I')  // <unscoped-template-name> <template-args>
+                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.empty())
+                    if (db.Names.size() < 2)
                         return first;
-                    db.Subs.push_back(db.Names.back());
-                    t0 = t1;
-                    t1 = parse_template_args(t0, last, db);
-                    if (t1 != t0)
+                    auto args = db.Names.back();
+                    db.Names.pop_back();
+                    db.Names.back() = db.make<NameWithTemplateArgs>(
+                        db.Names.back(), args);
+                    t = t1;
+                    if (t == last)
                     {
-                        if (db.Names.size() < 2)
-                            return first;
-                        auto tmp = db.Names.back();
                         db.Names.pop_back();
-                        if (db.Names.empty())
-                            return first;
-                        db.Names.back() =
-                            db.make<NameWithTemplateArgs>(
-                                db.Names.back(), tmp);
-                        first = t1;
-                        if (ends_with_template_args)
-                            *ends_with_template_args = true;
+                        return first;
                     }
                 }
-                else   // <unscoped-name>
-                    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
-            {   // try <substitution> <template-args>
-                t1 = parse_substitution(t0, last, db);
-                if (t1 != t0 && t1 != last && *t1 == 'I')
+            {
+                t += 2;
+                const char* t1 = parse_unresolved_type(t, last, db);
+                if (t1 != t)
                 {
-                    t0 = t1;
-                    t1 = parse_template_args(t0, last, db);
-                    if (t1 != t0)
+                    t = t1;
+                    t1 = parse_template_args(t, last, db);
+                    if (t1 != t)
                     {
                         if (db.Names.size() < 2)
                             return first;
-                        auto tmp = db.Names.back();
+                        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<NameWithTemplateArgs>(
-                                db.Names.back(), tmp);
-                        first = t1;
-                        if (ends_with_template_args)
-                            *ends_with_template_args = true;
+                            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;
                 }
             }
-            break;
-          }
         }
     }
     return first;
-}
-
-// <call-offset> ::= h <nv-offset> _
-//               ::= v <v-offset> _
-// 
-// <nv-offset> ::= <offset number>
-//               # non-virtual base override
-// 
-// <v-offset>  ::= <offset number> _ <virtual offset number>
-//               # virtual base override, with vcall offset
+}
 
+// <template-arg> ::= <type>                                             # type or template
+//                ::= X <expression> E                                   # expression
+//                ::= <expr-primary>                                     # simple expressions
+//                ::= J <template-arg>* E                                # argument pack
+//                ::= LZ <encoding> E                                    # extension
 const char*
-parse_call_offset(const char* first, const char* last)
+parse_template_arg(const char* first, const char* last, Db& db)
 {
     if (first != last)
     {
+        const char* t;
         switch (*first)
         {
-        case 'h':
+        case 'X':
+            t = parse_expression(first+1, last, db);
+            if (t != first+1)
             {
-            const char* t = parse_number(first + 1, last);
-            if (t != first + 1 && t != last && *t == '_')
-                first = t + 1;
+                if (t != last && *t == 'E')
+                    first = t+1;
             }
             break;
-        case 'v':
-            {
-            const char* t = parse_number(first + 1, last);
-            if (t != first + 1 && t != last && *t == '_')
+        case 'J': {
+            t = first+1;
+            if (t == last)
+                return first;
+            size_t ArgsBegin = db.Names.size();
+            while (*t != 'E')
             {
-                const char* t2 = parse_number(++t, last);
-                if (t2 != t && t2 != last && *t2 == '_')
-                    first = t2 + 1;
+                const char* t1 = parse_template_arg(t, last, db);
+                if (t1 == t)
+                    return first;
+                t = t1;
             }
+            NodeArray Args = db.popTrailingNodeArray(ArgsBegin);
+            db.Names.push_back(db.make<TemplateArgumentPack>(Args));
+            first = t+1;
+            break;
+        }
+        case 'L':
+            // <expr-primary> or LZ <encoding> E
+            if (first+1 != last && first[1] == 'Z')
+            {
+                t = parse_encoding(first+2, last, db);
+                if (t != first+2 && t != last && *t == 'E')
+                    first = t+1;
             }
+            else
+                first = parse_expr_primary(first, last, db);
+            break;
+        default:
+            // <type>
+            first = parse_type(first, last, db);
             break;
         }
     }
     return first;
 }
 
-// <special-name> ::= TV <type>    # virtual table
-//                ::= TT <type>    # VTT structure (construction vtable index)
-//                ::= TI <type>    # typeinfo structure
-//                ::= TS <type>    # typeinfo name (null-terminated byte string)
-//                ::= Tc <call-offset> <call-offset> <base encoding>
-//                    # base is the nominal target function of thunk
-//                    # first call-offset is 'this' adjustment
-//                    # second call-offset is result adjustment
-//                ::= T <call-offset> <base encoding>
-//                    # base is the nominal target function of thunk
-//                ::= GV <object name> # Guard variable for one-time initialization
-//                                     # No <type>
-//                ::= TW <object name> # Thread-local wrapper
-//                ::= TH <object name> # Thread-local initialization
-//      extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
-//      extension ::= GR <object name> # reference temporary for object
-
+// <template-args> ::= I <template-arg>* E
+//     extension, the abi says <template-arg>+
 const char*
-parse_special_name(const char* first, const char* last, Db& db)
+parse_template_args(const char* first, const char* last, Db& db)
 {
-    if (last - first > 2)
+    if (last - first >= 2 && *first == 'I')
     {
-        const char* t;
-        switch (*first)
+        if (db.TagTemplates)
+            db.TemplateParams.clear();
+        const char* t = first+1;
+        size_t begin_idx = db.Names.size();
+        while (*t != 'E')
         {
-        case 'T':
-            switch (first[1])
-            {
-            case 'V':
-                // TV <type>    # virtual table
-                t = parse_type(first+2, last, db);
-                if (t != first+2)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<SpecialName>("vtable for ", db.Names.back());
-                    first = t;
-                }
-                break;
-            case 'T':
-                // TT <type>    # VTT structure (construction vtable index)
-                t = parse_type(first+2, last, db);
-                if (t != first+2)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<SpecialName>("VTT for ", db.Names.back());
-                    first = t;
-                }
-                break;
-            case 'I':
-                // TI <type>    # typeinfo structure
-                t = parse_type(first+2, last, db);
-                if (t != first+2)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<SpecialName>("typeinfo for ", db.Names.back());
-                    first = t;
-                }
-                break;
-            case 'S':
-                // TS <type>    # typeinfo name (null-terminated byte string)
-                t = parse_type(first+2, last, db);
-                if (t != first+2)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<SpecialName>("typeinfo name for ", db.Names.back());
-                    first = t;
-                }
-                break;
-            case 'c':
-                // Tc <call-offset> <call-offset> <base encoding>
-              {
-                const char* t0 = parse_call_offset(first+2, last);
-                if (t0 == first+2)
-                    break;
-                const char* t1 = parse_call_offset(t0, last);
-                if (t1 == t0)
-                    break;
-                t = parse_encoding(t1, last, db);
-                if (t != t1)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<SpecialName>("covariant return thunk to ",
-                                              db.Names.back());
-                    first = t;
-                }
-              }
-                break;
-            case 'C':
-                // extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
-                t = parse_type(first+2, last, db);
-                if (t != first+2)
-                {
-                    const char* t0 = parse_number(t, last);
-                    if (t0 != t && t0 != last && *t0 == '_')
-                    {
-                        const char* t1 = parse_type(++t0, last, db);
-                        if (t1 != t0)
-                        {
-                            if (db.Names.size() < 2)
-                                return first;
-                            auto left = db.Names.back();
-                            db.Names.pop_back();
-                            if (db.Names.empty())
-                                return first;
-                            db.Names.back() = db.make<CtorVtableSpecialName>(
-                                left, db.Names.back());
-                            first = t1;
-                        }
-                    }
-                }
-                break;
-            case 'W':
-                // TW <object name> # Thread-local wrapper
-                t = parse_name(first + 2, last, db);
-                if (t != first + 2) 
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<SpecialName>("thread-local wrapper routine for ",
-                                              db.Names.back());
-                    first = t;
-                }
-                break;
-            case 'H':
-                //TH <object name> # Thread-local initialization
-                t = parse_name(first + 2, last, db);
-                if (t != first + 2) 
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() = db.make<SpecialName>(
-                        "thread-local initialization routine for ", db.Names.back());
-                    first = t;
-                }
-                break;
-            default:
-                // T <call-offset> <base encoding>
-                {
-                const char* t0 = parse_call_offset(first+1, last);
-                if (t0 == first+1)
-                    break;
-                t = parse_encoding(t0, last, db);
-                if (t != t0)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    if (first[1] == 'v')
-                    {
-                        db.Names.back() =
-                            db.make<SpecialName>("virtual thunk to ",
-                                                  db.Names.back());
-                        first = t;
-                    }
-                    else
-                    {
-                        db.Names.back() =
-                            db.make<SpecialName>("non-virtual thunk to ",
-                                                  db.Names.back());
-                        first = t;
-                    }
-                }
-                }
-                break;
-            }
-            break;
-        case 'G':
-            switch (first[1])
+            if (db.TagTemplates)
             {
-            case 'V':
-                // GV <object name> # Guard variable for one-time initialization
-                t = parse_name(first+2, last, db);
-                if (t != first+2)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<SpecialName>("guard variable for ", db.Names.back());
-                    first = t;
-                }
-                break;
-            case 'R':
-                // extension ::= GR <object name> # reference temporary for object
-                t = parse_name(first+2, last, db);
-                if (t != first+2)
-                {
-                    if (db.Names.empty())
-                        return first;
-                    db.Names.back() =
-                        db.make<SpecialName>("reference temporary for ",
-                                              db.Names.back());
-                    first = t;
-                }
-                break;
+                auto TmpParams = std::move(db.TemplateParams);
+                size_t k0 = db.Names.size();
+                const char* t1 = parse_template_arg(t, last, db);
+                size_t k1 = db.Names.size();
+                db.TemplateParams = std::move(TmpParams);
+                if (t1 == t || t1 == last || k0 + 1 != k1)
+                    return first;
+                Node *TableEntry = db.Names.back();
+                if (TableEntry->getKind() == Node::KTemplateArgumentPack)
+                  TableEntry = db.make<ParameterPack>(
+                      static_cast<TemplateArgumentPack*>(TableEntry)
+                          ->getElements());
+                db.TemplateParams.push_back(TableEntry);
+                t = t1;
+                continue;
             }
-            break;
+            size_t k0 = db.Names.size();
+            const char* t1 = parse_template_arg(t, last, db);
+            size_t k1 = db.Names.size();
+            if (t1 == t || t1 == last || k0 > k1)
+              return first;
+            t = t1;
         }
+        if (begin_idx > db.Names.size())
+            return first;
+        first = t + 1;
+        auto *tp = db.make<TemplateArgs>(
+            db.popTrailingNodeArray(begin_idx));
+        db.Names.push_back(tp);
     }
     return first;
 }
 
-template <class T>
-class save_value
-{
-    T& restore_;
-    T original_value_;
-public:
-    save_value(T& restore)
-        : restore_(restore),
-          original_value_(restore)
-        {}
-
-    ~save_value()
-    {
-        restore_ = std::move(original_value_);
-    }
-
-    save_value(const save_value&) = delete;
-    save_value& operator=(const save_value&) = delete;
-};
-
-// <encoding> ::= <function name> <bare-function-type>
-//            ::= <data name>
-//            ::= <special-name>
+// <discriminator> := _ <non-negative number>      # when number < 10
+//                 := __ <non-negative number> _   # when number >= 10
+//  extension      := decimal-digit+               # at the end of string
 
 const char*
-parse_encoding(const char* first, const char* last, Db& db)
+parse_discriminator(const char* first, const char* last)
 {
+    // parse but ignore discriminator
     if (first != last)
     {
-        save_value<decltype(db.EncodingDepth)> su(db.EncodingDepth);
-        ++db.EncodingDepth;
-        save_value<decltype(db.TagTemplates)> sb(db.TagTemplates);
-        if (db.EncodingDepth > 1)
-            db.TagTemplates = true;
-        save_value<decltype(db.ParsedCtorDtorCV)> sp(db.ParsedCtorDtorCV);
-        db.ParsedCtorDtorCV = false;
-        switch (*first)
+        if (*first == '_')
         {
-        case 'G':
-        case 'T':
-            first = parse_special_name(first, last, db);
-            break;
-        default:
-          {
-            bool ends_with_template_args = false;
-            const char* t = parse_name(first, last, db,
-                                       &ends_with_template_args);
-            if (db.Names.empty())
-                return first;
-            Qualifiers cv = db.CV;
-            FunctionRefQual ref = db.RefQuals;
-            if (t != first)
+            const char* t1 = first+1;
+            if (t1 != last)
             {
-                if (t != last && *t != 'E' && *t != '.')
+                if (std::isdigit(*t1))
+                    first = t1+1;
+                else if (*t1 == '_')
                 {
-                    save_value<bool> sb2(db.TagTemplates);
-                    db.TagTemplates = false;
-                    const char* t2;
-                    if (db.Names.empty())
-                        return first;
-                    if (!db.Names.back())
-                        return first;
-                    Node* return_type = nullptr;
-                    if (!db.ParsedCtorDtorCV && ends_with_template_args)
-                    {
-                        t2 = parse_type(t, last, db);
-                        if (t2 == t)
-                            return first;
-                        if (db.Names.size() < 1)
-                            return first;
-                        return_type = db.Names.back();
-                        db.Names.pop_back();
-                        t = t2;
-                    }
-
-                    Node* result = nullptr;
-
-                    if (t != last && *t == 'v')
-                    {
-                        ++t;
-                        if (db.Names.empty())
-                            return first;
-                        Node* name = db.Names.back();
-                        db.Names.pop_back();
-                        result = db.make<FunctionEncoding>(
-                            return_type, name, NodeArray(), cv, ref);
-                    }
-                    else
-                    {
-                        size_t params_begin = db.Names.size();
-                        while (true)
-                        {
-                            t2 = parse_type(t, last, db);
-                            if (t2 == t)
-                                break;
-                            t = t2;
-                        }
-                        if (db.Names.size() < params_begin)
-                            return first;
-                        NodeArray params =
-                            db.popTrailingNodeArray(params_begin);
-                        if (db.Names.empty())
-                            return first;
-                        Node* name = db.Names.back();
-                        db.Names.pop_back();
-                        result = db.make<FunctionEncoding>(
-                            return_type, name, params, cv, ref);
-                    }
-                    db.Names.push_back(result);
-                    first = t;
+                    for (++t1; t1 != last && std::isdigit(*t1); ++t1)
+                        ;
+                    if (t1 != last && *t1 == '_')
+                        first = t1 + 1;
                 }
-                else
-                    first = t;
             }
-            break;
-          }
+        }
+        else if (std::isdigit(*first))
+        {
+            const char* t1 = first+1;
+            for (; t1 != last && std::isdigit(*t1); ++t1)
+                ;
+            if (t1 == last)
+                first = last;
         }
     }
     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=326717&r1=326716&r2=326717&view=diff
==============================================================================
--- libcxxabi/trunk/test/test_demangle.pass.cpp (original)
+++ libcxxabi/trunk/test/test_demangle.pass.cpp Mon Mar  5 08:35:06 2018
@@ -29710,6 +29710,9 @@ const char* cases[][2] =
     {"_Z1pILb1EEiM1SKDOT_EFivRE", "int p<true>(int (S::*)() const & noexcept(true))"},
     {"_Z1pIJicfEEiM1SVKDwDpT_EFivOE", "int p<int, char, float>(int (S::*)() const volatile && throw(int, char, float))"},
     {"_Z1pM1SDoFivE", "p(int (S::*)() noexcept)"},
+
+    {"_ZNKR4llvm8OptionalINS_11MCFixupKindEEdeEv", "llvm::Optional<llvm::MCFixupKind>::operator*() const &"},
+    {"_ZZL23isValidCoroutineContextRN5clang4SemaENS_14SourceLocationEN4llvm9StringRefEENK3$_4clEZL23isValidCoroutineContextS1_S2_S4_E15InvalidFuncDiag", "isValidCoroutineContext(clang::Sema&, clang::SourceLocation, llvm::StringRef)::$_4::operator()(isValidCoroutineContext(clang::Sema&, clang::SourceLocation, llvm::StringRef)::InvalidFuncDiag) const"},
 };
 
 const unsigned N = sizeof(cases) / sizeof(cases[0]);




More information about the cfe-commits mailing list