diff mbox series

[demangler] Fix nested generic lambda

Message ID 8412b33a-6149-3359-179c-d1fe03cbd47e@acm.org
State New
Headers show
Series [demangler] Fix nested generic lambda | expand

Commit Message

Nathan Sidwell Sept. 15, 2017, 12:04 p.m. UTC
This patch fixes PR82195, which turned out to be a demangler bug -- the 
specification and the compiler were DTRT.

The originating source contained a non-generic lambda within a generic 
lambda.  Because we had an older GDB without recursion protection in its 
demangler, GDB died, and that kind of makes debugging difficult.

I did reduce the testcase further to:

void Foo () {
   // Foo()::<lambda(auto:1)> [with auto:1 = int]
   // _ZZ3FoovENKUlT_E_clIiEEfS_
   [](auto) ->float {
     struct Local {
       // void Foo()::<lambda(auto:1)>::Local::fn() [with auto:1 = int]
       // _ZZZ3FoovENKUlT_E_clIiEEfS_EN5Local2fnEv
       static void fn () {}
     };
     Local::fn ();
     return 0.0f;
   } (0);
}

The salient point is that we have to mangle names within the local 
context of an instantiation of a lambda's templated operator ().  The 
demangler was not expecting a local name:
    Z <context> E <local-entity> [discriminator]
to have a function as a local-entity.  This wasn't noticed because it 
still managed to parse the trailing parameter types in the context of 
d_local_name's caller, as there usually is no discriminator.  But those 
parameters got attached to the wrong level of the demangle tree.

When those parameters contain template parameters that refer to the 
instantiation parameters of the local entity, it all goes wrong.  (The 
instantiation parameters get correctly attached to the local-entity).

After parsing a local entity name we peek at the next character, and if 
it's not NUL, 'E' (end of containing scope) or '_' (discriminator) we 
parse function parameters before closing out the local name.

A complication is that we need to know if we're the outermost local 
name, and not do it in that case.  Otherwise we end up gluing the 
parameters too deeply and consequently --no-param demangles break 
(you'll see there's similar funky code in d_encoding).  Luckily in such 
cases we cannot meet the templated case.

Applying to trunk.

Pedro, would you like me to port to gdb's libiberty, or will you do a 
merge in the near future?

nathan

Comments

Pedro Alves Sept. 15, 2017, 4:15 p.m. UTC | #1
On 09/15/2017 01:04 PM, Nathan Sidwell wrote:
> 
> 
> Pedro, would you like me to port to gdb's libiberty, or will you do a
> merge in the near future?

I wasn't planning to, but I'm doing it now.

Thanks much for the fix!
Pedro Alves Sept. 15, 2017, 4:52 p.m. UTC | #2
On 09/15/2017 05:15 PM, Pedro Alves wrote:
> On 09/15/2017 01:04 PM, Nathan Sidwell wrote:
>>
>>
>> Pedro, would you like me to port to gdb's libiberty, or will you do a
>> merge in the near future?
> 
> I wasn't planning to, but I'm doing it now.
> 

Now done:
 https://sourceware.org/ml/gdb-patches/2017-09/msg00421.html

> Thanks much for the fix!

Thanks,
Pedro Alves
diff mbox series

Patch

2017-09-15  Nathan Sidwell  <nathan@acm.org>

	PR demangle/82195
	* cp-demangle.c (d_name): Add 'toplevel' parm.  Pass to	...
	(d_local_name): ... here.  Parse trailing function args on nested
	local_name.
	(d_encoding, d_special_name, d_class_enum_type): Adjust d_name calls.
	* testsuite/demangle-expected: Add tests.

