[libcxxabi] r328464 - [demangler] Use a back-patching scheme to resolve forward references.

Erik Pilkington via cfe-commits cfe-commits at lists.llvm.org
Sun Mar 25 15:50:33 PDT 2018


Author: epilk
Date: Sun Mar 25 15:50:33 2018
New Revision: 328464

URL: http://llvm.org/viewvc/llvm-project?rev=328464&view=rev
Log:
[demangler] Use a back-patching scheme to resolve forward references.

Strictly in a conversion operator's type, a <template-param> refers to a
<template-arg> that is further ahead in the mangled name. Instead of
doing a second parse to resolve these, introduce a
ForwardTemplateReference Node and back-patch the referenced
<template-arg> when we're in the right context.

This is also a correctness fix, previously we would only do a second
parse if the <template-param> was out of bounds in the current set of
<template-args>. This lead to misdemangles (gasp!) when the conversion
operator was a member of a templated struct, for instance.

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=328464&r1=328463&r2=328464&view=diff
==============================================================================
--- libcxxabi/trunk/src/cxa_demangle.cpp (original)
+++ libcxxabi/trunk/src/cxa_demangle.cpp Sun Mar 25 15:50:33 2018
@@ -190,6 +190,7 @@ public:
     KTemplateArgumentPack,
     KParameterPackExpansion,
     KTemplateArgs,
+    KForwardTemplateReference,
     KNameWithTemplateArgs,
     KGlobalQualifiedName,
     KStdQualifiedName,
@@ -1091,6 +1092,29 @@ public:
   }
 };
 
+struct ForwardTemplateReference : Node {
+  size_t Index;
+  Node *Ref = nullptr;
+
+  ForwardTemplateReference(size_t Index_)
+      : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown,
+             Cache::Unknown),
+        Index(Index_) {}
+
+  bool hasRHSComponentSlow(OutputStream &S) const override {
+    return Ref->hasRHSComponent(S);
+  }
+  bool hasArraySlow(OutputStream &S) const override {
+    return Ref->hasArray(S);
+  }
+  bool hasFunctionSlow(OutputStream &S) const override {
+    return Ref->hasFunction(S);
+  }
+
+  void printLeft(OutputStream &S) const override { Ref->printLeft(S); }
+  void printRight(OutputStream &S) const override { Ref->printRight(S); }
+};
+
 class NameWithTemplateArgs final : public Node {
   // name<template_args>
   Node *Name;
@@ -1924,10 +1948,12 @@ struct Db {
   // 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;
@@ -1989,7 +2015,7 @@ struct Db {
   bool parseSeqId(size_t *Out);
   Node *parseSubstitution();
   Node *parseTemplateParam();
-  Node *parseTemplateArgs();
+  Node *parseTemplateArgs(bool TagTemplates = false);
   Node *parseTemplateArg();
 
   /// Parse the <expr> production.
@@ -2025,8 +2051,25 @@ struct Db {
     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);
@@ -2075,7 +2118,7 @@ Node *Db::parseName(NameState *State) {
       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;
@@ -2088,7 +2131,7 @@ Node *Db::parseName(NameState *State) {
   //        ::= <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;
@@ -2302,9 +2345,15 @@ Node *Db::parseOperatorName(NameState *S
       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;
@@ -2528,7 +2577,7 @@ Node *Db::parseCtorDtorName(Node *&SoFar
     ++First;
     if (State) State->CtorDtorConversion = true;
     if (IsInherited) {
-      if (parseName() == nullptr)
+      if (parseName(State) == nullptr)
         return nullptr;
     }
     return make<CtorDtorName>(SoFar, false);
@@ -2598,7 +2647,7 @@ Node *Db::parseNestedName(NameState *Sta
 
     //          ::= <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);
@@ -4369,15 +4418,6 @@ Node *Db::parseSpecialName() {
 //            ::= <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();
 
@@ -4388,12 +4428,16 @@ Node *Db::parseEncoding() {
     return numLeft() == 0 || look() == 'E' || look() == '.' || look() == '_';
   };
 
-  NameState NameInfo;
+  NameState NameInfo(this);
   Node *Name = parseName(&NameInfo);
-  if (Name == nullptr || IsEndOfEncoding())
-    return Name;
+  if (Name == nullptr)
+    return nullptr;
 
-  TagTemplates = false;
+  if (resolveForwardTemplateRefs(NameInfo))
+    return nullptr;
+
+  if (IsEndOfEncoding())
+    return Name;
 
   Node *Attrs = nullptr;
   if (consumeIf("Ua9enable_ifI")) {
@@ -4601,10 +4645,16 @@ Node *Db::parseTemplateParam() {
   if (ParsingLambdaParams)
     return make<NameType>("auto");
 
-  if (Index >= TemplateParams.size()) {
-    FixForwardReferences = true;
-    return make<NameType>("FORWARD_REFERENCE");
+  // 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];
 }
 
@@ -4653,7 +4703,7 @@ Node *Db::parseTemplateArg() {
 
 // <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;
 
@@ -4790,20 +4840,9 @@ __cxa_demangle(const char *MangledName,
   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) {
+    assert(Parser.ForwardTemplateRefs.empty());
+
     if (Buf == nullptr) {
       BufSize = 1024;
       Buf = static_cast<char*>(std::malloc(BufSize));

Modified: libcxxabi/trunk/test/test_demangle.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/test_demangle.pass.cpp?rev=328464&r1=328463&r2=328464&view=diff
==============================================================================
--- libcxxabi/trunk/test/test_demangle.pass.cpp (original)
+++ libcxxabi/trunk/test/test_demangle.pass.cpp Sun Mar 25 15:50:33 2018
@@ -29732,6 +29732,12 @@ const char* cases[][2] =
     {"_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]);
@@ -29799,11 +29805,11 @@ const char* invalid_cases[] =
     "Z1 Z1 IJEEAcvZcvT_EcvT_T_",
     "T_IZaaIJEEAnaaaT_T__",
     "PT_IJPNT_IJEET_T_T_T_)J)JKE",
-//  "1 IJEVNT_T_T_EE",
+    "1 IJEVNT_T_T_EE",
     "AT__ZSiIJEEAnwscT_T__",
     "FSiIJEENT_IoE ",
     "ZTVSiIZTVSiIZTVSiIZTVSiINIJEET_T_T_T_T_ ",
-//  "Ana_T_E_T_IJEffffffffffffffersfffffrsrsffffffbgE",
+    "Ana_T_E_T_IJEffffffffffffffersfffffrsrsffffffbgE",
 };
 
 const unsigned NI = sizeof(invalid_cases) / sizeof(invalid_cases[0]);




More information about the cfe-commits mailing list