diff mbox

[v2,2/2] Sync libiberty with gcc upstream

Message ID CABOHX+eBCz2o0oaTmcUjgRgswhvues4H1exnPPGvGyJQTPV+cw@mail.gmail.com
State New
Headers show

Commit Message

Iain Buclaw June 19, 2015, 3:24 p.m. UTC
Second downstream patch with dlang demangling related changes only.
Should be a more straightforward read than the other one.

Regards
Iain

---

Comments

Pedro Alves June 29, 2015, 3:50 p.m. UTC | #1
On 06/19/2015 04:24 PM, Iain Buclaw wrote:
> Second downstream patch with dlang demangling related changes only.
> Should be a more straightforward read than the other one.

Hi Iain, seems like this unfortunately caused regressions in
gdb.dlang/demangle.exp.  See replies to:

  https://sourceware.org/ml/gdb-testers/2015-q2/msg07341.html

I see the same here, e.g., on x86_64 F20:

-PASS: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
+FAIL: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv

gdb.log shows:

demangle.test.~this()
(gdb) PASS: gdb.dlang/demangle.exp: _D8demangle4test6__dtorMFZv
demangle _D8demangle4test6__postblitMFZv
Can't demangle "_D8demangle4test6__postblitMFZv"
(gdb) FAIL: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
demangle _D8demangle4testFHAbaZv

Thanks,
Pedro Alves
Iain Buclaw June 29, 2015, 4:26 p.m. UTC | #2
On 29 June 2015 at 17:50, Pedro Alves <palves@redhat.com> wrote:
> On 06/19/2015 04:24 PM, Iain Buclaw wrote:
>> Second downstream patch with dlang demangling related changes only.
>> Should be a more straightforward read than the other one.
>
> Hi Iain, seems like this unfortunately caused regressions in
> gdb.dlang/demangle.exp.  See replies to:
>
>   https://sourceware.org/ml/gdb-testers/2015-q2/msg07341.html
>
> I see the same here, e.g., on x86_64 F20:
>
> -PASS: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
> +FAIL: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
>
> gdb.log shows:
>
> demangle.test.~this()
> (gdb) PASS: gdb.dlang/demangle.exp: _D8demangle4test6__dtorMFZv
> demangle _D8demangle4test6__postblitMFZv
> Can't demangle "_D8demangle4test6__postblitMFZv"
> (gdb) FAIL: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
> demangle _D8demangle4testFHAbaZv
>

Interesting, I don't understand where these tests come from.

---
PASS -> FAIL: gdb.dlang/demangle.exp:
_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZi7tryExecMFMDFZvZv
PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle3fnAFZv3fnBMFZv
PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle4mainFZv1S3fnCFZv
PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle4mainFZv1S3fnDMFZv
PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle4mainFZv5localMFZi
PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
---

All of these *should* fail, and they have been updated to their
correct mangled symbols in libiberty.

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=f91ca6bc00c1e3c70c1d2f4527d4297b76e2eaca#patch18
Iain Buclaw June 29, 2015, 4:30 p.m. UTC | #3
On 29 June 2015 at 18:26, Iain Buclaw <ibuclaw@gdcproject.org> wrote:
> On 29 June 2015 at 17:50, Pedro Alves <palves@redhat.com> wrote:
>> On 06/19/2015 04:24 PM, Iain Buclaw wrote:
>>> Second downstream patch with dlang demangling related changes only.
>>> Should be a more straightforward read than the other one.
>>
>> Hi Iain, seems like this unfortunately caused regressions in
>> gdb.dlang/demangle.exp.  See replies to:
>>
>>   https://sourceware.org/ml/gdb-testers/2015-q2/msg07341.html
>>
>> I see the same here, e.g., on x86_64 F20:
>>
>> -PASS: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
>> +FAIL: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
>>
>> gdb.log shows:
>>
>> demangle.test.~this()
>> (gdb) PASS: gdb.dlang/demangle.exp: _D8demangle4test6__dtorMFZv
>> demangle _D8demangle4test6__postblitMFZv
>> Can't demangle "_D8demangle4test6__postblitMFZv"
>> (gdb) FAIL: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
>> demangle _D8demangle4testFHAbaZv
>>
>
> Interesting, I don't understand where these tests come from.
>
> ---
> PASS -> FAIL: gdb.dlang/demangle.exp:
> _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZi7tryExecMFMDFZvZv
> PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle3fnAFZv3fnBMFZv
> PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle4mainFZv1S3fnCFZv
> PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle4mainFZv1S3fnDMFZv
> PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle4mainFZv5localMFZi
> PASS -> FAIL: gdb.dlang/demangle.exp: _D8demangle4test6__postblitMFZv
> ---
>
> All of these *should* fail, and they have been updated to their
> correct mangled symbols in libiberty.
>
> https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=f91ca6bc00c1e3c70c1d2f4527d4297b76e2eaca#patch18


Oh, I see what I've done.  I forgot to update the gdb testsuite to
reflect changes in libiberty's - or just forgot about their existence
in general.

Great, I'll raise a patch to change these tests too them.

Regards
Iain.
diff mbox

Patch