Index: cp-demangle.c
===================================================================
--- cp-demangle.c	(revision 252802)
+++ cp-demangle.c	(working copy)
@@ -425,7 +425,7 @@  is_ctor_dtor_or_conversion (struct deman
 
 static struct demangle_component *d_encoding (struct d_info *, int);
 
-static struct demangle_component *d_name (struct d_info *);
+static struct demangle_component *d_name (struct d_info *, int);
 
 static struct demangle_component *d_nested_name (struct d_info *);
 
@@ -484,7 +484,7 @@  static struct demangle_component *d_expr
 
 static struct demangle_component *d_expr_primary (struct d_info *);
 
-static struct demangle_component *d_local_name (struct d_info *);
+static struct demangle_component *d_local_name (struct d_info *, int);
 
 static int d_discriminator (struct d_info *);
 
@@ -1308,7 +1308,7 @@  d_encoding (struct d_info *di, int top_l
     {
       struct demangle_component *dc, *dcr;
 
-      dc = d_name (di);
+      dc = d_name (di, top_level);
 
       if (dc != NULL && top_level && (di->options & DMGL_PARAMS) == 0)
 	{
@@ -1383,7 +1383,7 @@  d_abi_tags (struct d_info *di, struct de
 */
 
 static struct demangle_component *
-d_name (struct d_info *di)
+d_name (struct d_info *di, int top_level)
 {
   char peek = d_peek_char (di);
   struct demangle_component *dc;
@@ -1394,7 +1394,7 @@  d_name (struct d_info *di)
       return d_nested_name (di);
 
     case 'Z':
-      return d_local_name (di);
+      return d_local_name (di, top_level);
 
     case 'U':
       return d_unqualified_name (di);
@@ -2079,11 +2079,11 @@  d_special_name (struct d_info *di)
 
 	case 'H':
 	  return d_make_comp (di, DEMANGLE_COMPONENT_TLS_INIT,
-			      d_name (di), NULL);
+			      d_name (di, 0), NULL);
 
 	case 'W':
 	  return d_make_comp (di, DEMANGLE_COMPONENT_TLS_WRAPPER,
-			      d_name (di), NULL);
+			      d_name (di, 0), NULL);
 
 	default:
 	  return NULL;
@@ -2094,11 +2094,12 @@  d_special_name (struct d_info *di)
       switch (d_next_char (di))
 	{
 	case 'V':
-	  return d_make_comp (di, DEMANGLE_COMPONENT_GUARD, d_name (di), NULL);
+	  return d_make_comp (di, DEMANGLE_COMPONENT_GUARD,
+			      d_name (di, 0), NULL);
 
 	case 'R':
 	  {
-	    struct demangle_component *name = d_name (di);
+	    struct demangle_component *name = d_name (di, 0);
 	    return d_make_comp (di, DEMANGLE_COMPONENT_REFTEMP, name,
 				d_number_component (di));
 	  }
@@ -2934,7 +2935,7 @@  d_bare_function_type (struct d_info *di,
 static struct demangle_component *
 d_class_enum_type (struct d_info *di)
 {
-  return d_name (di);
+  return d_name (di, 0);
 }
 
 /* <array-type> ::= A <(positive dimension) number> _ <(element) type>
@@ -3567,7 +3568,7 @@  d_expr_primary (struct d_info *di)
 */
 
 static struct demangle_component *
-d_local_name (struct d_info *di)
+d_local_name (struct d_info *di, int top_level)
 {
   struct demangle_component *function;
   struct demangle_component *name;
@@ -3600,14 +3601,30 @@  d_local_name (struct d_info *di)
 	    return NULL;
 	}
 
-      name = d_name (di);
+      name = d_name (di, 0);
+
       if (name
-	  /* Lambdas and unnamed types have internal discriminators.  */
+	  /* Lambdas and unnamed types have internal discriminators
+	     and are not functions.  */
 	  && name->type != DEMANGLE_COMPONENT_LAMBDA
-	  && name->type != DEMANGLE_COMPONENT_UNNAMED_TYPE
-	  /* Otherwise read and ignore an optional discriminator.  */
-	  && ! d_discriminator (di))
-	return NULL;
+	  && name->type != DEMANGLE_COMPONENT_UNNAMED_TYPE)
+	{
+	  if (!top_level
+	      && d_peek_char (di) != 0 /* Not end of string.  */
+	      && d_peek_char (di) != 'E' /* Not end of nested encoding.  */
+	      && d_peek_char (di) != '_') /* Not discriminator.  */
+	    {
+	      struct demangle_component *args;
+
+	      args = d_bare_function_type (di, has_return_type (name));
+	      name = d_make_comp (di, DEMANGLE_COMPONENT_TYPED_NAME,
+				  name, args);
+	    }
+
+	  /* Read and ignore an optional discriminator.  */
+	  if (! d_discriminator (di))
+	    return NULL;
+	}
 
       if (num >= 0)
 	name = d_make_default_arg (di, num, name);
Index: testsuite/demangle-expected
===================================================================
--- testsuite/demangle-expected	(revision 252802)
+++ testsuite/demangle-expected	(working copy)
@@ -4736,3 +4736,17 @@  __thunk_16a_$_1x
 __thunk_4294967297__$_1x
 __thunk_4294967297__$_1x
 #
+# demangler/82195 members of lambdas
+--no-params
+_ZZZ3FoovENKUlT_E_clIiEEfS_EN5Local2fnEv
+Foo()::float {lambda(auto:1)#1}::operator()<int>(int) const::Local::fn()
+Foo()::float {lambda(auto:1)#1}::operator()<int>(int) const::Local::fn
+--no-params
+_Z7CaptureIZZ3FoovENKUlT_E_clIiEEvS0_EUlvE_EvOS0_
+void Capture<Foo()::void {lambda(auto:1)#1}::operator()<int>(int) const::{lambda()#1}>(Foo()::void {lambda(auto:1)#1}::operator()<int>(int) const::{lambda()#1}&&)
+Capture<Foo()::void {lambda(auto:1)#1}::operator()<int>(int) const::{lambda()#1}>
+--no-params
+_Z4FrobIZZ3FoovENKUlT_E_clIiEEvS0_EUlvE_Evv
+void Frob<Foo()::void {lambda(auto:1)#1}::operator()<int>(int) const::{lambda()#1}>()
+Frob<Foo()::void {lambda(auto:1)#1}::operator()<int>(int) const::{lambda()#1}>
+#