[pushed] c++: Fix demangling of <unresolved-name>

Jason Merrill jason@redhat.com
Tue Dec 22 02:16:06 GMT 2020


The ABI for unresolved scoped names on the RHS of . and -> used to be

  sr <type> <unqualified-id>

That changed years ago to something more complex, but G++ was never updated.
This change was particularly incompatible for simple qualified-ids like
A::x, which were previously mangled as sr1A1x, and now sr1AE1x.

This obviously makes life hard for demanglers, which can't know whether to
consume that E or not.  To work around this, we now try demangling with the
newer ABI, and if that fails and we saw an "sr", try again with the older
ABI.

The compiler side will be adjusted later, as there's an open ABI issue in this
area.

libiberty/ChangeLog:

	PR c++/67343
	* cp-demangle.h (struct d_info): Add unresolved_name_state.
	* cp-demangle.c (d_prefix): Add subst parm.
	(d_nested_name): Pass it.
	(d_unresolved_name): Split out from...
	(d_expression_1): ...here.
	(d_demangle_callback): Maybe retry with old sr mangling.
	* testsuite/demangle-expected: Add test.
---
 libiberty/cp-demangle.h               |  4 ++
 libiberty/cp-demangle.c               | 87 ++++++++++++++++++++-------
 libiberty/testsuite/demangle-expected |  3 +
 3 files changed, 72 insertions(+), 22 deletions(-)

diff --git a/libiberty/cp-demangle.h b/libiberty/cp-demangle.h
index 943a3ef478e..27014cde258 100644
--- a/libiberty/cp-demangle.h
+++ b/libiberty/cp-demangle.h
@@ -122,6 +122,10 @@ struct d_info
   /* Non-zero if we are parsing the type operand of a conversion
      operator, but not when in an expression.  */
   int is_conversion;
+  /*  1: using new unresolved-name grammar.
+     -1: using new unresolved-name grammar and saw an unresolved-name.
+      0: using old unresolved-name grammar.  */
+  int unresolved_name_state;
   /* If DMGL_NO_RECURSE_LIMIT is not active then this is set to
      the current recursion level.  */
   unsigned int recursion_level;
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 98ab47a3460..52427275f44 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -429,7 +429,7 @@ static struct demangle_component *d_name (struct d_info *);
 
 static struct demangle_component *d_nested_name (struct d_info *);
 
-static struct demangle_component *d_prefix (struct d_info *);
+static struct demangle_component *d_prefix (struct d_info *, int);
 
 static struct demangle_component *d_unqualified_name (struct d_info *);
 
@@ -1510,7 +1510,7 @@ d_nested_name (struct d_info *di)
      once we have something to attach it to.  */
   rqual = d_ref_qualifier (di, NULL);
 
-  *pret = d_prefix (di);
+  *pret = d_prefix (di, 1);
   if (*pret == NULL)
     return NULL;
 
@@ -1536,10 +1536,12 @@ d_nested_name (struct d_info *di)
    <template-prefix> ::= <prefix> <(template) unqualified-name>
                      ::= <template-param>
                      ::= <substitution>
-*/
+
+   SUBST is true if we should add substitutions (as normal), false
+   if not (in an unresolved-name).  */
 
 static struct demangle_component *
-d_prefix (struct d_info *di)
+d_prefix (struct d_info *di, int subst)
 {
   struct demangle_component *ret = NULL;
 
@@ -1605,7 +1607,7 @@ d_prefix (struct d_info *di)
       else
 	ret = d_make_comp (di, comb_type, ret, dc);
 
-      if (peek != 'S' && d_peek_char (di) != 'E')
+      if (peek != 'S' && d_peek_char (di) != 'E' && subst)
 	{
 	  if (! d_add_substitution (di, ret))
 	    return NULL;
@@ -3291,14 +3293,58 @@ op_is_new_cast (struct demangle_component *op)
 	      || code[0] == 'c' || code[0] == 'r'));
 }
 
