diff mbox

PR 59195: C++ demangler handles conversion operator incorrectly

Message ID 20131119190908.D054D16095E@ccoutant.mtv.corp.google.com
State New
Headers show

Commit Message

Cary Coutant Nov. 19, 2013, 7:09 p.m. UTC
In PR 59195, I describe a demangler problem with parsing
conversion operators that have template parameters:

  http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59195

For example,

  $ c++filt _ZN1AcvPT_I1CEEv

fails -- it should print "A::operator C*<C>()".

  $ c++filt _ZN1AcvT_IiEI1CEEv

prints "A::operator int<int><C>()" instead of
"A::operator C<int><C>()".

This patch fixes the parser to resolve the ambiguity in the mangled
name grammar where it can't tell if the "T_" is a <template-param>
or is the start of <template-template-param> <template-args>, and
fixes the printer to use the correct context when resolving the
template parameter substitution.

Passes all regression tests, and I've added new test cases.

OK to commit?

-cary


2013-11-19  Cary Coutant  <ccoutant@google.com>

libiberty/
        PR other/59195
	* cp-demangle.c (struct d_info_checkpoint): New struct.
	(struct d_print_info): Add current_template field.
        (d_operator_name): Set flag when processing a conversion
        operator.
        (cplus_demangle_type): When processing <template-args> for
        a conversion operator, backtrack if necessary.
        (d_expression_1): Renamed from d_expression.
        (d_expression): New wrapper around d_expression_1.
        (d_checkpoint): New function.
        (d_backtrack): New function.
        (d_print_init): Initialize current_template.
        (d_print_comp): Set current_template.
        (d_print_cast): Put current_template in scope for
        printing conversion operator name.
	(cplus_demangle_init_info): Initialize is_expression and
        is_conversion.
	* cp-demangle.h (struct d_info): Add is_expression and
        is_conversion fields.
	* testsuite/demangle-expected: New test cases.
diff mbox

Patch

diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 7be9804..edfd85c 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -287,6 +287,19 @@  struct d_saved_scope
   struct d_print_template *templates;
 };
 
+/* Checkpoint structure to allow backtracking.  This holds copies
+   of the fields of struct d_info that need to be restored
+   if a trial parse needs to be backtracked over.  */
+
+struct d_info_checkpoint
+{
+  const char *n;
+  int next_comp;
+  int next_sub;
+  int did_subs;
+  int expansion;
+};
+
 enum { D_PRINT_BUFFER_LENGTH = 256 };
 struct d_print_info
 {
@@ -318,6 +331,8 @@  struct d_print_info
   struct d_saved_scope *saved_scopes;
   /* Number of saved scopes in the above array.  */
   int num_saved_scopes;
+  /* The nearest enclosing template, if any.  */
+  const struct demangle_component *current_template;
 };
 
 #ifdef CP_DEMANGLE_DEBUG
@@ -444,6 +459,10 @@  d_add_substitution (struct d_info *, struct demangle_component *);
 
 static struct demangle_component *d_substitution (struct d_info *, int);
 
+static void d_checkpoint (struct d_info *, struct d_info_checkpoint *);
+
+static void d_backtrack (struct d_info *, struct d_info_checkpoint *);
+
 static void d_growable_string_init (struct d_growable_string *, size_t);
 
 static inline void
@@ -1734,8 +1753,15 @@  d_operator_name (struct d_info *di)
   if (c1 == 'v' && IS_DIGIT (c2))
     return d_make_extended_operator (di, c2 - '0', d_source_name (di));
   else if (c1 == 'c' && c2 == 'v')
