Merge commit 565ba0415b6b

- upstream svn@328507

Test: git diff 565ba0415b6b == git diff 89cdbc2e15cc f26c8d18976
Change-Id: I631ab42bb70c97b7b90b68e2b773d584ebc603a1
diff --git a/src/cxa_demangle.cpp b/src/cxa_demangle.cpp
index d0362dc..9253e83 100644
--- a/src/cxa_demangle.cpp
+++ b/src/cxa_demangle.cpp
@@ -9,7 +9,6 @@
 
 // FIXME: (possibly) incomplete list of features that clang mangles that this
 // file does not yet support:
-//   - enable_if attribute
 //   - C++ modules TS
 //   - All C++14 and C++17 features
 
@@ -110,6 +109,7 @@
   /// If a ParameterPackExpansion (or similar type) is encountered, the offset
   /// into the pack that we're currently printing.
   unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
+  unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
 
   OutputStream &operator+=(StringView R) {
     size_t Size = R.size();
@@ -127,7 +127,8 @@
     return *this;
   }
 
-  size_t getCurrentPosition() const { return CurrentPosition; };
+  size_t getCurrentPosition() const { return CurrentPosition; }
+  void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
 
   char back() const {
     return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0';
@@ -168,6 +169,7 @@
     KElaboratedTypeSpefType,
     KNameType,
     KAbiTagAttr,
+    KEnableIfAttr,
     KObjCProtoName,
     KPointerType,
     KLValueReferenceType,
@@ -188,6 +190,7 @@
     KTemplateArgumentPack,
     KParameterPackExpansion,
     KTemplateArgs,
+    KForwardTemplateReference,
     KNameWithTemplateArgs,
     KGlobalQualifiedName,
     KStdQualifiedName,
@@ -197,15 +200,12 @@
     KDtorName,
     KUnnamedTypeName,
     KClosureTypeName,
+    KStructuredBindingName,
     KExpr,
     KBracedExpr,
     KBracedRangeExpr,
   };
 
-  static constexpr unsigned NoParameterPack =
-    std::numeric_limits<unsigned>::max();
-  unsigned ParameterPackSize = NoParameterPack;
-
   Kind K;
 
   /// Three-way bool to track a cached value. Unknown is possible if this node
@@ -224,17 +224,11 @@
   /// affect how we format the output string.
   Cache FunctionCache;
 
-  Node(Kind K_, unsigned ParameterPackSize_ = NoParameterPack,
-       Cache RHSComponentCache_ = Cache::No, Cache ArrayCache_ = Cache::No,
-       Cache FunctionCache_ = Cache::No)
-      : ParameterPackSize(ParameterPackSize_), K(K_),
-        RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_),
+  Node(Kind K_, Cache RHSComponentCache_ = Cache::No,
+       Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No)
+      : K(K_), RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_),
         FunctionCache(FunctionCache_) {}
 