libiberty/
2015-05-16  Iain Buclaw  <ibuclaw@gdcproject.org>

	* d-demangle.c (dlang_symbol_kinds): New enum.
	(dlang_parse_symbol): Update signature.  Handle an ambiguity between
	pascal functions and template value arguments.  Only check for a type
	if parsing a function, or at the top level.  Return failure if the
	entire symbol was not successfully demangled.
	(dlang_identifier): Update signature.  Handle an ambiguity between two
	adjacent digits in a mangled symbol string.
	(dlang_type): Update call to dlang_parse_symbol.
	(dlang_template_args): Likewise.
	(dlang_parse_template): Likewise.
	(dlang_demangle): Likewise.
	* testsuite/d-demangle-expected: Fix bad tests found, and add problematic
	examples to the unittests.

2015-05-16  Iain Buclaw  <ibuclaw@gdcproject.org>

	* d-demangle.c (dlang_template_args): Skip over specialized template
	parameters in mangled symbol.
	* testsuite/d-demangle-expected: Add coverage and unittest for specialized
	template parameters.

2015-05-16  Iain Buclaw  <ibuclaw@gdcproject.org>

	* d-demangle.c (dlang_type): Handle cent and ucent types.
	* testsuite/d-demangle-expected: Add coverage tests for cent and ucent.

2015-05-16  Iain Buclaw  <ibuclaw@gdcproject.org>

	* d-demangle.c (dlang_attributes): Handle return attributes, ignoring
	return parameters in the mangled string.  Return NULL if have encountered
	an unknown attribute.
	(dlang_function_args): Handle return parameters in the mangled string.
	* testsuite/d-demangle-expected: Add coverage tests for functions with
	return parameters and return attributes.

2015-05-16  Iain Buclaw  <ibuclaw@gdcproject.org>

	* d-demangle.c (dlang_identifier): Check encoded length of identifier
	to verify strncmp matches entire string.
	* testsuite/d-demangle-expected: Fix wrong test for postblit symbol.

2015-05-16  Iain Buclaw  <ibuclaw@gdcproject.org>

	* d-demangle.c (dlang_type_modifiers): New function.
	(dlang_type_modifier_p): New function.
	(dlang_call_convention_p): Ignore any kind of type modifier.
	(dlang_type): Handle and emit the type modifier after delegate types.
	(dlang_parse_symbol): Handle and emit the type modifier after the symbol.
	* testsuite/d-demangle-expected: Add coverage tests for all valid
	usages of function symbols with type modifiers.

2015-05-16  Iain Buclaw  <ibuclaw@gdcproject.org>

	* d-demangle.c (dlang_call_convention): Return NULL if have reached the
	end of the symbol, but expected something to read.
	(dlang_attributes): Likewise.
	(dlang_function_type): Likewise.
	(dlang_type): Likewise.
	(dlang_identifier): Likewise.
	(dlang_value): Likewise.

2015-05-16  Iain Buclaw  <ibuclaw@gdcproject.org>

	* d-demangle.c (dlang_parse_string): Represent embedded whitespace or
	non-printable characters as hex or escape sequences.
	* testsuite/d-demangle-expected: Add test for templates with tabs and
	newlines embedded into the signature.

diff --git a/libiberty/d-demangle.c b/libiberty/d-demangle.c
index bb481c0..a2a3b32 100644
--- a/libiberty/d-demangle.c
+++ b/libiberty/d-demangle.c
@@ -1,5 +1,5 @@ 
 /* Demangler for the D programming language
-   Copyright 2014 Free Software Foundation, Inc.
+   Copyright 2014, 2015 Free Software Foundation, Inc.
    Written by Iain Buclaw (ibuclaw@gdcproject.org)
 
 This file is part of the libiberty library.
@@ -165,6 +165,21 @@  string_prepend (string *p, const char *s)
     }
 }
 
+/* What kinds of symbol we could be parsing.  */
+enum dlang_symbol_kinds
+{
+  /* Top-level symbol, needs it's type checked.  */
+  dlang_top_level,
+  /* Function symbol, needs it's type checked.   */
+  dlang_function,
+  /* Strongly typed name, such as for classes, structs and enums.  */
+  dlang_type_name,
+  /* Template identifier.  */
+  dlang_template_ident,
+  /* Template symbol parameter.  */
+  dlang_template_param
+};
+
 /* Prototypes for forward referenced functions */
 static const char *dlang_function_args (string *, const char *);
 
@@ -172,7 +187,8 @@  static const char *dlang_type (string *, const char *);
 
 static const char *dlang_value (string *, const char *, const char *, char);
 
-static const char *dlang_parse_symbol (string *, const char *);
+static const char *dlang_parse_symbol (string *, const char *,
+				       enum dlang_symbol_kinds);
 
 static const char *dlang_parse_tuple (string *, const char *);
 