-    return d_make_comp (di, DEMANGLE_COMPONENT_CAST,
-			cplus_demangle_type (di), NULL);
+    {
+      struct demangle_component *type;
+
+      if (!di->is_expression)
+        di->is_conversion = 1;
+      type = cplus_demangle_type (di);
+      di->is_conversion = 0;
+      return d_make_comp (di, DEMANGLE_COMPONENT_CAST, type, NULL);
+    }
   else
     {
       /* LOW is the inclusive lower bound.  */
@@ -2284,13 +2310,61 @@  cplus_demangle_type (struct d_info *di)
       ret = d_template_param (di);
       if (d_peek_char (di) == 'I')
 	{
-	  /* This is <template-template-param> <template-args>.  The
-	     <template-template-param> part is a substitution
+	  /* This may be <template-template-param> <template-args>.
+	     If this is the type for a conversion operator, we can
+	     have a <template-template-param> here only by following
+	     a derivation like this:
+
+	     <nested-name>
+	     -> <template-prefix> <template-args>
+	     -> <prefix> <template-unqualified-name> <template-args>
+	     -> <unqualified-name> <template-unqualified-name> <template-args>
+	     -> <source-name> <template-unqualified-name> <template-args>
+	     -> <source-name> <operator-name> <template-args>
+	     -> <source-name> cv <type> <template-args>
+	     -> <source-name> cv <template-template-param> <template-args> <template-args>
+
+	     where the <template-args> is followed by another.
+	     Otherwise, we must have a derivation like this:
+
+	     <nested-name>
+	     -> <template-prefix> <template-args>
+	     -> <prefix> <template-unqualified-name> <template-args>
+	     -> <unqualified-name> <template-unqualified-name> <template-args>
+	     -> <source-name> <template-unqualified-name> <template-args>
+	     -> <source-name> <operator-name> <template-args>
+	     -> <source-name> cv <type> <template-args>
+	     -> <source-name> cv <template-param> <template-args>
+
+	     where we need to leave the <template-args> to be processed
+	     by d_prefix (following the <template-prefix>).
+
+	     The <template-template-param> part is a substitution
 	     candidate.  */
-	  if (! d_add_substitution (di, ret))
-	    return NULL;
-	  ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret,
-			     d_template_args (di));
+	  if (! di->is_conversion)
+	    {
+	      if (! d_add_substitution (di, ret))
+		return NULL;
+	      ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret,
+				 d_template_args (di));
+	    }
+	  else
+	    {
+	      struct demangle_component *args;
+	      struct d_info_checkpoint checkpoint;
+
+	      d_checkpoint (di, &checkpoint);
+	      args = d_template_args (di);
+	      if (d_peek_char (di) == 'I')
+		{
+		  if (! d_add_substitution (di, ret))
+		    return NULL;
+		  ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret,
+				     args);
+		}
+	      else
+		d_backtrack (di, &checkpoint);
+	    }
 	}
       break;
 
@@ -2976,8 +3050,8 @@  op_is_new_cast (struct demangle_component *op)
                 ::= <expr-primary>
 */
 
-static struct demangle_component *
-d_expression (struct d_info *di)
+static inline struct demangle_component *
+d_expression_1 (struct d_info *di)
 {
   char peek;
 
@@ -3203,6 +3277,17 @@  d_expression (struct d_info *di)
     }
 }
 
+static struct demangle_component *
+d_expression (struct d_info *di)
+{
+  struct demangle_component *ret;
+
+  di->is_expression = 1;
+  ret = d_expression_1 (di);
+  di->is_expression = 0;
+  return ret;
+}
+
 /* <expr-primary> ::= L <type> <(value) number> E
                   ::= L <type> <(value) float> E
                   ::= L <mangled-name> E
@@ -3588,6 +3673,26 @@  d_substitution (struct d_info *di, int prefix)
     }
 }
 
+static void
+d_checkpoint (struct d_info *di, struct d_info_checkpoint *checkpoint)
+{
+  checkpoint->n = di->n;
+  checkpoint->next_comp = di->next_comp;
+  checkpoint->next_sub = di->next_sub;
+  checkpoint->did_subs = di->did_subs;
+  checkpoint->expansion = di->expansion;
+}
+
+static void
+d_backtrack (struct d_info *di, struct d_info_checkpoint *checkpoint)
+{
+  di->n = checkpoint->n;
+  di->next_comp = checkpoint->next_comp;
+  di->next_sub = checkpoint->next_sub;
+  di->did_subs = checkpoint->did_subs;
+  di->expansion = checkpoint->expansion;
+}
+
 /* Initialize a growable string.  */
 
 static void
@@ -3684,6 +3789,7 @@  d_print_init (struct d_print_info *dpi, demangle_callbackref callback,
 
   dpi->saved_scopes = NULL;
   dpi->num_saved_scopes = 0;
+  dpi->current_template = NULL;
 }
 
 /* Free a print information structure.  */