-  bool containsUnexpandedParameterPack() const {
-    return ParameterPackSize != NoParameterPack;
-  }
-
   bool hasRHSComponent(OutputStream &S) const {
     if (RHSComponentCache != Cache::Unknown)
       return RHSComponentCache == Cache::Yes;
@@ -259,10 +253,6 @@
   virtual bool hasArraySlow(OutputStream &) const { return false; }
   virtual bool hasFunctionSlow(OutputStream &) const { return false; }
 
-  /// If this node is a pack expansion that expands to 0 elements. This can have
-  /// an effect on how we should format the output.
-  bool isEmptyPackExpansion() const;
-
   void print(OutputStream &S) const {
     printLeft(S);
     if (RHSComponentCache != Cache::No)
@@ -315,12 +305,20 @@
   void printWithComma(OutputStream &S) const {
     bool FirstElement = true;
     for (size_t Idx = 0; Idx != NumElements; ++Idx) {
-      if (Elements[Idx]->isEmptyPackExpansion())
-        continue;
+      size_t BeforeComma = S.getCurrentPosition();
       if (!FirstElement)
         S += ", ";
-      FirstElement = false;
+      size_t AfterComma = S.getCurrentPosition();
       Elements[Idx]->print(S);
+
+      // Elements[Idx] is an empty parameter pack expansion, we should erase the
+      // comma we just printed.
+      if (AfterComma == S.getCurrentPosition()) {
+        S.setCurrentPosition(BeforeComma);
+        continue;
+      }
+
+      FirstElement = false;
     }
   }
 };
@@ -347,8 +345,7 @@
 
 public:
   VendorExtQualType(Node *Ty_, StringView Ext_)
-      : Node(KVendorExtQualType, Ty_->ParameterPackSize),
-        Ty(Ty_), Ext(Ext_) {}
+      : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_) {}
 
   void printLeft(OutputStream &S) const override {
     Ty->print(S);
@@ -390,7 +387,7 @@
 
 public:
   QualType(Node *Child_, Qualifiers Quals_)
-      : Node(KQualType, Child_->ParameterPackSize, Child_->RHSComponentCache,
+      : Node(KQualType, Child_->RHSComponentCache,
              Child_->ArrayCache, Child_->FunctionCache),
         Quals(Quals_), Child(Child_) {}
 
@@ -417,7 +414,7 @@
 
 public:
   ConversionOperatorType(Node *Ty_)
-      : Node(KConversionOperatorType, Ty_->ParameterPackSize), Ty(Ty_) {}
+      : Node(KConversionOperatorType), Ty(Ty_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "operator ";
@@ -431,8 +428,7 @@
 
 public:
   PostfixQualifiedType(Node *Ty_, StringView Postfix_)
-      : Node(KPostfixQualifiedType, Ty_->ParameterPackSize),
-        Ty(Ty_), Postfix(Postfix_) {}
+      : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {}
 
   void printLeft(OutputStream &s) const override {
     Ty->printLeft(s);
@@ -457,9 +453,7 @@
   Node *Child;
 public:
   ElaboratedTypeSpefType(StringView Kind_, Node *Child_)
-      : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {
-    ParameterPackSize = Child->ParameterPackSize;
-  }
+      : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {}
 
   void printLeft(OutputStream &S) const override {
     S += Kind;
@@ -473,7 +467,7 @@
   StringView Tag;
 public:
   AbiTagAttr(const Node* Base_, StringView Tag_)
-      : Node(KAbiTagAttr, Base_->ParameterPackSize, Base_->RHSComponentCache,
+      : Node(KAbiTagAttr, Base_->RHSComponentCache,
              Base_->ArrayCache, Base_->FunctionCache),
         Base(Base_), Tag(Tag_) {}
 
@@ -485,6 +479,19 @@
   }
 };
 
+class EnableIfAttr : public Node {
+  NodeArray Conditions;
+public:
+  EnableIfAttr(NodeArray Conditions_)
+      : Node(KEnableIfAttr), Conditions(Conditions_) {}
+
+  void printLeft(OutputStream &S) const override {
+    S += " [enable_if:";
+    Conditions.printWithComma(S);
+    S += ']';
+  }
+};
+
 class ObjCProtoName : public Node {
   Node *Ty;
   StringView Protocol;
@@ -513,8 +520,7 @@
 
 public:
   PointerType(Node *Pointee_)
-      : Node(KPointerType, Pointee_->ParameterPackSize,
-             Pointee_->RHSComponentCache),
+      : Node(KPointerType, Pointee_->RHSComponentCache),
         Pointee(Pointee_) {}
 
   bool hasRHSComponentSlow(OutputStream &S) const override {
@@ -554,8 +560,7 @@
 
 public:
   LValueReferenceType(Node *Pointee_)
-      : Node(KLValueReferenceType, Pointee_->ParameterPackSize,
-             Pointee_->RHSComponentCache),
+      : Node(KLValueReferenceType, Pointee_->RHSComponentCache),
         Pointee(Pointee_) {}
 
   bool hasRHSComponentSlow(OutputStream &S) const override {
@@ -583,8 +588,7 @@
 
 public:
   RValueReferenceType(Node *Pointee_)
-      : Node(KRValueReferenceType, Pointee_->ParameterPackSize,
-             Pointee_->RHSComponentCache),
+      : Node(KRValueReferenceType, Pointee_->RHSComponentCache),
         Pointee(Pointee_) {}
 
   bool hasRHSComponentSlow(OutputStream &S) const override {
@@ -614,10 +618,7 @@
 
 public:
   PointerToMemberType(Node *ClassType_, Node *MemberType_)
-      : Node(KPointerToMemberType,
-             std::min(MemberType_->ParameterPackSize,
-                      ClassType_->ParameterPackSize),
-             MemberType_->RHSComponentCache),
+      : Node(KPointerToMemberType, MemberType_->RHSComponentCache),
         ClassType(ClassType_), MemberType(MemberType_) {}
 
   bool hasRHSComponentSlow(OutputStream &S) const override {
@@ -683,18 +684,14 @@
 
 public:
   ArrayType(Node *Base_, NodeOrString Dimension_)
-      : Node(KArrayType, Base_->ParameterPackSize,
+      : Node(KArrayType,
              /*RHSComponentCache=*/Cache::Yes,
              /*ArrayCache=*/Cache::Yes),
-        Base(Base_), Dimension(Dimension_) {
-    if (Dimension.isNode())
-      ParameterPackSize =
-          std::min(ParameterPackSize, Dimension.asNode()->ParameterPackSize);
-  }
+        Base(Base_), Dimension(Dimension_) {}
 
   // Incomplete array type.
   ArrayType(Node *Base_)
-      : Node(KArrayType, Base_->ParameterPackSize,
+      : Node(KArrayType,
              /*RHSComponentCache=*/Cache::Yes,
              /*ArrayCache=*/Cache::Yes),
         Base(Base_) {}
@@ -727,17 +724,11 @@
 public:
   FunctionType(Node *Ret_, NodeArray Params_, Qualifiers CVQuals_,
                FunctionRefQual RefQual_, Node *ExceptionSpec_)
-      : Node(KFunctionType, Ret_->ParameterPackSize,
+      : Node(KFunctionType,
              /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No,
              /*FunctionCache=*/Cache::Yes),
         Ret(Ret_), Params(Params_), CVQuals(CVQuals_), RefQual(RefQual_),
-        ExceptionSpec(ExceptionSpec_) {
-    for (Node *P : Params)
-      ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
-    if (ExceptionSpec != nullptr)
-      ParameterPackSize =
-        std::min(ParameterPackSize, ExceptionSpec->ParameterPackSize);
-  }
+        ExceptionSpec(ExceptionSpec_) {}
 
   bool hasRHSComponentSlow(OutputStream &) const override { return true; }
   bool hasFunctionSlow(OutputStream &) const override { return true; }
@@ -782,7 +773,7 @@
 class NoexceptSpec : public Node {
   Node *E;
 public:
-  NoexceptSpec(Node *E_) : Node(KNoexceptSpec, E_->ParameterPackSize), E(E_) {}
+  NoexceptSpec(Node *E_) : Node(KNoexceptSpec), E(E_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "noexcept(";
@@ -795,10 +786,7 @@
   NodeArray Types;
 public:
   DynamicExceptionSpec(NodeArray Types_)
-      : Node(KDynamicExceptionSpec), Types(Types_) {
-    for (Node *T : Types)
-      ParameterPackSize = std::min(ParameterPackSize, T->ParameterPackSize);
-  }
+      : Node(KDynamicExceptionSpec), Types(Types_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "throw(";
@@ -811,22 +799,18 @@
   const Node *Ret;
   const Node *Name;
   NodeArray Params;
+  Node *Attrs;
   Qualifiers CVQuals;
   FunctionRefQual RefQual;
 
 public:
   FunctionEncoding(Node *Ret_, Node *Name_, NodeArray Params_,
-                   Qualifiers CVQuals_, FunctionRefQual RefQual_)
-      : Node(KFunctionEncoding, NoParameterPack,
+                   Node *Attrs_, Qualifiers CVQuals_, FunctionRefQual RefQual_)
+      : Node(KFunctionEncoding,
              /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No,
              /*FunctionCache=*/Cache::Yes),
-        Ret(Ret_), Name(Name_), Params(Params_), CVQuals(CVQuals_),
-        RefQual(RefQual_) {
-    for (Node *P : Params)
-      ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
-    if (Ret)
-      ParameterPackSize = std::min(ParameterPackSize, Ret->ParameterPackSize);
-  }
+        Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_),
+        CVQuals(CVQuals_), RefQual(RefQual_) {}
 
   bool hasRHSComponentSlow(OutputStream &) const override { return true; }
   bool hasFunctionSlow(OutputStream &) const override { return true; }
@@ -860,6 +844,9 @@
       S += " &";
     else if (RefQual == FrefQualRValue)
       S += " &&";
+
+    if (Attrs != nullptr)
+      Attrs->print(S);
   }
 };
 
@@ -867,8 +854,7 @@
   const Node *OpName;
 
 public:
-  LiteralOperator(Node *OpName_)
-      : Node(KLiteralOperator, OpName_->ParameterPackSize), OpName(OpName_) {}
+  LiteralOperator(Node *OpName_) : Node(KLiteralOperator), OpName(OpName_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "operator\"\" ";
@@ -882,8 +868,7 @@
 
 public:
   SpecialName(StringView Special_, Node* Child_)
-      : Node(KSpecialName, Child_->ParameterPackSize), Special(Special_),
-        Child(Child_) {}
+      : Node(KSpecialName), Special(Special_), Child(Child_) {}
 
   void printLeft(OutputStream &S) const override {
     S += Special;
@@ -897,8 +882,7 @@
 
 public:
   CtorVtableSpecialName(Node *FirstType_, Node *SecondType_)
-      : Node(KCtorVtableSpecialName, std::min(FirstType_->ParameterPackSize,
-                                              SecondType_->ParameterPackSize)),
+      : Node(KCtorVtableSpecialName),
         FirstType(FirstType_), SecondType(SecondType_) {}
 
   void printLeft(OutputStream &S) const override {
@@ -916,9 +900,7 @@
 
 public:
   QualifiedName(Node* Qualifier_, Node* Name_)
-      : Node(KQualifiedName,
-             std::min(Qualifier_->ParameterPackSize, Name_->ParameterPackSize)),
-        Qualifier(Qualifier_), Name(Name_) {}
+      : Node(KQualifiedName), Qualifier(Qualifier_), Name(Name_) {}
 
   StringView getBaseName() const override { return Name->getBaseName(); }
 
@@ -943,17 +925,10 @@
 public:
   VectorType(NodeOrString Dimension_)
       : Node(KVectorType), BaseType(nullptr), Dimension(Dimension_),
-        IsPixel(true) {
-    if (Dimension.isNode())
-      ParameterPackSize = Dimension.asNode()->ParameterPackSize;
-  }
+        IsPixel(true) {}
   VectorType(Node *BaseType_, NodeOrString Dimension_)
-      : Node(KVectorType, BaseType_->ParameterPackSize), BaseType(BaseType_),
-        Dimension(Dimension_), IsPixel(false) {
-    if (Dimension.isNode())
-      ParameterPackSize =
-          std::min(ParameterPackSize, Dimension.asNode()->ParameterPackSize);
-  }
+      : Node(KVectorType), BaseType(BaseType_),
+        Dimension(Dimension_), IsPixel(false) {}
 
   void printLeft(OutputStream &S) const override {
     if (IsPixel) {
@@ -982,9 +957,17 @@
 /// T_).
 class ParameterPack final : public Node {
   NodeArray Data;
+
+  // Setup OutputStream for a pack expansion unless we're already expanding one.
+  void initializePackExpansion(OutputStream &S) const {
+    if (S.CurrentPackMax == std::numeric_limits<unsigned>::max()) {
+      S.CurrentPackMax = static_cast<unsigned>(Data.size());
+      S.CurrentPackIndex = 0;
+    }
+  }
+
 public:
-  ParameterPack(NodeArray Data_)
-      : Node(KParameterPack, static_cast<unsigned>(Data_.size())), Data(Data_) {
+  ParameterPack(NodeArray Data_) : Node(KParameterPack), Data(Data_) {
     ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown;
     if (std::all_of(Data.begin(), Data.end(), [](Node* P) {
           return P->ArrayCache == Cache::No;
@@ -1001,24 +984,29 @@
   }
 
   bool hasRHSComponentSlow(OutputStream &S) const override {
+    initializePackExpansion(S);
     size_t Idx = S.CurrentPackIndex;
     return Idx < Data.size() && Data[Idx]->hasRHSComponent(S);
   }
   bool hasArraySlow(OutputStream &S) const override {
+    initializePackExpansion(S);
     size_t Idx = S.CurrentPackIndex;
     return Idx < Data.size() && Data[Idx]->hasArray(S);
   }
   bool hasFunctionSlow(OutputStream &S) const override {
+    initializePackExpansion(S);
     size_t Idx = S.CurrentPackIndex;
     return Idx < Data.size() && Data[Idx]->hasFunction(S);
   }
 
   void printLeft(OutputStream &S) const override {
+    initializePackExpansion(S);
     size_t Idx = S.CurrentPackIndex;
     if (Idx < Data.size())
       Data[Idx]->printLeft(S);
   }
   void printRight(OutputStream &S) const override {
+    initializePackExpansion(S);
     size_t Idx = S.CurrentPackIndex;
     if (Idx < Data.size())
       Data[Idx]->printRight(S);
@@ -1034,10 +1022,7 @@
   NodeArray Elements;
 public:
   TemplateArgumentPack(NodeArray Elements_)
-      : Node(KTemplateArgumentPack), Elements(Elements_) {
-    for (Node *E : Elements)
-      ParameterPackSize = std::min(E->ParameterPackSize, ParameterPackSize);
-  }
+      : Node(KTemplateArgumentPack), Elements(Elements_) {}
 
   NodeArray getElements() const { return Elements; }
 
@@ -1058,66 +1043,103 @@
   const Node *getChild() const { return Child; }
 
   void printLeft(OutputStream &S) const override {
-    unsigned PackSize = Child->ParameterPackSize;
-    if (PackSize == NoParameterPack) {
-      Child->print(S);
+    constexpr unsigned Max = std::numeric_limits<unsigned>::max();
+    SwapAndRestore<unsigned> SavePackIdx(S.CurrentPackIndex, Max);
+    SwapAndRestore<unsigned> SavePackMax(S.CurrentPackMax, Max);
+    size_t StreamPos = S.getCurrentPosition();
+
+    // Print the first element in the pack. If Child contains a ParameterPack,
+    // it will set up S.CurrentPackMax and print the first element.
+    Child->print(S);
+
+    // No ParameterPack was found in Child. This can occur if we've found a pack
+    // expansion on a <function-param>.
+    if (S.CurrentPackMax == Max) {
       S += "...";
       return;
     }
 
-    SwapAndRestore<unsigned> SavePackIndex(S.CurrentPackIndex, 0);
-    for (unsigned I = 0; I != PackSize; ++I) {
-      if (I != 0)
-        S += ", ";
+    // We found a ParameterPack, but it has no elements. Erase whatever we may
+    // of printed.
+    if (S.CurrentPackMax == 0) {
+      S.setCurrentPosition(StreamPos);
+      return;
+    }
+
+    // Else, iterate through the rest of the elements in the pack.
+    for (unsigned I = 1, E = S.CurrentPackMax; I < E; ++I) {
+      S += ", ";
       S.CurrentPackIndex = I;
       Child->print(S);
     }
   }
 };
 
-inline bool Node::isEmptyPackExpansion() const {
-  if (getKind() == KParameterPackExpansion) {
-    auto *AsPack = static_cast<const ParameterPackExpansion *>(this);
-    return AsPack->getChild()->isEmptyPackExpansion();
-  }
-  if (getKind() == KTemplateArgumentPack) {
-    auto *AsTemplateArg = static_cast<const TemplateArgumentPack *>(this);
-    for (Node *E : AsTemplateArg->getElements())
-      if (!E->isEmptyPackExpansion())
-        return false;
-    return true;
-  }
-  return ParameterPackSize == 0;
-}
-
 class TemplateArgs final : public Node {
   NodeArray Params;
 
 public:
-  TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {
-    for (Node *P : Params)
-      ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
-  }
+  TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {}
 
   NodeArray getParams() { return Params; }
 
   void printLeft(OutputStream &S) const override {
     S += "<";
-    bool FirstElement = true;
-    for (size_t Idx = 0, E = Params.size(); Idx != E; ++Idx) {
-      if (Params[Idx]->isEmptyPackExpansion())
-        continue;
-      if (!FirstElement)
-        S += ", ";
-      FirstElement = false;
-      Params[Idx]->print(S);
-    }
+    Params.printWithComma(S);
     if (S.back() == '>')
       S += " ";
     S += ">";
   }
 };
 
+struct ForwardTemplateReference : Node {
+  size_t Index;
+  Node *Ref = nullptr;
+
+  // If we're currently printing this node. It is possible (though invalid) for
+  // a forward template reference to refer to itself via a substitution. This
+  // creates a cyclic AST, which will stack overflow printing. To fix this, bail
+  // out if more than one print* function is active.
+  mutable bool Printing = false;
+
+  ForwardTemplateReference(size_t Index_)
+      : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown,
+             Cache::Unknown),
+        Index(Index_) {}
+
+  bool hasRHSComponentSlow(OutputStream &S) const override {
+    if (Printing)
+      return false;
+    SwapAndRestore<bool> SavePrinting(Printing, true);
+    return Ref->hasRHSComponent(S);
+  }
+  bool hasArraySlow(OutputStream &S) const override {
+    if (Printing)
+      return false;
+    SwapAndRestore<bool> SavePrinting(Printing, true);
+    return Ref->hasArray(S);
+  }
+  bool hasFunctionSlow(OutputStream &S) const override {
+    if (Printing)
+      return false;
+    SwapAndRestore<bool> SavePrinting(Printing, true);
+    return Ref->hasFunction(S);
+  }
+
+  void printLeft(OutputStream &S) const override {
+    if (Printing)
+      return;
+    SwapAndRestore<bool> SavePrinting(Printing, true);
+    Ref->printLeft(S);
+  }
+  void printRight(OutputStream &S) const override {
+    if (Printing)
+      return;
+    SwapAndRestore<bool> SavePrinting(Printing, true);
+    Ref->printRight(S);
+  }
+};
+
 class NameWithTemplateArgs final : public Node {
   // name<template_args>
   Node *Name;
@@ -1125,9 +1147,7 @@
 
 public:
   NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_)
-      : Node(KNameWithTemplateArgs, std::min(Name_->ParameterPackSize,
-                                             TemplateArgs_->ParameterPackSize)),
-        Name(Name_), TemplateArgs(TemplateArgs_) {}
+      : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {}
 
   StringView getBaseName() const override { return Name->getBaseName(); }
 
@@ -1142,7 +1162,7 @@
 
 public:
   GlobalQualifiedName(Node* Child_)
-      : Node(KGlobalQualifiedName, Child_->ParameterPackSize), Child(Child_) {}
+      : Node(KGlobalQualifiedName), Child(Child_) {}
 
   StringView getBaseName() const override { return Child->getBaseName(); }
 
@@ -1156,8 +1176,7 @@
   Node *Child;
 
 public:
-  StdQualifiedName(Node *Child_)
-      : Node(KStdQualifiedName, Child_->ParameterPackSize), Child(Child_) {}
+  StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {}
 
   StringView getBaseName() const override { return Child->getBaseName(); }
 
@@ -1280,8 +1299,7 @@
 
 public:
   CtorDtorName(Node *Basename_, bool IsDtor_)
-      : Node(KCtorDtorName, Basename_->ParameterPackSize),
-        Basename(Basename_), IsDtor(IsDtor_) {}
+      : Node(KCtorDtorName), Basename(Basename_), IsDtor(IsDtor_) {}
 
   void printLeft(OutputStream &S) const override {
     if (IsDtor)
@@ -1294,9 +1312,7 @@
   const Node *Base;
 
 public:
-  DtorName(Node *Base_) : Node(KDtorName), Base(Base_) {
-    ParameterPackSize = Base->ParameterPackSize;
-  }
+  DtorName(Node *Base_) : Node(KDtorName), Base(Base_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "~";
@@ -1323,10 +1339,7 @@
 
 public:
   ClosureTypeName(NodeArray Params_, StringView Count_)
-      : Node(KClosureTypeName), Params(Params_), Count(Count_) {
-    for (Node *P : Params)
-      ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
-  }
+      : Node(KClosureTypeName), Params(Params_), Count(Count_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "\'lambda";
@@ -1337,6 +1350,19 @@
   }
 };
 
+class StructuredBindingName : public Node {
+  NodeArray Bindings;
+public:
+  StructuredBindingName(NodeArray Bindings_)
+      : Node(KStructuredBindingName), Bindings(Bindings_) {}
+
+  void printLeft(OutputStream &S) const override {
+    S += '[';
+    Bindings.printWithComma(S);
+    S += ']';
+  }
+};
+
 // -- Expression Nodes --
 
 struct Expr : public Node {
@@ -1350,10 +1376,7 @@
 
 public:
   BinaryExpr(Node *LHS_, StringView InfixOperator_, Node *RHS_)
-      : LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) {
-    ParameterPackSize =
-      std::min(LHS->ParameterPackSize, RHS->ParameterPackSize);
-  }
+      : LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) {}
 
   void printLeft(OutputStream &S) const override {
     // might be a template argument expression, then we need to disambiguate
@@ -1379,10 +1402,7 @@
   const Node *Op2;
 
 public:
-  ArraySubscriptExpr(Node *Op1_, Node *Op2_) : Op1(Op1_), Op2(Op2_) {
-    ParameterPackSize =
-      std::min(Op1->ParameterPackSize, Op2->ParameterPackSize);
-  }
+  ArraySubscriptExpr(Node *Op1_, Node *Op2_) : Op1(Op1_), Op2(Op2_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "(";
@@ -1399,9 +1419,7 @@
 
 public:
   PostfixExpr(Node *Child_, StringView Operand_)
-      : Child(Child_), Operand(Operand_) {
-    ParameterPackSize = Child->ParameterPackSize;
-  }
+      : Child(Child_), Operand(Operand_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "(";
@@ -1418,11 +1436,7 @@
 
 public:
   ConditionalExpr(Node *Cond_, Node *Then_, Node *Else_)
-      : Cond(Cond_), Then(Then_), Else(Else_) {
-    ParameterPackSize =
-        std::min(Cond->ParameterPackSize,
-                 std::min(Then->ParameterPackSize, Else->ParameterPackSize));
-  }
+      : Cond(Cond_), Then(Then_), Else(Else_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "(";
@@ -1442,10 +1456,7 @@
 
 public:
   MemberExpr(Node *LHS_, StringView Kind_, Node *RHS_)
-      : LHS(LHS_), Kind(Kind_), RHS(RHS_) {
-    ParameterPackSize =
-      std::min(LHS->ParameterPackSize, RHS->ParameterPackSize);
-  }
+      : LHS(LHS_), Kind(Kind_), RHS(RHS_) {}
 
   void printLeft(OutputStream &S) const override {
     LHS->print(S);
@@ -1461,9 +1472,7 @@
 
 public:
   EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_)
-      : Prefix(Prefix_), Infix(Infix_), Postfix(Postfix_) {
-    ParameterPackSize = Infix->ParameterPackSize;
-  }
+      : Prefix(Prefix_), Infix(Infix_), Postfix(Postfix_) {}
 
   void printLeft(OutputStream &S) const override {
     S += Prefix;
@@ -1480,10 +1489,7 @@
 
 public:
   CastExpr(StringView CastKind_, Node *To_, Node *From_)
-      : CastKind(CastKind_), To(To_), From(From_) {
-    ParameterPackSize =
-      std::min(To->ParameterPackSize, From->ParameterPackSize);
-  }
+      : CastKind(CastKind_), To(To_), From(From_) {}
 
   void printLeft(OutputStream &S) const override {
     S += CastKind;
@@ -1514,11 +1520,7 @@
   NodeArray Args;
 
 public:
-  CallExpr(Node *Callee_, NodeArray Args_) : Callee(Callee_), Args(Args_) {
-    for (Node *P : Args)
-      ParameterPackSize = std::min(ParameterPackSize, P->ParameterPackSize);
-    ParameterPackSize = std::min(ParameterPackSize, Callee->ParameterPackSize);
-  }
+  CallExpr(Node *Callee_, NodeArray Args_) : Callee(Callee_), Args(Args_) {}
 
   void printLeft(OutputStream &S) const override {
     Callee->print(S);
@@ -1539,14 +1541,7 @@
   NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_,
           bool IsArray_)
       : ExprList(ExprList_), Type(Type_), InitList(InitList_),
-        IsGlobal(IsGlobal_), IsArray(IsArray_) {
-    for (Node *E : ExprList)
-      ParameterPackSize = std::min(ParameterPackSize, E->ParameterPackSize);
-    for (Node *I : InitList)
-      ParameterPackSize = std::min(ParameterPackSize, I->ParameterPackSize);
-    if (Type)
-      ParameterPackSize = std::min(ParameterPackSize, Type->ParameterPackSize);
-  }
+        IsGlobal(IsGlobal_), IsArray(IsArray_) {}
 
   void printLeft(OutputStream &S) const override {
     if (IsGlobal)
@@ -1577,9 +1572,7 @@
 
 public:
   DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_)
-      : Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {
-    ParameterPackSize = Op->ParameterPackSize;
-  }
+      : Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {}
 
   void printLeft(OutputStream &S) const override {
     if (IsGlobal)
@@ -1596,9 +1589,7 @@
   Node *Child;
 
 public:
-  PrefixExpr(StringView Prefix_, Node *Child_) : Prefix(Prefix_), Child(Child_) {
-    ParameterPackSize = Child->ParameterPackSize;
-  }
+  PrefixExpr(StringView Prefix_, Node *Child_) : Prefix(Prefix_), Child(Child_) {}
 
   void printLeft(OutputStream &S) const override {
     S += Prefix;
@@ -1626,11 +1617,7 @@
 
 public:
   ConversionExpr(const Node *Type_, NodeArray Expressions_)
-      : Type(Type_), Expressions(Expressions_) {
-    for (Node *E : Expressions)
-      ParameterPackSize = std::min(ParameterPackSize, E->ParameterPackSize);
-    ParameterPackSize = std::min(ParameterPackSize, Type->ParameterPackSize);
-  }
+      : Type(Type_), Expressions(Expressions_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "(";
@@ -1645,13 +1632,7 @@
   Node *Ty;
   NodeArray Inits;
 public:
-  InitListExpr(Node *Ty_, NodeArray Inits_)
-      : Ty(Ty_), Inits(Inits_) {
-    if (Ty)
-      ParameterPackSize = Ty->ParameterPackSize;
-    for (Node *I : Inits)
-      ParameterPackSize = std::min(I->ParameterPackSize, ParameterPackSize);
-  }
+  InitListExpr(Node *Ty_, NodeArray Inits_) : Ty(Ty_), Inits(Inits_) {}
 
   void printLeft(OutputStream &S) const override {
     if (Ty)
@@ -1709,9 +1690,7 @@
   const Node *Op;
 
 public:
-  ThrowExpr(Node *Op_) : Op(Op_) {
-    ParameterPackSize = Op->ParameterPackSize;
-  }
+  ThrowExpr(Node *Op_) : Op(Op_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "throw ";
@@ -1736,9 +1715,8 @@
   StringView Integer;
 
 public:
-  IntegerCastExpr(Node *Ty_, StringView Integer_) : Ty(Ty_), Integer(Integer_) {
-    ParameterPackSize = Ty->ParameterPackSize;
-  }
+  IntegerCastExpr(Node *Ty_, StringView Integer_)
+      : Ty(Ty_), Integer(Integer_) {}
 
   void printLeft(OutputStream &S) const override {
     S += "(";
@@ -1995,10 +1973,13 @@
   // stored on the stack.
   PODSmallVector<Node *, 8> TemplateParams;
 
-  unsigned EncodingDepth = 0;
-  bool TagTemplates = true;
-  bool FixForwardReferences = false;
+  // Set of unresolved forward <template-param> references. These can occur in a
+  // conversion operator's type, and are resolved in the enclosing <encoding>.
+  PODSmallVector<ForwardTemplateReference *, 4> ForwardTemplateRefs;
+
   bool TryToParseTemplateArgs = true;
+  bool PermitForwardTemplateReferences = false;
+  bool ParsingLambdaParams = false;
 
   BumpPointerAllocator ASTAllocator;
 
@@ -2059,7 +2040,7 @@
   bool parseSeqId(size_t *Out);
   Node *parseSubstitution();
   Node *parseTemplateParam();
-  Node *parseTemplateArgs();
+  Node *parseTemplateArgs(bool TagTemplates = false);
   Node *parseTemplateArg();
 
   /// Parse the <expr> production.
@@ -2095,8 +2076,25 @@
     bool EndsWithTemplateArgs = false;
     Qualifiers CVQualifiers = QualNone;
     FunctionRefQual ReferenceQualifier = FrefQualNone;
+    size_t ForwardTemplateRefsBegin;
+
+    NameState(Db *Enclosing)
+        : ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {}
   };
 
+  bool resolveForwardTemplateRefs(NameState &State) {
+    size_t I = State.ForwardTemplateRefsBegin;
+    size_t E = ForwardTemplateRefs.size();
+    for (; I < E; ++I) {
+      size_t Idx = ForwardTemplateRefs[I]->Index;
+      if (Idx >= TemplateParams.size())
+        return true;
+      ForwardTemplateRefs[I]->Ref = TemplateParams[Idx];
+    }
+    ForwardTemplateRefs.dropBack(State.ForwardTemplateRefsBegin);
+    return false;
+  }
+
   /// Parse the <name> production>
   Node *parseName(NameState *State = nullptr);
   Node *parseLocalName(NameState *State);
@@ -2145,7 +2143,7 @@
       return nullptr;
     if (look() != 'I')
       return nullptr;
-    Node *TA = parseTemplateArgs();
+    Node *TA = parseTemplateArgs(State != nullptr);
     if (TA == nullptr)
       return nullptr;
     if (State) State->EndsWithTemplateArgs = true;
@@ -2158,7 +2156,7 @@
   //        ::= <unscoped-template-name> <template-args>
   if (look() == 'I') {
     Subs.push_back(N);
-    Node *TA = parseTemplateArgs();
+    Node *TA = parseTemplateArgs(State != nullptr);
     if (TA == nullptr)
       return nullptr;
     if (State) State->EndsWithTemplateArgs = true;
@@ -2217,7 +2215,7 @@
 //                    ::= <ctor-dtor-name>
 //                    ::= <source-name>
 //                    ::= <unnamed-type-name>
-// FIXME:             ::= DC <source-name>+ E      # structured binding declaration
+//                    ::= DC <source-name>+ E      # structured binding declaration
 Node *Db::parseUnqualifiedName(NameState *State) {
  // <ctor-dtor-name>s are special-cased in parseNestedName().
  Node *Result;
@@ -2225,7 +2223,16 @@
    Result = parseUnnamedTypeName(State);
  else if (look() >= '1' && look() <= '9')
    Result = parseSourceName(State);
- else
+ else if (consumeIf("DC")) {
+   size_t BindingsBegin = Names.size();
+   do {
+     Node *Binding = parseSourceName(State);
+     if (Binding == nullptr)
+       return nullptr;
+     Names.push_back(Binding);
+   } while (!consumeIf('E'));
+   Result = make<StructuredBindingName>(popTrailingNodeArray(BindingsBegin));
+ } else
    Result = parseOperatorName(State);
  if (Result != nullptr)
    Result = parseAbiTags(Result);
@@ -2247,6 +2254,7 @@
   }
   if (consumeIf("Ul")) {
     NodeArray Params;
+    SwapAndRestore<bool> SwapParams(ParsingLambdaParams, true);
     if (!consumeIf("vE")) {
       size_t ParamsBegin = Names.size();
       do {
@@ -2362,9 +2370,15 @@
       return make<NameType>("operator~");
     //                   ::= cv <type>    # (cast)
     case 'v': {
-      SwapAndRestore<bool> SaveTemplate(TryToParseTemplateArgs, false);
       First += 2;
-      Node *Ty = parseType();
+      SwapAndRestore<bool> SaveTemplate(TryToParseTemplateArgs, false);
+      // If we're parsing an encoding, State != nullptr and the conversion
+      // operators' <type> could have a <template-param> that refers to some
+      // <template-arg>s further ahead in the mangled name.
+      SwapAndRestore<bool> SavePermit(PermitForwardTemplateReferences,
+                                      PermitForwardTemplateReferences ||
+                                          State != nullptr);
+      Node* Ty = parseType();
       if (Ty == nullptr)
         return nullptr;
       if (State) State->CtorDtorConversion = true;
@@ -2588,7 +2602,7 @@
     ++First;
     if (State) State->CtorDtorConversion = true;
     if (IsInherited) {
-      if (parseName() == nullptr)
+      if (parseName(State) == nullptr)
         return nullptr;
     }
     return make<CtorDtorName>(SoFar, false);
@@ -2658,7 +2672,7 @@
 
     //          ::= <template-prefix> <template-args>
     if (look() == 'I') {
-      Node *TA = parseTemplateArgs();
+      Node *TA = parseTemplateArgs(State != nullptr);
       if (TA == nullptr || SoFar == nullptr)
         return nullptr;
       SoFar = make<NameWithTemplateArgs>(SoFar, TA);
@@ -2689,7 +2703,7 @@
     }
 
     // Parse an <unqualified-name> thats actually a <ctor-dtor-name>.
-    if (look() == 'C' || look() == 'D') {
+    if (look() == 'C' || (look() == 'D' && look(1) != 'C')) {
       if (SoFar == nullptr)
         return nullptr;
       Node *CtorDtor = parseCtorDtorName(SoFar, State);
@@ -4301,6 +4315,8 @@
 //                                     # No <type>
 //                ::= TW <object name> # Thread-local wrapper
 //                ::= TH <object name> # Thread-local initialization
+//                ::= GR <object name> _             # First temporary
+//                ::= GR <object name> <seq-id> _    # Subsequent temporaries
 //      extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
 //      extension ::= GR <object name> # reference temporary for object
 Node *Db::parseSpecialName() {
@@ -4405,11 +4421,17 @@
       return make<SpecialName>("guard variable for ", Name);
     }
     // GR <object name> # reference temporary for object
+    // GR <object name> _             # First temporary
+    // GR <object name> <seq-id> _    # Subsequent temporaries
     case 'R': {
       First += 2;
       Node *Name = parseName();
       if (Name == nullptr)
         return nullptr;
+      size_t Count;
+      bool ParsedSeqId = !parseSeqId(&Count);
+      if (!consumeIf('_') && ParsedSeqId)
+        return nullptr;
       return make<SpecialName>("reference temporary for ", Name);
     }
     }
@@ -4421,15 +4443,6 @@
 //            ::= <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();
 
@@ -4440,12 +4453,28 @@
     return numLeft() == 0 || look() == 'E' || look() == '.' || look() == '_';
   };
 
-  NameState NameInfo;
+  NameState NameInfo(this);
   Node *Name = parseName(&NameInfo);
-  if (Name == nullptr || IsEndOfEncoding())
+  if (Name == nullptr)
+    return nullptr;
+
+  if (resolveForwardTemplateRefs(NameInfo))
+    return nullptr;
+
+  if (IsEndOfEncoding())
     return Name;
 
-  TagTemplates = false;
+  Node *Attrs = nullptr;
+  if (consumeIf("Ua9enable_ifI")) {
+    size_t BeforeArgs = Names.size();
+    while (!consumeIf('E')) {
+      Node *Arg = parseTemplateArg();
+      if (Arg == nullptr)
+        return nullptr;
+      Names.push_back(Arg);
+    }
+    Attrs = make<EnableIfAttr>(popTrailingNodeArray(BeforeArgs));
+  }
 
   Node *ReturnType = nullptr;
   if (!NameInfo.CtorDtorConversion && NameInfo.EndsWithTemplateArgs) {
@@ -4456,7 +4485,7 @@
 
   if (consumeIf('v'))
     return make<FunctionEncoding>(ReturnType, Name, NodeArray(),
-                                  NameInfo.CVQualifiers,
+                                  Attrs, NameInfo.CVQualifiers,
                                   NameInfo.ReferenceQualifier);
 
   size_t ParamsBegin = Names.size();
@@ -4469,7 +4498,7 @@
 
   return make<FunctionEncoding>(ReturnType, Name,
                                 popTrailingNodeArray(ParamsBegin),
-                                NameInfo.CVQualifiers,
+                                Attrs, NameInfo.CVQualifiers,
                                 NameInfo.ReferenceQualifier);
 }
 
@@ -4627,24 +4656,30 @@
   if (!consumeIf('T'))
     return nullptr;
 
-  if (consumeIf('_')) {
-    if (TemplateParams.empty()) {
-      FixForwardReferences = true;
-      return make<NameType>("FORWARD_REFERENCE");
-    }
-    return TemplateParams[0];
+  size_t Index = 0;
+  if (!consumeIf('_')) {
+    if (parsePositiveInteger(&Index))
+      return nullptr;
+    ++Index;
+    if (!consumeIf('_'))
+      return nullptr;
   }
 
-  size_t Index;
-  if (parsePositiveInteger(&Index))
-    return nullptr;
-  ++Index;
-  if (!consumeIf('_'))
-    return nullptr;
-  if (Index >= TemplateParams.size()) {
-    FixForwardReferences = true;
-    return make<NameType>("FORWARD_REFERENCE");
+  // Itanium ABI 5.1.8: In a generic lambda, uses of auto in the parameter list
+  // are mangled as the corresponding artificial template type parameter.
+  if (ParsingLambdaParams)
+    return make<NameType>("auto");
+
+  // If we're in a context where this <template-param> refers to a
+  // <template-arg> further ahead in the mangled name (currently just conversion
+  // operator types), then we should only look it up in the right context.
+  if (PermitForwardTemplateReferences) {
+    ForwardTemplateRefs.push_back(make<ForwardTemplateReference>(Index));
+    return ForwardTemplateRefs.back();
   }
+
+  if (Index >= TemplateParams.size())
+    return nullptr;
   return TemplateParams[Index];
 }
 
@@ -4693,7 +4728,7 @@
 
 // <template-args> ::= I <template-arg>* E
 //     extension, the abi says <template-arg>+
-Node *Db::parseTemplateArgs() {
+Node *Db::parseTemplateArgs(bool TagTemplates) {
   if (!consumeIf('I'))
     return nullptr;
 
@@ -4788,8 +4823,8 @@
     Node *Encoding = parseEncoding();
     if (Encoding == nullptr || !consumeIf("_block_invoke"))
       return nullptr;
-    consumeIf('_');
-    if (parseNumber().empty())
+    bool RequireNumber = consumeIf('_');
+    if (parseNumber().empty() && RequireNumber)
       return nullptr;
     if (numLeft() != 0)
       return nullptr;
@@ -4830,23 +4865,9 @@
   if (AST == nullptr)
     InternalStatus = invalid_mangled_name;
 
-  if (InternalStatus == success && Parser.FixForwardReferences &&
-      !Parser.TemplateParams.empty()) {
-    Parser.FixForwardReferences = false;
-    Parser.TagTemplates = false;
-    Parser.Names.clear();
-    Parser.Subs.clear();
-    Parser.First = MangledName;
-    Parser.Last = MangledName + MangledNameLength;
-    AST = Parser.parse();
-    if (AST == nullptr || Parser.FixForwardReferences)
-      InternalStatus = invalid_mangled_name;
-  }
-
-  if (InternalStatus == success && AST->containsUnexpandedParameterPack())
-    InternalStatus = invalid_mangled_name;
-
   if (InternalStatus == success) {
+    assert(Parser.ForwardTemplateRefs.empty());
+
     if (Buf == nullptr) {
       BufSize = 1024;
       Buf = static_cast<char*>(std::malloc(BufSize));
diff --git a/test/test_demangle.pass.cpp b/test/test_demangle.pass.cpp
index b54f8d3..98921c2 100644
--- a/test/test_demangle.pass.cpp
+++ b/test/test_demangle.pass.cpp
@@ -29604,7 +29604,7 @@
     {"PFvRmOE", "void (*)(unsigned long&) &&"},
     {"_ZTW1x", "thread-local wrapper routine for x"},
     {"_ZTHN3fooE", "thread-local initialization routine for foo"},
-    {"_Z4algoIJiiiEEvZ1gEUlDpT_E_", "void algo<int, int, int>(g::'lambda'(int, int, int))"},
+
     // attribute abi_tag
     {"_Z1fB3foov", "f[abi:foo]()"},
     {"_Z1fB3fooB3barv", "f[abi:foo][abi:bar]()"},
@@ -29716,6 +29716,28 @@
 
     // ABI tags can apply to built-in substitutions.
     {"_Z1fSsB1XS_", "f(std::string[abi:X], std::string[abi:X])"},
+
+    {"___Z10blocksNRVOv_block_invoke", "invocation function for block in blocksNRVO()"},
+
+    // Structured bindings:
+    {"_ZDC2a12a2E", "[a1, a2]"},
+    {"_ZN2NSDC1x1yEE", "NS::[x, y]"},
+
+    {"_ZGRDC1x1yE_", "reference temporary for [x, y]"},
+    {"_ZGR1bIvE2_", "reference temporary for b<void>"},
+
+    {"_ZZ18test_assign_throwsI20small_throws_on_copyLb0EEvvENKUlRNSt3__13anyEOT_E_clIRS0_EEDaS3_S5_", "auto void test_assign_throws<small_throws_on_copy, false>()::'lambda'(std::__1::any&, auto&&)::operator()<small_throws_on_copy&>(std::__1::any&, auto&&) const"},
+
+    // enable_if attributes:
+    {"_Z1fUa9enable_ifIXLi1EEEv", "f() [enable_if:1]"},
+    {"_ZN5test4IdE1fEUa9enable_ifIXeqfL0p_Li1EEXeqfL0p0_Li2EEEi", "test4<double>::f(int) [enable_if:(fp) == (1), (fp0) == (2)]"},
+    {"_Z3quxUa9enable_ifIXLi1EEXL_Z9TRUEFACTSEEEi", "qux(int) [enable_if:1, TRUEFACTS]"},
+
+    // Conversion operators:
+    {"_ZN5OuterI4MarpEcv7MuncherIJT_T0_DpT1_EEI4MerpS0_JicfEEEv", "Outer<Marp>::operator Muncher<Merp, Marp, int, char, float><Merp, Marp, int, char, float>()"},
+    {"_ZN5OuterI4MarpEcvT_I4MerpEEv", "Outer<Marp>::operator Merp<Merp>()"},
+    {"_ZZN5OuterI4MarpEcv7MuncherIJT_T0_DpT1_EEI4MerpS0_JicfEEEvEN1ScvS9_Ev", "Outer<Marp>::operator Muncher<Merp, Marp, int, char, float><Merp, Marp, int, char, float>()::S::operator Merp()"},
+    {"_ZN1Scv7MuncherIJDpPT_EEIJFivEA_iEEEv", "S::operator Muncher<int (*)(), int (*) []><int (), int []>()"},
 };
 
 const unsigned N = sizeof(cases) / sizeof(cases[0]);
@@ -29787,7 +29809,6 @@
     "AT__ZSiIJEEAnwscT_T__",
     "FSiIJEENT_IoE ",
     "ZTVSiIZTVSiIZTVSiIZTVSiINIJEET_T_T_T_T_ ",
-    "_ZSiIJEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvttvvvvvvET_v",
     "Ana_T_E_T_IJEffffffffffffffersfffffrsrsffffffbgE",
 };
 
@@ -29797,22 +29818,25 @@
 {
     std::size_t len = 0;
     char* buf = nullptr;
+    bool failed = false;
     for (unsigned i = 0; i < N; ++i)
     {
         int status;
         char* demang = __cxxabiv1::__cxa_demangle(cases[i][0], buf, &len, &status);
         if (demang == 0 || std::strcmp(demang, cases[i][1]) != 0)
         {
-            std::cout << cases[i][0] << " -> " << cases[i][1] << '\n';
+            std::cout << "ERROR demangling " << cases[i][0] << '\n'
+                      << "expected: " << cases[i][1] << std::endl;
             if (demang)
             {
-                std::cout << "Got instead: " << demang << '\n';
-                assert(std::strcmp(demang, cases[i][1]) == 0);
+                std::cout << " reality: " << demang << '\n' << std::endl;
+                buf = demang;
+                failed = true;
             }
             else
             {
                 std::cout << "Got instead: NULL, " << status << '\n';
-                assert(demang != 0);
+                failed = true;
             }
         }
         else
@@ -29820,6 +29844,7 @@
             buf = demang;
         }
     }
+    assert(!failed);
     free(buf);
 }
 
@@ -29845,8 +29870,6 @@
 }
 
 const char *xfail_cases[] = {
-    "_Z1fUa9enable_ifIXLi1EEEv", // enable_if attribute
-    "_ZDC2a12a2E", // decomposition decl
     "_ZW6FooBarE2f3v", // C++ modules TS
 
     // FIXME: Why does clang generate the "cp" expr?