@@ -185,7 +201,7 @@  static const char *
 dlang_call_convention (string *decl, const char *mangled)
 {
   if (mangled == NULL || *mangled == '\0')
-    return mangled;
+    return NULL;
 
   switch (*mangled)
     {
@@ -215,13 +231,51 @@  dlang_call_convention (string *decl, const char *mangled)
   return mangled;
 }
 
+/* Extract the type modifiers from MANGLED and append them to DECL.
+   Returns the remaining signature on success or NULL on failure.  */
+static const char *
+dlang_type_modifiers (string *decl, const char *mangled)
+{
+  if (mangled == NULL || *mangled == '\0')
+    return NULL;
+
+  switch (*mangled)
+    {
+    case 'x': /* const */
+      mangled++;
+      string_append (decl, " const");
+      return mangled;
+    case 'y': /* immutable */
+      mangled++;
+      string_append (decl, " immutable");
+      return mangled;
+    case 'O': /* shared */
+      mangled++;
+      string_append (decl, " shared");
+      return dlang_type_modifiers (decl, mangled);
+    case 'N':
+      mangled++;
+      if (*mangled == 'g') /* wild */
+	{
+	  mangled++;
+	  string_append (decl, " inout");
+	  return dlang_type_modifiers (decl, mangled);
+	}
+      else
+	return NULL;
+
+    default:
+      return mangled;
+    }
+}
+
 /* Demangle the D function attributes from MANGLED and append it to DECL.
    Return the remaining string on success or NULL on failure.  */
 static const char *
 dlang_attributes (string *decl, const char *mangled)
 {
   if (mangled == NULL || *mangled == '\0')
-    return mangled;
+    return NULL;
 
   while (*mangled == 'N')
     {
@@ -254,8 +308,10 @@  dlang_attributes (string *decl, const char *mangled)
 	  continue;
 	case 'g':
 	case 'h':
+	case 'k':
 	  /* inout parameter is represented as 'Ng'.
 	     vector parameter is represented as 'Nh'.
+	     return paramenter is represented as 'Nk'.
 	     If we see this, then we know we're really in the
 	     parameter list.  Rewind and break.  */
 	  mangled--;
@@ -264,6 +320,13 @@  dlang_attributes (string *decl, const char *mangled)
 	  mangled++;
 	  string_append (decl, "@nogc ");
 	  continue;
+	case 'j': /* return */
+	  mangled++;
+	  string_append (decl, "return ");
+	  continue;
+
+	default: /* unknown attribute */
+	  return NULL;
 	}
       break;
     }
@@ -280,7 +343,7 @@  dlang_function_type (string *decl, const char *mangled)
   size_t szattr, szargs, sztype;
 
   if (mangled == NULL || *mangled == '\0')
-    return mangled;
+    return NULL;
 
   /* The order of the mangled string is:
 	CallConvention FuncAttrs Arguments ArgClose Type
@@ -353,6 +416,12 @@  dlang_function_args (string *decl, const char *mangled)
 	  string_append (decl, "scope ");
 	}
 
+      if (mangled[0] == 'N' && mangled[1] == 'k') /* return(T) */
+	{
+	  mangled += 2;
+	  string_append (decl, "return ");
+	}
+
       switch (*mangled)
 	{
 	case 'J': /* out(T) */
@@ -380,7 +449,7 @@  static const char *
 dlang_type (string *decl, const char *mangled)
 {
   if (mangled == NULL || *mangled == '\0')
-    return mangled;
+    return NULL;
 
   switch (*mangled)
     {
@@ -474,12 +543,24 @@  dlang_type (string *decl, const char *mangled)
     case 'E': /* enum T */
     case 'T': /* typedef T */
       mangled++;
-      return dlang_parse_symbol (decl, mangled);
+      return dlang_parse_symbol (decl, mangled, dlang_type_name);
     case 'D': /* delegate T */
+    {
+      string mods;
+      size_t szmods;
       mangled++;
+
+      string_init (&mods);
+      mangled = dlang_type_modifiers (&mods, mangled);
+      szmods = string_length (&mods);
+
       mangled = dlang_function_type (decl, mangled);
       string_append (decl, "delegate");
+      string_appendn (decl, mods.b, szmods);
+
+      string_delete (&mods);
       return mangled;
+    }
     case 'B': /* tuple T */
       mangled++;
       return dlang_parse_tuple (decl, mangled);
@@ -588,6 +669,20 @@  dlang_type (string *decl, const char *mangled)
       mangled++;
       string_append (decl, "dchar");
       return mangled;
+    case 'z':
+      mangled++;
+      switch (*mangled)
+	{
+	case 'i':
+	  mangled++;
+	  string_append (decl, "cent");
+	  return mangled;
+	case 'k':
+	  mangled++;
+	  string_append (decl, "ucent");
+	  return mangled;
+	}
+      return NULL;
 
     default: /* unhandled */
       return NULL;
@@ -597,97 +692,162 @@  dlang_type (string *decl, const char *mangled)
 /* Extract the identifier from MANGLED and append it to DECL.
    Return the remaining string on success or NULL on failure.  */
 static const char *
-dlang_identifier (string *decl, const char *mangled)
+dlang_identifier (string *decl, const char *mangled,
+		  enum dlang_symbol_kinds kind)
 {
+  char *endptr;
+  long len;
+
   if (mangled == NULL || *mangled == '\0')
-    return mangled;
+    return NULL;
 
-  if (ISDIGIT (*mangled))
+  len = strtol (mangled, &endptr, 10);
+
+  if (endptr == NULL || len <= 0)
+    return NULL;
+
+  /* In template parameter symbols, the first character of the mangled
+     name can be a digit.  This causes ambiguity issues because the
+     digits of the two numbers are adjacent.  */
+  if (kind == dlang_template_param)
     {
-      char *endptr;
-      long i = strtol (mangled, &endptr, 10);
+      long psize = len;
+      char *pend;
+      int saved = string_length (decl);
 
-      if (endptr == NULL || i <= 0 || strlen (endptr) < (size_t) i)
+      /* Work backwards until a match is found.  */
+      for (pend = endptr; endptr != NULL; pend--)
+	{
+	  mangled = pend;
+
+	  /* Reached the beginning of the pointer to the name length,
+	     try parsing the entire symbol.  */
+	  if (psize == 0)
+	    {
+	      psize = len;
+	      pend = endptr;
+	      endptr = NULL;
+	    }
+
+	  /* Check whether template parameter is a function with a valid
+	     return type or an untyped identifier.  */
+	  if (ISDIGIT (*mangled))
+	    mangled = dlang_parse_symbol (decl, mangled, dlang_template_ident);
+	  else if (strncmp (mangled, "_D", 2) == 0)
+	    {
+	      mangled += 2;
+	      mangled = dlang_parse_symbol (decl, mangled, dlang_function);
+	    }
+
+	  /* Check for name length mismatch.  */
+	  if (mangled && (mangled - pend) == psize)
+	    return mangled;
+
+	  psize /= 10;
+	  string_setlength (decl, saved);
+	}
+
+      /* No match on any combinations.  */
+      return NULL;
+    }
+  else
+    {
+      if (strlen (endptr) < (size_t) len)
 	return NULL;
 
       mangled = endptr;
 
       /* May be a template instance.  */
-      if (i >= 5 && strncmp (mangled, "__T", 3) == 0)
+      if (len >= 5 && strncmp (mangled, "__T", 3) == 0)
 	{
 	  /* Template symbol.  */
 	  if (ISDIGIT (mangled[3]) && mangled[3] != '0')
-	    return dlang_parse_template (decl, mangled, i);
+	    return dlang_parse_template (decl, mangled, len);
 
 	  return NULL;
 	}
 
-      if (strncmp (mangled, "__ctor", i) == 0)
-	{
-	  /* Constructor symbol for a class/struct.  */
-	  string_append (decl, "this");
-	  mangled += i;
-	  return mangled;
-	}
-      else if (strncmp (mangled, "__dtor", i) == 0)
-	{
-	  /* Destructor symbol for a class/struct.  */
-	  string_append (decl, "~this");
-	  mangled += i;
-	  return mangled;
-	}
-      else if (strncmp (mangled, "__postblit", i) == 0)
-	{
-	  /* Postblit symbol for a struct.  */
-	  string_append (decl, "this(this)");
-	  mangled += i;
-	  return mangled;
-	}
-      else if (strncmp (mangled, "__initZ", i+1) == 0)
-	{
-	  /* The static initialiser for a given symbol.  */
-	  string_append (decl, "init$");
-	  mangled += i + 1;
-	  return mangled;
-	}
-      else if (strncmp (mangled, "__ClassZ", i+1) == 0)
+      switch (len)
 	{
-	  /* The classinfo symbol for a given class.  */
-	  string_prepend (decl, "ClassInfo for ");
-	  string_setlength (decl, string_length (decl) - 1);
-	  mangled += i + 1;
-	  return mangled;
-	}
-      else if (strncmp (mangled, "__vtblZ", i+1) == 0)
-	{
-	  /* The vtable symbol for a given class.  */
-	  string_prepend (decl, "vtable for ");
-	  string_setlength (decl, string_length (decl) - 1);
-	  mangled += i + 1;
-	  return mangled;
-	}
-      else if (strncmp (mangled, "__InterfaceZ", i+1) == 0)
-	{
-	  /* The interface symbol for a given class.  */
-	  string_prepend (decl, "Interface for ");
-	  string_setlength (decl, string_length (decl) - 1);
-	  mangled += i + 1;
-	  return mangled;
-	}
-      else if (strncmp (mangled, "__ModuleInfoZ", i+1) == 0)
-	{
-	  /* The ModuleInfo symbol for a given module.  */
-	  string_prepend (decl, "ModuleInfo for ");
-	  string_setlength (decl, string_length (decl) - 1);
-	  mangled += i + 1;
-	  return mangled;
+	case 6:
+	  if (strncmp (mangled, "__ctor", len) == 0)
+	    {
+	      /* Constructor symbol for a class/struct.  */
+	      string_append (decl, "this");
+	      mangled += len;
+	      return mangled;
+	    }
+	  else if (strncmp (mangled, "__dtor", len) == 0)
+	    {
+	      /* Destructor symbol for a class/struct.  */
+	      string_append (decl, "~this");
+	      mangled += len;
+	      return mangled;
+	    }
+	  else if (strncmp (mangled, "__initZ", len+1) == 0)
+	    {
+	      /* The static initialiser for a given symbol.  */
+	      string_append (decl, "init$");
+	      mangled += len;
+	      return mangled;
+	    }
+	  else if (strncmp (mangled, "__vtblZ", len+1) == 0)
+	    {
+	      /* The vtable symbol for a given class.  */
+	      string_prepend (decl, "vtable for ");
+	      string_setlength (decl, string_length (decl) - 1);
+	      mangled += len;
+	      return mangled;
+	    }
+	  break;
+
+	case 7:
+	  if (strncmp (mangled, "__ClassZ", len+1) == 0)
+	    {
+	      /* The classinfo symbol for a given class.  */
+	      string_prepend (decl, "ClassInfo for ");
+	      string_setlength (decl, string_length (decl) - 1);
+	      mangled += len;
+	      return mangled;
+	    }
+	  break;
+
+	case 10:
+	  if (strncmp (mangled, "__postblitMFZ", len+3) == 0)
+	    {
+	      /* Postblit symbol for a struct.  */
+	      string_append (decl, "this(this)");
+	      mangled += len + 3;
+	      return mangled;
+	    }
+	  break;
+
+	case 11:
+	  if (strncmp (mangled, "__InterfaceZ", len+1) == 0)
+	    {
+	      /* The interface symbol for a given class.  */
+	      string_prepend (decl, "Interface for ");
+	      string_setlength (decl, string_length (decl) - 1);
+	      mangled += len;
+	      return mangled;
+	    }
+	  break;
+
+	case 12:
+	  if (strncmp (mangled, "__ModuleInfoZ", len+1) == 0)
+	    {
+	      /* The ModuleInfo symbol for a given module.  */
+	      string_prepend (decl, "ModuleInfo for ");
+	      string_setlength (decl, string_length (decl) - 1);
+	      mangled += len;
+	      return mangled;
+	    }
+	  break;
 	}
 
-      string_appendn (decl, mangled, i);
-      mangled += i;
+      string_appendn (decl, mangled, len);
+      mangled += len;
     }
-  else
-    return NULL;
 
   return mangled;
 }
@@ -931,7 +1091,38 @@  dlang_parse_string (string *decl, const char *mangled)
 	  char a = ascii2hex (mangled[0]);
 	  char b = ascii2hex (mangled[1]);
 	  char val = (a << 4) | b;
-	  string_appendn (decl, &val, 1);
+
+	  /* Sanitize white and non-printable characters.  */
+	  switch (val)
+	    {
+	    case ' ':
+	      string_append (decl, " ");
+	      break;
+	    case '\t':
+	      string_append (decl, "\\t");
+	      break;
+	    case '\n':
+	      string_append (decl, "\\n");
+	      break;
+	    case '\r':
+	      string_append (decl, "\\r");
+	      break;
+	    case '\f':
+	      string_append (decl, "\\f");
+	      break;
+	    case '\v':
+	      string_append (decl, "\\v");
+	      break;
+
+	    default:
+	      if (ISPRINT (val))
+		string_appendn (decl, &val, 1);
+	      else
+		{
+		  string_append (decl, "\\x");
+		  string_appendn (decl, mangled, 2);
+		}
+	    }
 	}
       else
 	return NULL;
@@ -1030,7 +1221,7 @@  static const char *
 dlang_value (string *decl, const char *mangled, const char *name, char type)
 {
   if (mangled == NULL || *mangled == '\0')
-    return mangled;
+    return NULL;
 
   switch (*mangled)
     {
@@ -1104,28 +1295,54 @@  dlang_value (string *decl, const char *mangled, const char *name, char type)
   return mangled;
 }
 
+/* Extract the type modifiers from MANGLED and return the string
+   length that it consumes in MANGLED on success or 0 on failure.  */
 static int
-dlang_call_convention_p (const char *mangled)
+dlang_type_modifier_p (const char *mangled)
 {
-  size_t i;
+  int i;
 
   switch (*mangled)
     {
-    case 'F': case 'U': case 'V':
-    case 'W': case 'R':
+    case 'x': case 'y':
       return 1;
 
-    case 'M': /* Prefix for functions needing 'this' */
-      i = 1;
-      if (mangled[i] == 'x')
-	i++;
+    case 'O':
+      mangled++;
+      i = dlang_type_modifier_p (mangled);
+      return i + 1;
 
-      switch (mangled[i])
+    case 'N':
+      mangled++;
+      if (*mangled == 'g')
 	{
-	case 'F': case 'U': case 'V':
-	case 'W': case 'R':
-	  return 1;
+	  mangled++;
+	  i = dlang_type_modifier_p (mangled);
+	  return i + 2;
 	}
+    }
+
+  return 0;
+}
+
+/* Extract the function calling convention from MANGLED and
+   return 1 on success or 0 on failure.  */
+static int
+dlang_call_convention_p (const char *mangled)
+{
+  /* Prefix for functions needing 'this' */
+  if (*mangled == 'M')
+    {
+      mangled++;
+      /* Also skip over any type modifiers.  */
+      mangled += dlang_type_modifier_p (mangled);
+    }
+
+  switch (*mangled)
+    {
+    case 'F': case 'U': case 'V':
+    case 'W': case 'R':
+      return 1;
 
     default:
       return 0;
@@ -1135,23 +1352,41 @@  dlang_call_convention_p (const char *mangled)
 /* Extract and demangle the symbol in MANGLED and append it to DECL.
    Returns the remaining signature on success or NULL on failure.  */
 static const char *
-dlang_parse_symbol (string *decl, const char *mangled)
+dlang_parse_symbol (string *decl, const char *mangled,
+		    enum dlang_symbol_kinds kind)
 {
+  int saved;
   size_t n = 0;
   do
     {
       if (n++)
 	string_append (decl, ".");
 
-      mangled = dlang_identifier (decl, mangled);
+      mangled = dlang_identifier (decl, mangled, kind);
 
       if (mangled && dlang_call_convention_p (mangled))
 	{
-	  int saved;
+	  string mods;
+	  const char *start = NULL;
+	  int checkpoint = 0;
 
 	  /* Skip over 'this' parameter.  */
 	  if (*mangled == 'M')
-	    mangled += (mangled[1] == 'x') ? 2 : 1;
+	    mangled++;
+
+	  /* We have reached here because we expect an extern(Pascal) function.
+	     However this is so rare, that it is more likely a template value
+	     parameter.  Since this can't be assumed, first attempt parsing
+	     the symbol as a function, and then back out on failure.  */
+	  if (*mangled == 'V')
+	    {
+	      start = mangled;
+	      checkpoint = string_length (decl);
+	    }
+
+	  /* Save the type modifiers for appending at the end.  */
+	  string_init (&mods);
+	  mangled = dlang_type_modifiers (&mods, mangled);
 
 	  /* Skip over calling convention and attributes in qualified name.  */
 	  saved = string_length (decl);
@@ -1163,17 +1398,41 @@  dlang_parse_symbol (string *decl, const char *mangled)
 	  mangled = dlang_function_args (decl, mangled);
 	  string_append (decl, ")");
 
-	  /* Demangle the function return type as a kind of sanity test.  */
-	  if (mangled && !ISDIGIT (*mangled))
+	  /* Add any const/immutable/shared modifier. */
+	  string_appendn (decl, mods.b, string_length (&mods));
+	  string_delete (&mods);
+
+	  if (mangled == NULL && checkpoint != 0)
 	    {
-	      saved = string_length (decl);
-	      mangled = dlang_type (decl, mangled);
-	      string_setlength (decl, saved);
+	      mangled = start;
+	      string_setlength (decl, checkpoint);
 	    }
 	}
     }
   while (mangled && ISDIGIT (*mangled));
 
+  /* Only top-level symbols or function template parameters have
+     a type that needs checking.  */
+  if (kind == dlang_top_level || kind == dlang_function)
+    {
+      /* Artificial symbols end with 'Z' and have no type.  */
+      if (mangled && *mangled == 'Z')
+	mangled++;
+      else
+	{
+	  saved = string_length (decl);
+	  mangled = dlang_type (decl, mangled);
+	  string_setlength (decl, saved);
+	}
+
+      /* Check that the entire symbol was successfully demangled.  */
+      if (kind == dlang_top_level)
+	{
+	  if (mangled == NULL || *mangled != '\0')
+	    return NULL;
+	}
+    }
+
   return mangled;
 }
 
@@ -1221,11 +1480,15 @@  dlang_template_args (string *decl, const char *mangled)
       if (n++)
 	string_append (decl, ", ");
 
+      /* Skip over specialised template prefix.  */
+      if (*mangled == 'H')
+	mangled++;
+
       switch (*mangled)
 	{
 	case 'S': /* Symbol parameter.  */
 	  mangled++;
-	  mangled = dlang_parse_symbol (decl, mangled);
+	  mangled = dlang_parse_symbol (decl, mangled, dlang_template_param);
 	  break;
 	case 'T': /* Type parameter.  */
 	  mangled++;
@@ -1283,7 +1546,7 @@  dlang_parse_template (string *decl, const char *mangled, long len)
   mangled += 3;
 
   /* Template identifier.  */
-  mangled = dlang_identifier (decl, mangled);
+  mangled = dlang_identifier (decl, mangled, dlang_template_ident);
 
   /* Template arguments.  */
   string_append (decl, "!(");
@@ -1322,7 +1585,7 @@  dlang_demangle (const char *mangled, int option ATTRIBUTE_UNUSED)
     {
       mangled += 2;
 
-      if (dlang_parse_symbol (&decl, mangled) == NULL)
+      if (dlang_parse_symbol (&decl, mangled, dlang_top_level) == NULL)
 	string_delete (&decl);
     }
 
diff --git a/libiberty/testsuite/d-demangle-expected b/libiberty/testsuite/d-demangle-expected
index 2aeacb8..6c8ccdf 100644
--- a/libiberty/testsuite/d-demangle-expected
+++ b/libiberty/testsuite/d-demangle-expected
@@ -114,6 +114,14 @@  _D8demangle4testFwZv
 demangle.test(dchar)
 #
 --format=dlang
+_D8demangle4testFziZv
+demangle.test(cent)
+#
+--format=dlang
+_D8demangle4testFzkZv
+demangle.test(ucent)
+#
+--format=dlang
 _D8demangle4testFOaZv
 demangle.test(shared(char))
 #
@@ -314,6 +322,14 @@  _D8demangle4testFMaZv
 demangle.test(scope char)
 #
 --format=dlang
+_D8demangle4testFNjaZv
+demangle.test(char)
+#
+--format=dlang
+_D8demangle4testFNkaZv
+demangle.test(return char)
+#
+--format=dlang
 _D8demangle4testFaXv
 demangle.test(char...)
 #
@@ -434,6 +450,22 @@  _D8demangle4testFDFNdNfNaZaZv
 demangle.test(char() @property @safe pure delegate)
 #
 --format=dlang
+_D8demangle4testFNjDFZaZv
+demangle.test(char() delegate)
+#
+--format=dlang
+_D8demangle4testFNkDFZaZv
+demangle.test(return char() delegate)
+#
+--format=dlang
+_D8demangle4testFDFNjZaZv
+demangle.test(char() return delegate)
+#
+--format=dlang
+_D8demangle4testFNjNkDFNjZaZv
+demangle.test(return char() return delegate)
+#
+--format=dlang
 _D8demangle4testFFNaZaZv
 demangle.test(char() pure function)
 #
@@ -474,6 +506,22 @@  _D8demangle4testFFNdNfNaZaZv
 demangle.test(char() @property @safe pure function)
 #
 --format=dlang
+_D8demangle4testFNjFZaZv
+demangle.test(char() function)
+#
+--format=dlang
+_D8demangle4testFNkFZaZv
+demangle.test(return char() function)
+#
+--format=dlang
+_D8demangle4testFFNjZaZv
+demangle.test(char() return function)
+#
+--format=dlang
+_D8demangle4testFNjNkFNjZaZv
+demangle.test(return char() return function)
+#
+--format=dlang
 _D8demangle4test6__initZ
 demangle.test.init$
 #
@@ -502,7 +550,7 @@  _D8demangle4test6__dtorMFZv
 demangle.test.~this()
 #
 --format=dlang
-_D8demangle4test6__postblitMFZv
+_D8demangle4test10__postblitMFZv
 demangle.test.this(this)
 #
 --format=dlang
@@ -566,12 +614,12 @@  _D8demangle17__T4testS6symbolZv
 demangle.test!(symbol)
 #
 --format=dlang
-_D8demangle21__T4testS6symbol3fooZv
+_D8demangle23__T4testS116symbol3fooZv
 demangle.test!(symbol.foo)
 #
 --format=dlang
-_D8demangle25__T4testS6symbol3foo3barZv
-demangle.test!(symbol.foo.bar)
+_D8demangle32__T4testS20_D6symbol3foo3barFZvZv
+demangle.test!(symbol.foo.bar())
 #
 --format=dlang
 _D8demangle19__T4testTaS6symbolZv
@@ -582,6 +630,10 @@  _D8demangle19__T4testS6symbolTaZv
 demangle.test!(symbol, char)
 #
 --format=dlang
+_D8demangle12__T4testHTaZv
+demangle.test!(char)
+#
+--format=dlang
 _D8demangle13__T4testVPinZv
 demangle.test!(null)
 #
@@ -753,6 +805,70 @@  demangle.test!(demangle.S(1, 2))
 _D8demangle35__T4testVS8demangle1SS2i1a3_616263Zv
 demangle.test!(demangle.S(1, "abc"))
 #
+--format=dlang
+_D8demangle4testMxFZv
+demangle.test() const
+#
+--format=dlang
+_D8demangle4testMyFZv
+demangle.test() immutable
+#
+--format=dlang
+_D8demangle4testMNgFZv
+demangle.test() inout
+#
+--format=dlang
+_D8demangle4testMNgxFZv
+demangle.test() inout const
+#
+--format=dlang
+_D8demangle4testMOFZv
+demangle.test() shared
+#
+--format=dlang
+_D8demangle4testMOxFZv
+demangle.test() shared const
+#
+--format=dlang
+_D8demangle4testMONgFZv
+demangle.test() shared inout
+#
+--format=dlang
+_D8demangle4testMONgxFZv
+demangle.test() shared inout const
+#
+--format=dlang
+_D8demangle4testFDxFZaZv
+demangle.test(char() delegate const)
+#
+--format=dlang
+_D8demangle4testFDyFZaZv
+demangle.test(char() delegate immutable)
+#
+--format=dlang
+_D8demangle4testFDNgFZaZv
+demangle.test(char() delegate inout)
+#
+--format=dlang
+_D8demangle4testFDNgxFZaZv
+demangle.test(char() delegate inout const)
+#
+--format=dlang
+_D8demangle4testFDOFZaZv
+demangle.test(char() delegate shared)
+#
+--format=dlang
+_D8demangle4testFDOxFZaZv
+demangle.test(char() delegate shared const)
+#
+--format=dlang
+_D8demangle4testFDONgFZaZv
+demangle.test(char() delegate shared inout)
+#
+--format=dlang
+_D8demangle4testFDONgxFZaZv
+demangle.test(char() delegate shared inout const)
+#
 # Unittests
 #
 --format=dlang
@@ -816,19 +932,19 @@  _D6plugin8generateFiiZAOa
 plugin.generate(int, int)
 #
 --format=dlang
-_D8demangle3fnAFZv3fnBMFZv
+_D8demangle3fnAFZ3fnBMFZv
 demangle.fnA().fnB()
 #
 --format=dlang
-_D8demangle4mainFZv1S3fnCFZv
+_D8demangle4mainFZ1S3fnCMFZv
 demangle.main().S.fnC()
 #
 --format=dlang
-_D8demangle4mainFZv1S3fnDMFZv
+_D8demangle4mainFZ1S3fnDMFZv
 demangle.main().S.fnD()
 #
 --format=dlang
-_D8demangle4mainFZv5localMFZi
+_D8demangle4mainFZ5localMFZi
 demangle.main().local()
 #
 --format=dlang
@@ -872,7 +988,7 @@  _D6object14TypeInfo_Array8argTypesMFNbNfJC8TypeInfoJC8TypeInfoZi
 object.TypeInfo_Array.argTypes(out TypeInfo, out TypeInfo)
 #
 --format=dlang
-_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZi7tryExecMFMDFZvZv
+_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv
 rt.dmain2._d_run_main(int, char**, extern(C) int(char[][]) function*).tryExec(scope void() delegate)
 #
 --format=dlang
@@ -928,9 +1044,37 @@  _D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkB
 gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)
 #
 --format=dlang
-_D8serenity9persister6Sqlite70__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZv4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZv4Test
+_D8serenity9persister6Sqlite69__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZ4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZ4Test
 serenity.persister.Sqlite.SqlitePersister!(serenity.persister.Sqlite.__unittest6().Test).SqlitePersister.opIndex!().opIndex(ulong)
 #
 --format=dlang
-_D4test4mainFZv5localMFZi
-test.main().local()
+_D3std6socket12InternetHost221__T13getHostNoSyncVAyaa96_0a09202020206175746f2078203d2068746f6e6c28706172616d293b0a09202020206175746f206865203d20676574686f73746279616464722826782c20342c206361737428696e74294164647265737346616d696c792e494e4554293b0a09TkZ13getHostNoSyncMFkZb
+std.socket.InternetHost.getHostNoSync!("\n\t    auto x = htonl(param);\n\t    auto he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET);\n\t", uint).getHostNoSync(uint)
+#
+--format=dlang
+_D2gc6config13__T5parseHTfZ5parseFNbNiAxaKAxaKfZb
+gc.config.parse!(float).parse(const(char)[], ref const(char)[], ref float)
+#
+--format=dlang
+_D3std11parallelism273__T4TaskS213std11parallelism3runTDFS3std9algorithm87__T9MapResultS27_D4test4mainFZ7getTermMFiZeTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ9MapResultmmZeTS3std9algorithm87__T9MapResultS27_D4test4mainFZ7getTermMFiZeTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ9MapResultTmTmZ4Task4implFPvZv
+std.parallelism.Task!(std.parallelism.run, real(std.algorithm.MapResult!(test.main().getTerm(int), std.range.iota!(int, int).iota(int, int).Result).MapResult, ulong, ulong) delegate, std.algorithm.MapResult!(test.main().getTerm(int), std.range.iota!(int, int).iota(int, int).Result).MapResult, ulong, ulong).Task.impl(void*)
+#
+--format=dlang
+_D2rt5minfo16__unittestL518_6FZ12UTModuleInfo6__ctorMFNckZS2rt5minfo16__unittestL518_6FZ12UTModuleInfo
+rt.minfo.__unittestL518_6().UTModuleInfo.this(uint)
+#
+--format=dlang
+_D3std6traits37__T7fqnTypeTC6ObjectVbi0Vbi0Vbi0Vbi0Z13addQualifiersFAyabbbbZAya
+std.traits.fqnType!(Object, false, false, false, false).addQualifiers(immutable(char)[], bool, bool, bool, bool)
+#
+--format=dlang
+_D3std9algorithm117__T9MapResultS153std5range4onlyTS3std9algorithm53__T12FilterResultS28_D3std3uni7isUpperFNaNbNfwZbTAyaZ12FilterResultZ9MapResult5frontMFNaNdNfZS3std5range22__T10OnlyResultTwVmi1Z10OnlyResult
+std.algorithm.MapResult!(std.range.only, std.algorithm.FilterResult!(std.uni.isUpper(dchar), immutable(char)[]).FilterResult).MapResult.front()
+#
+--format=dlang
+_D3std6traits17__T6fqnSymS43stdZ11adjustIdentFAyaZAya
+std.traits.fqnSym!(std).adjustIdent(immutable(char)[])
+#
+--format=dlang
+_D2rt8lifetime36__T14_d_newarrayOpTS13_d_newarrayiTZ14_d_newarrayOpTFNaNbxC8TypeInfomPmZAv
+rt.lifetime._d_newarrayOpT!(_d_newarrayiT)._d_newarrayOpT(const(TypeInfo), ulong, ulong*)