@@ -4165,6 +4271,12 @@  d_print_comp (struct d_print_info *dpi, int options,
       {
 	struct d_print_mod *hold_dpm;
 	struct demangle_component *dcl;
+	const struct demangle_component *hold_current;
+
+	/* This template may need to be referenced by a cast operator
+	   contained in its subtree.  */
+	hold_current = dpi->current_template;
+	dpi->current_template = dc;
 
 	/* Don't push modifiers into a template definition.  Doing so
 	   could give the wrong definition for a template argument.
@@ -4201,6 +4313,7 @@  d_print_comp (struct d_print_info *dpi, int options,
           }
 
 	dpi->modifiers = hold_dpm;
+	dpi->current_template = hold_current;
 
 	return;
       }
@@ -5416,28 +5529,32 @@  static void
 d_print_cast (struct d_print_info *dpi, int options,
               const struct demangle_component *dc)
 {
-  if (d_left (dc)->type != DEMANGLE_COMPONENT_TEMPLATE)
-    d_print_comp (dpi, options, d_left (dc));
-  else
-    {
-      struct d_print_mod *hold_dpm;
-      struct d_print_template dpt;
-
-      /* It appears that for a templated cast operator, we need to put
-	 the template parameters in scope for the operator name, but
-	 not for the parameters.  The effect is that we need to handle
-	 the template printing here.  */
-
-      hold_dpm = dpi->modifiers;
-      dpi->modifiers = NULL;
+  struct d_print_template dpt;
 
+  /* For a cast operator, we need the template parameters from
+     the enclosing template in scope for processing the type.  */
+  if (dpi->current_template != NULL)
+    {
       dpt.next = dpi->templates;
       dpi->templates = &dpt;
-      dpt.template_decl = d_left (dc);
+      dpt.template_decl = dpi->current_template;
+    }
 
+  if (d_left (dc)->type != DEMANGLE_COMPONENT_TEMPLATE)
+    {
+      d_print_comp (dpi, options, d_left (dc));
+      if (dpi->current_template != NULL)
+	dpi->templates = dpt.next;
+    }
+  else
+    {
       d_print_comp (dpi, options, d_left (d_left (dc)));
 
-      dpi->templates = dpt.next;
+      /* For a templated cast operator, we need to remove the template
+	 parameters from scope after printing the operator name,
+	 so we need to handle the template printing here.  */
+      if (dpi->current_template != NULL)
+	dpi->templates = dpt.next;
 
       if (d_last_char (dpi) == '<')
 	d_append_char (dpi, ' ');
@@ -5448,8 +5565,6 @@  d_print_cast (struct d_print_info *dpi, int options,
       if (d_last_char (dpi) == '>')
 	d_append_char (dpi, ' ');
       d_append_char (dpi, '>');
-
-      dpi->modifiers = hold_dpm;
     }
 }
 
@@ -5482,6 +5597,8 @@  cplus_demangle_init_info (const char *mangled, int options, size_t len,
   di->last_name = NULL;
 
   di->expansion = 0;
+  di->is_expression = 0;
+  di->is_conversion = 0;
 }
 
 /* Internal implementation for the demangler.  If MANGLED is a g++ v3 ABI
diff --git a/libiberty/cp-demangle.h b/libiberty/cp-demangle.h
index ae635be..6fce025 100644
--- a/libiberty/cp-demangle.h
+++ b/libiberty/cp-demangle.h
@@ -122,6 +122,11 @@  struct d_info
      mangled name to the demangled name, such as standard
      substitutions and builtin types.  */
   int expansion;
+  /* Non-zero if we are parsing an expression.  */
+  int is_expression;
+  /* Non-zero if we are parsing the type operand of a conversion
+     operator, but not when in an expression.  */
+  int is_conversion;
 };
 
 /* To avoid running past the ending '\0', don't:
diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected
index de03bfa..3ff08e6 100644
--- a/libiberty/testsuite/demangle-expected
+++ b/libiberty/testsuite/demangle-expected
@@ -4304,6 +4304,16 @@  strings::internal::Splitter<strings::delimiter::AnyOf, strings::SkipEmpty>::oper
 strings::internal::Splitter<strings::delimiter::AnyOf, strings::SkipEmpty>::operator std::vector<basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<basic_string<char, std::char_traits<char>, std::allocator<char> > > ><std::vector<basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<basic_string<char, std::char_traits<char>, std::allocator<char> > > >, void>
 #
 --format=gnu-v3 --no-params
-_ZN1AIiEcvPT_I1CEEv
-A<int>::operator C<C>*()
-A<int>::operator C<C>*
+_ZN1AcvT_I1CEEv
+A::operator C<C>()
+A::operator C<C>
+#
+--format=gnu-v3 --no-params
+_ZN1AcvPT_I1CEEv
+A::operator C*<C>()
+A::operator C*<C>
+#
+--format=gnu-v3 --no-params
+_ZN1AcvT_IiEI1CEEv
+A::operator C<int><C>()
+A::operator C<int><C>