+/*   <unresolved-name> ::= [gs] <base-unresolved-name> # x or (with "gs") ::x
+       ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x
+       # T::N::x /decltype(p)::N::x
+       ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
+       # A::x, N::y, A<T>::z; "gs" means leading "::"
+       ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
+
+     "gs" is handled elsewhere, as a unary operator.  */
+
+static struct demangle_component *
+d_unresolved_name (struct d_info *di)
+{
+  struct demangle_component *type;
+  struct demangle_component *name;
+  char peek;
+
+  /* Consume the "sr".  */
+  d_advance (di, 2);
+
+  peek = d_peek_char (di);
+  if (di->unresolved_name_state
+      && (IS_DIGIT (peek)
+	  || IS_LOWER (peek)
+	  || peek == 'C'
+	  || peek == 'U'
+	  || peek == 'L'))
+    {
+      /* The third production is ambiguous with the old unresolved-name syntax
+	 of <type> <base-unresolved-name>; in the old mangling, A::x was mangled
+	 as sr1A1x, now sr1AE1x.  So we first try to demangle using the new
+	 mangling, then with the old if that fails.  */
+      di->unresolved_name_state = -1;
+      type = d_prefix (di, 0);
+      if (d_peek_char (di) == 'E')
+	d_advance (di, 1);
+    }
+  else
+    type = cplus_demangle_type (di);
+  name = d_unqualified_name (di);
+  if (d_peek_char (di) == 'I')
+    name = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, name,
+			d_template_args (di));
+  return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type, name);
+}
+
 /* <expression> ::= <(unary) operator-name> <expression>
                 ::= <(binary) operator-name> <expression> <expression>
                 ::= <(trinary) operator-name> <expression> <expression> <expression>
 		::= cl <expression>+ E
                 ::= st <type>
                 ::= <template-param>
-                ::= sr <type> <unqualified-name>
-                ::= sr <type> <unqualified-name> <template-args>
+		::= <unresolved-name>
                 ::= <expr-primary>
 
   <braced-expression> ::= <expression>
@@ -3308,7 +3354,7 @@ op_is_new_cast (struct demangle_component *op)
 									# [expr ... expr] = expr
 */
 
-static inline struct demangle_component *
+static struct demangle_component *
 d_expression_1 (struct d_info *di)
 {
   char peek;
@@ -3319,20 +3365,7 @@ d_expression_1 (struct d_info *di)
   else if (peek == 'T')
     return d_template_param (di);
   else if (peek == 's' && d_peek_next_char (di) == 'r')
-    {
-      struct demangle_component *type;
-      struct demangle_component *name;
-
-      d_advance (di, 2);
-      type = cplus_demangle_type (di);
-      name = d_unqualified_name (di);
-      if (d_peek_char (di) != 'I')
-	return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type, name);
-      else
-	return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type,
-			    d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, name,
-					 d_template_args (di)));
-    }
+    return d_unresolved_name (di);
   else if (peek == 's' && d_peek_next_char (di) == 'p')
     {
       d_advance (di, 2);
@@ -6397,6 +6430,9 @@ d_demangle_callback (const char *mangled, int options,
       type = DCT_TYPE;
     }
 
+  di.unresolved_name_state = 1;
+
+ again:
   cplus_demangle_init_info (mangled, options, strlen (mangled), &di);
 
   /* PR 87675 - Check for a mangled string that is so long
@@ -6455,6 +6491,13 @@ d_demangle_callback (const char *mangled, int options,
     if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0')
       dc = NULL;
 
+    /* See discussion in d_unresolved_name.  */
+    if (dc == NULL && di.unresolved_name_state == -1)
+      {
+	di.unresolved_name_state = 0;
+	goto again;
+      }
+
 #ifdef CP_DEMANGLE_DEBUG
     d_dump (dc, 0);
 #endif
diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected
index 6789d0d1d9d..e6b5b64b9a9 100644
--- a/libiberty/testsuite/demangle-expected
+++ b/libiberty/testsuite/demangle-expected
@@ -1485,3 +1485,6 @@ decltype (({parm#1}.(operator A*))()) j<A>(A)
 
 _Z1fI1AEDtdtfp_srT_1xES1_
 decltype ({parm#1}.A::x) f<A>(A)
+
+_Z2f6IP1AEDtptfp_gssr1A1BE1xET_
+decltype ({parm#1}->(::A::B::x)) f6<A*>(A*)

base-commit: 93ac0c05ffc84acba8e73ed5238fc325044378e0
-- 
2.27.0



More information about the Gcc-patches mailing list