From 9ddca3b1327ccae185000a9cabaf6c5d61466907 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Sat, 27 May 2017 14:18:26 +0200
Subject: [PATCH 01/13] core.demangle: support template encoding without length
 prefix

---
 src/core/demangle.d | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index ae68d22291..b4d12dc599 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -1483,21 +1483,21 @@ private struct Demangle
     TemplateInstanceName:
         Number __T LName TemplateArgs Z
     */
-    void parseTemplateInstanceName()
+    void parseTemplateInstanceName(bool hasNumber)
     {
         debug(trace) printf( "parseTemplateInstanceName+\n" );
         debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" );
 
         auto sav = pos;
         scope(failure) pos = sav;
-        auto n = decodeNumber();
+        auto n = hasNumber ? decodeNumber() : 0;
         auto beg = pos;
         match( "__T" );
         parseLName();
         put( "!(" );
         parseTemplateArgs();
         match( 'Z' );
-        if( pos - beg != n )
+        if( hasNumber && pos - beg != n )
             error( "Template name length mismatch" );
         put( ')' );
     }
@@ -1532,6 +1532,11 @@ private struct Demangle
         // TemplateInstanceName -> Number "__T"
         switch( front )
         {
+        case '_':
+            // no length encoding for templates for new mangling
+            parseTemplateInstanceName(false);
+            return;
+
         case '0': .. case '9':
             if( mayBeTemplateInstanceName() )
             {
@@ -1540,7 +1545,7 @@ private struct Demangle
                 try
                 {
                     debug(trace) printf( "may be template instance name\n" );
-                    parseTemplateInstanceName();
+                    parseTemplateInstanceName(true);
                     return;
                 }
                 catch( ParseException e )
@@ -1589,12 +1594,12 @@ private struct Demangle
                 put( '(' );
                 parseFuncArguments();
                 put( ')' );
-                if( !isDigit( front ) ) // voldemort types don't have a return type on the function
+                if( !isDigit( front ) && front != '_' ) // voldemort types don't have a return type on the function
                 {
                     auto funclen = len;
                     parseType();
 
-                    if( !isDigit( front ) )
+                    if( !isDigit( front ) && front != '_' )
                     {
                         // not part of a qualified name, so back up
                         pos = prevpos;
@@ -1604,7 +1609,7 @@ private struct Demangle
                         len = funclen; // remove return type from qualified name
                 }
             }
-        } while( isDigit( front ) );
+        } while( isDigit( front ) || front == '_' );
         return dst[beg .. len];
     }
 

From ac13f8fd0d695031ae115b96464e3ef0fc758377 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Sat, 27 May 2017 17:18:08 +0200
Subject: [PATCH 02/13] core.demangle: add demangling of back references

---
 src/core/demangle.d | 202 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 163 insertions(+), 39 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index b4d12dc599..1be88bf067 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -68,8 +68,9 @@ private struct Demangle
     char[]          dst     = null;
     size_t          pos     = 0;
     size_t          len     = 0;
+    size_t          brp     = 0; // current back reference pos
     AddType         addType = AddType.yes;
-
+    bool            mute    = false;
 
     static class ParseException : Exception
     {
@@ -173,7 +174,7 @@ private struct Demangle
             dst[b] = t;
         }
 
-        if( val.length )
+        if( val.length && !mute )
         {
             assert( contains( dst[0 .. len], val ) );
             debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr );
@@ -193,7 +194,7 @@ private struct Demangle
 
     char[] append( const(char)[] val )
     {
-        if( val.length )
+        if( val.length && !mute )
         {
             if( !dst.length )
                 dst.length = minBufSize;
@@ -296,6 +297,13 @@ private struct Demangle
         return char.init;
     }
 
+    char peek( size_t n )
+    {
+        if( pos + n < buf.length )
+            return buf[pos + n];
+        return char.init;
+    }
+
 
     void test( char val )
     {
@@ -334,6 +342,56 @@ private struct Demangle
             popFront();
     }
 
+    bool isSymbolNameFront()
+    {
+        char val = front;
+        if( isDigit( val ) || val == '_' )
+            return true;
+        if( val != 'Q' )
+            return false;
+
+        // check the back reference encoding after 'Q'
+        val = peekBackref();
+        return isDigit( val ); // identifier ref
+    }
+
+    // return the first character at the back reference
+    char peekBackref()
+    {
+        assert( front == 'Q' );
+        auto n = decodeBackref!1();
+        if (!n || n > pos)
+            error("invalid back reference");
+
+        return buf[pos - n];
+    }
+
+    size_t decodeBackref(size_t peekAt = 0)()
+    {
+        enum base = 26;
+        size_t n = 0;
+        for (size_t p; ; p++)
+        {
+            char t;
+            static if (peekAt > 0)
+            {
+                t = peek(peekAt + p);
+            }
+            else
+            {
+                t = front;
+                popFront();
+            }
+            if (t < 'A' || t > 'Z')
+            {
+                if (t < 'a' || t > 'z')
+                    error("invalid back reference");
+                n = base * n + t - 'a';
+                return n;
+            }
+            n = base * n + t - 'A';
+        }
+    }
 
     //////////////////////////////////////////////////////////////////////////
     // Parsing Implementation
@@ -492,8 +550,24 @@ private struct Demangle
         debug(trace) printf( "parseLName+\n" );
         debug(trace) scope(success) printf( "parseLName-\n" );
 
+        if( front == 'Q' )
+        {
+            // back reference to LName
+            auto refPos = pos;
+            popFront();
+            size_t n = decodeBackref();
+            if( !n || n > refPos )
+                error( "Invalid LName back reference" );
+            if ( !mute )
+            {
+                auto savePos = pos;
+                scope(exit) pos = savePos;
+                pos = refPos - n;
+                parseLName();
+            }
+            return;
+        }
         auto n = decodeNumber();
-
         if( !n || n > buf.length || n > buf.length - pos )
             error( "LName must be at least 1 character" );
         if( '_' != front && !isAlpha( front ) )
@@ -713,8 +787,30 @@ private struct Demangle
         auto beg = len;
         auto t = front;
 
+        char[] parseBackrefType(scope char[] delegate() parseDg)
+        {
+            if (pos == brp)
+                error("recursive back reference");
+            auto refPos = pos;
+            popFront();
+            auto n = decodeBackref();
+            if (n == 0 || n > pos)
+                error("invalid back reference");
+            if ( mute )
+                return null;
+            auto savePos = pos;
+            auto saveBrp = brp;
+            scope(success) { pos = savePos; brp = saveBrp; }
+            pos = refPos - n;
+            brp = refPos;
+            auto ret = parseDg();
+            return ret;
+        }
+
         switch( t )
         {
+        case 'Q': // Type back reference
+            return parseBackrefType( () => parseType( name ) );
         case 'O': // Shared (O Type)
             popFront();
             put( "shared(" );
@@ -801,7 +897,10 @@ private struct Demangle
             return dst[beg .. len];
         case 'D': // TypeDelegate (D TypeFunction)
             popFront();
-            parseTypeFunction( name, IsDelegate.yes );
+            if( front == 'Q' )
+                parseBackrefType( () => parseTypeFunction( name, IsDelegate.yes ) );
+            else
+                parseTypeFunction( name, IsDelegate.yes );
             return dst[beg .. len];
         case 'n': // TypeNone (n)
             popFront();
@@ -1419,6 +1518,8 @@ private struct Demangle
                 //       generated by parseValue, so it is safe to simply
                 //       decrement len and let put/append do its thing.
                 char t = front; // peek at type for parseValue
+                if( t == 'Q' )
+                    t = peekBackref();
                 char[] name; silent( name = parseType() );
                 parseValue( name, t );
                 continue;
@@ -1430,7 +1531,7 @@ private struct Demangle
                 {
                     auto l = len;
                     auto p = pos;
-
+                    auto b = brp;
                     try
                     {
                         debug(trace) printf( "may be mangled name arg\n" );
@@ -1441,6 +1542,7 @@ private struct Demangle
                     {
                         len = l;
                         pos = p;
+                        brp = b;
                         debug(trace) printf( "not a mangled name arg\n" );
                     }
                 }
@@ -1489,7 +1591,12 @@ private struct Demangle
         debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" );
 
         auto sav = pos;
-        scope(failure) pos = sav;
+        auto saveBrp = brp;
+        scope(failure)
+        {
+            pos = sav;
+            brp = saveBrp;
+        }
         auto n = hasNumber ? decodeNumber() : 0;
         auto beg = pos;
         match( "__T" );
@@ -1554,6 +1661,8 @@ private struct Demangle
                     len = t;
                 }
             }
+            goto case;
+        case 'Q':
             parseLName();
             return;
         default:
@@ -1561,6 +1670,47 @@ private struct Demangle
         }
     }
 
+    void parseFunctionType( bool muteReturnType )
+    {
+        if( 'M' == front )
+        {
+            // do not emit "needs this"
+            popFront();
+        }
+        if( isCallConvention( front ) )
+        {
+            // try to demangle a function, in case we are pointing to some function local
+            auto prevpos = pos;
+            auto prevlen = len;
+
+            // we don't want calling convention and attributes in the qualified name
+            parseCallConvention();
+            parseFuncAttr();
+            len = prevlen;
+
+            put( '(' );
+            parseFuncArguments();
+            put( ')' );
+            if( !isSymbolNameFront() ) // voldemort types don't have a return type on the function
+            {
+                auto funclen = len;
+
+                bool prevMute = mute;
+                scope(exit) mute = prevMute;
+                mute = muteReturnType;
+                parseType();
+
+                if( !muteReturnType && !isSymbolNameFront() )
+                {
+                    // not part of a qualified name, so back up
+                    pos = prevpos;
+                    len = prevlen;
+                }
+                else
+                    len = funclen; // remove return type from qualified name
+            }
+        }
+    }
 
     /*
     QualifiedName:
@@ -1579,37 +1729,8 @@ private struct Demangle
             if( n++ )
                 put( '.' );
             parseSymbolName();
-
-            if( isCallConvention( front ) )
-            {
-                // try to demangle a function, in case we are pointing to some function local
-                auto prevpos = pos;
-                auto prevlen = len;
-
-                // we don't want calling convention and attributes in the qualified name
-                parseCallConvention();
-                parseFuncAttr();
-                len = prevlen;
-
-                put( '(' );
-                parseFuncArguments();
-                put( ')' );
-                if( !isDigit( front ) && front != '_' ) // voldemort types don't have a return type on the function
-                {
-                    auto funclen = len;
-                    parseType();
-
-                    if( !isDigit( front ) && front != '_' )
-                    {
-                        // not part of a qualified name, so back up
-                        pos = prevpos;
-                        len = prevlen;
-                    }
-                    else
-                        len = funclen; // remove return type from qualified name
-                }
-            }
-        } while( isDigit( front ) || front == '_' );
+            parseFunctionType( false );
+        } while( isSymbolNameFront() );
         return dst[beg .. len];
     }
 
@@ -1662,7 +1783,7 @@ private struct Demangle
                 auto newsz = a < b ? b : a;
                 debug(info) printf( "growing dst to %lu bytes\n", newsz );
                 dst.length = newsz;
-                pos = len = 0;
+                pos = len = brp = 0;
                 continue;
             }
             catch( ParseException e )
@@ -1981,6 +2102,9 @@ version(unittest)
         ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"],
         ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"],
         ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"],
+        // back references
+        ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference
+        ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference
     ];
 
     template staticIota(int x)

From ea596c53922f4684f1bc47a5f059023ad8a603d9 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Sun, 28 May 2017 22:37:02 +0200
Subject: [PATCH 03/13] core.demangle: add test cases for back references

---
 src/core/demangle.d | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index 1be88bf067..ac0b62b8de 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -2105,8 +2105,16 @@ version(unittest)
         // back references
         ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference
         ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference
+        ["_D3std11parallelism__T4TaskS8unittest3cmpTAyaTQeZQBb6__dtorMFNfZv",
+        "@safe void std.parallelism.Task!(unittest.cmp, immutable(char)[], immutable(char)[]).Task.__dtor()"],
+        // 1.s.s.foo from https://issues.dlang.org/show_bug.cgi?id=15831
+        ["_D13testexpansion44__T1sTS13testexpansion8__T1sTiZ1sFiZ6ResultZ1sFS13testexpansion8__T1sTiZ1sFiZ6ResultZ6Result3fooMFNaNfZv",
+         "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
+        ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv",
+         "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
     ];
 
+
     template staticIota(int x)
     {
         template Seq(T...){ alias Seq = T; }

From b8dcb052fa5779ef8f997f7890c90fa472f115bf Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Fri, 2 Jun 2017 22:41:26 +0200
Subject: [PATCH 04/13] demangle: improve performance for longer symbols

---
 src/core/demangle.d | 22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index ac0b62b8de..255f53bf27 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -165,27 +165,21 @@ private struct Demangle
     }
 
 
+    // move val to the end of the dst buffer
     char[] shift( const(char)[] val )
     {
-        void exch( size_t a, size_t b )
-        {
-            char t = dst[a];
-            dst[a] = dst[b];
-            dst[b] = t;
-        }
-
         if( val.length && !mute )
         {
             assert( contains( dst[0 .. len], val ) );
             debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr );
 
-            for( size_t n = 0; n < val.length; n++ )
-            {
-                for( size_t v = val.ptr - dst.ptr; v + 1 < len; v++ )
-                {
-                    exch( v, v + 1 );
-                }
-            }
+            if (len + val.length > dst.length)
+                overflow();
+            size_t v = val.ptr - dst.ptr;
+            dst[len .. len + val.length] = val[];
+            for (size_t p = v; p < len; p++)
+                dst[p] = dst[p + val.length];
+
             return dst[len - val.length .. len];
         }
         return null;

From f7f87ac45b995e5c5f5a7af3c7ee0629c24804a1 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Fri, 2 Jun 2017 22:51:56 +0200
Subject: [PATCH 05/13] fix issue 14576: backtrack on ambiguous template alias
 parameter encoding, support no full length specified

---
 src/core/demangle.d | 52 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 45 insertions(+), 7 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index 255f53bf27..aa8725a1d9 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -1485,6 +1485,7 @@ private struct Demangle
     TemplateArgX:
         T Type
         V Type Value
+        S Number_opt QualifiedName
         S LName
     */
     void parseTemplateArgs()
@@ -1492,6 +1493,7 @@ private struct Demangle
         debug(trace) printf( "parseTemplateArgs+\n" );
         debug(trace) scope(success) printf( "parseTemplateArgs-\n" );
 
+    L_nextArg:
         for( size_t n = 0; true; n++ )
         {
             if( front == 'H' )
@@ -1540,7 +1542,32 @@ private struct Demangle
                         debug(trace) printf( "not a mangled name arg\n" );
                     }
                 }
-
+                if( isDigit( front ) && isDigit( peek( 1 ) ) )
+                {
+                    // ambiguity: length followed by qualified name (starting with number)
+                    // try all possible pairs of numbers
+                    auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName
+                    pos--;
+                    auto l = len;
+                    auto p = pos;
+                    auto b = brp;
+                    while( qlen > 0 )
+                    {
+                        try
+                        {
+                            parseQualifiedName();
+                            if( pos == p + qlen )
+                                continue L_nextArg;
+                        }
+                        catch( ParseException e )
+                        {
+                        }
+                        qlen /= 10; // retry with one digit less
+                        pos = --p;
+                        len = l;
+                        brp = b;
+                    }
+                }
                 parseQualifiedName();
                 continue;
             default:
@@ -1557,11 +1584,20 @@ private struct Demangle
 
         auto p = pos;
         scope(exit) pos = p;
-        auto n = decodeNumber();
-        return n >= 4 &&
-           pos < buf.length && '_' == buf[pos++] &&
-           pos < buf.length && 'D' == buf[pos++] &&
-           isDigit(buf[pos]);
+        if ( isDigit( buf[pos] ) )
+        {
+            auto n = decodeNumber();
+            return n >= 4 &&
+                pos < buf.length && '_' == buf[pos++] &&
+                pos < buf.length && 'D' == buf[pos++] &&
+                isDigit( buf[pos] );
+        }
+        else
+        {
+            return pos < buf.length && '_' == buf[pos++] &&
+                   pos < buf.length && 'D' == buf[pos++] &&
+                   isSymbolNameFront();
+        }
     }
 
 
@@ -1570,7 +1606,9 @@ private struct Demangle
         debug(trace) printf( "parseMangledNameArg+\n" );
         debug(trace) scope(success) printf( "parseMangledNameArg-\n" );
 
-        auto n = decodeNumber();
+        size_t n = 0;
+        if ( isDigit( front ) )
+            n = decodeNumber();
         parseMangledName( n );
     }
 

From 0f564d62e8e982f7e73ac662592198a7313348fb Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Fri, 2 Jun 2017 22:45:12 +0200
Subject: [PATCH 06/13] fix issues 14319, 14563, 17609, 17610, 17611: demangle:
 better detect function signatures in QualifiedNames, only print return types
 for the outmost symbol

---
 src/core/demangle.d | 247 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 186 insertions(+), 61 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index aa8725a1d9..c50abb3f57 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -185,6 +185,20 @@ private struct Demangle
         return null;
     }
 
+    // remove val from dst buffer
+    void remove( const(char)[] val )
+    {
+        if( val.length )
+        {
+            assert( contains( dst[0 .. len], val ) );
+            debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr );
+
+            size_t v = val.ptr - dst.ptr;
+            for (size_t p = v; p < len; p++)
+                dst[p] = dst[p + val.length];
+            len -= val.length;
+        }
+    }
 
     char[] append( const(char)[] val )
     {
@@ -891,10 +905,19 @@ private struct Demangle
             return dst[beg .. len];
         case 'D': // TypeDelegate (D TypeFunction)
             popFront();
+            auto modbeg = len;
+            parseModifier();
+            auto modend = len;
             if( front == 'Q' )
                 parseBackrefType( () => parseTypeFunction( name, IsDelegate.yes ) );
             else
                 parseTypeFunction( name, IsDelegate.yes );
+            if (modend > modbeg)
+            {
+                // move modifiers behind the function arguments
+                shift(dst[modend-1 .. modend]); // trailing space
+                shift(dst[modbeg .. modend-1]);
+            }
             return dst[beg .. len];
         case 'n': // TypeNone (n)
             popFront();
@@ -1049,6 +1072,39 @@ private struct Demangle
         }
     }
 
+    void parseModifier()
+    {
+        switch( front )
+        {
+        case 'y':
+            popFront();
+            put( "immutable " );
+            break;
+        case 'O':
+            popFront();
+            put( "shared " );
+            if( front == 'x' )
+                goto case 'x';
+            if( front == 'N' )
+                goto case 'N';
+            break;
+        case 'N':
+            if( peek( 1 ) != 'g' )
+                break;
+            popFront();
+            popFront();
+            put( "inout " );
+            if( front == 'x' )
+                goto case 'x';
+            break;
+        case 'x':
+            popFront();
+            put( "const " );
+            break;
+        default: break;
+        }
+    }
+
     void parseFuncAttr()
     {
         // FuncAttrs
@@ -1174,7 +1230,11 @@ private struct Demangle
     }
 
     enum IsDelegate { no, yes }
-    // returns the argument list with the left parenthesis, but not the right
+
+    /*
+        TypeFunction:
+            CallConvention FuncAttrs Arguments ArgClose Type
+    */
     char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no )
     {
         debug(trace) printf( "parseTypeFunction+\n" );
@@ -1182,33 +1242,40 @@ private struct Demangle
         auto beg = len;
 
         parseCallConvention();
+        auto attrbeg = len;
         parseFuncAttr();
 
-        beg = len;
+        auto argbeg = len;
         put( '(' );
-        scope(success)
+        parseFuncArguments();
+        put( ')' );
+        if (attrbeg < argbeg)
         {
-            put( ')' );
-            auto t = len;
-            parseType();
-            put( ' ' );
-            if( name.length )
+            // move function attributes behind arguments
+            shift( dst[argbeg - 1 .. argbeg] ); // trailing space
+            shift( dst[attrbeg .. argbeg - 1] ); // attributes
+            argbeg = attrbeg;
+        }
+        auto retbeg = len;
+        parseType();
+        put( ' ' );
+        // append name/delegate/function
+        if( name.length )
+        {
+            if( !contains( dst[0 .. len], name ) )
+                put( name );
+            else if( shift( name ).ptr != name.ptr )
             {
-                if( !contains( dst[0 .. len], name ) )
-                    put( name );
-                else if( shift( name ).ptr != name.ptr )
-                {
-                    beg -= name.length;
-                    t -= name.length;
-                }
+                argbeg -= name.length;
+                retbeg -= name.length;
             }
-            else if( IsDelegate.yes == isdg )
-                put( "delegate" );
-            else
-                put( "function" );
-            shift( dst[beg .. t] );
         }
-        parseFuncArguments();
+        else if( IsDelegate.yes == isdg )
+            put( "delegate" );
+        else
+            put( "function" );
+        // move arguments and attributes behind name
+        shift( dst[argbeg .. retbeg] );
         return dst[beg..len];
     }
 
@@ -1609,7 +1676,7 @@ private struct Demangle
         size_t n = 0;
         if ( isDigit( front ) )
             n = decodeNumber();
-        parseMangledName( n );
+        parseMangledName( false, n );
     }
 
 
@@ -1702,46 +1769,52 @@ private struct Demangle
         }
     }
 
-    void parseFunctionType( bool muteReturnType )
+    // parse optional function arguments as part of a symbol name, i.e without return type
+    // if keepAttr, the calling convention and function attributes are not discarded, but returned
+    char[] parseFunctionTypeNoReturn( bool keepAttr = false )
     {
-        if( 'M' == front )
-        {
-            // do not emit "needs this"
-            popFront();
-        }
-        if( isCallConvention( front ) )
-        {
-            // try to demangle a function, in case we are pointing to some function local
-            auto prevpos = pos;
-            auto prevlen = len;
-
-            // we don't want calling convention and attributes in the qualified name
-            parseCallConvention();
-            parseFuncAttr();
-            len = prevlen;
+        // try to demangle a function, in case we are pointing to some function local
+        auto prevpos = pos;
+        auto prevlen = len;
+        auto prevbrp = brp;
 
-            put( '(' );
-            parseFuncArguments();
-            put( ')' );
-            if( !isSymbolNameFront() ) // voldemort types don't have a return type on the function
+        char[] attr;
+        try
+        {
+            if( 'M' == front )
             {
-                auto funclen = len;
-
-                bool prevMute = mute;
-                scope(exit) mute = prevMute;
-                mute = muteReturnType;
-                parseType();
-
-                if( !muteReturnType && !isSymbolNameFront() )
+                // do not emit "needs this"
+                popFront();
+                parseModifier();
+            }
+            if( isCallConvention( front ) )
+            {
+                // we don't want calling convention and attributes in the qualified name
+                parseCallConvention();
+                parseFuncAttr();
+                if( keepAttr )
                 {
-                    // not part of a qualified name, so back up
-                    pos = prevpos;
-                    len = prevlen;
+                    attr = dst[prevlen .. len];
                 }
                 else
-                    len = funclen; // remove return type from qualified name
+                {
+                    len = prevlen;
+                }
+
+                put( '(' );
+                parseFuncArguments();
+                put( ')' );
             }
         }
+        catch( ParseException )
+        {
+            // not part of a qualified name, so back up
+            pos = prevpos;
+            len = prevlen;
+            brp = prevbrp;
+            attr = null;
+        }
+        return attr;
     }
 
     /*
@@ -1761,7 +1834,8 @@ private struct Demangle
             if( n++ )
                 put( '.' );
             parseSymbolName();
-            parseFunctionType( false );
+            parseFunctionTypeNoReturn();
+
         } while( isSymbolNameFront() );
         return dst[beg .. len];
     }
@@ -1772,7 +1846,7 @@ private struct Demangle
         _D QualifiedName Type
         _D QualifiedName M Type
     */
-    void parseMangledName(size_t n = 0)
+    void parseMangledName( bool displayType, size_t n = 0 )
     {
         debug(trace) printf( "parseMangledName+\n" );
         debug(trace) scope(success) printf( "parseMangledName-\n" );
@@ -1784,18 +1858,68 @@ private struct Demangle
         match( 'D' );
         do
         {
-            name = parseQualifiedName();
+            size_t  beg = len;
+            size_t  nameEnd = len;
+            char[] attr;
+            do
+            {
+                if( attr )
+                    remove( attr ); // dump attributes of parent symbols
+                if( beg != len )
+                    put( '.' );
+                parseSymbolName();
+                nameEnd = len;
+                attr = parseFunctionTypeNoReturn( displayType );
+
+            } while( isSymbolNameFront() );
+
+            if( displayType )
+            {
+                attr = shift( attr );
+                nameEnd = len - attr.length;  // name includes function arguments
+            }
+            name = dst[beg .. nameEnd];
+
             debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.ptr );
             if( 'M' == front )
                 popFront(); // has 'this' pointer
-            if( AddType.yes == addType )
-                parseType( name );
+
+            auto lastlen = len;
+            auto type = parseType();
+            if( displayType )
+            {
+                if( type.length )
+                    put( ' ' );
+                // sort (name,attr,type) -> (attr,type,name)
+                shift( name );
+            }
+            else
+            {
+                // remove type
+                assert( attr.length == 0 );
+                len = lastlen;
+            }
             if( pos >= buf.length || (n != 0 && pos >= end) )
                 return;
+
+            switch( front )
+            {
+            case 'T': // terminators when used as template alias parameter
+            case 'V':
+            case 'S':
+            case 'Z':
+                return;
+            default:
+            }
             put( '.' );
+
         } while( true );
     }
 
+    void parseMangledName()
+    {
+        parseMangledName( AddType.yes == addType );
+    }
 
     char[] doDemangle(alias FUNC)()
     {
@@ -2081,7 +2205,8 @@ version(unittest)
         ["_D4test3fooAa", "char[] test.foo"],
         ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"],
         ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"],
-        ["_D4test2dgDFiYd", "double test.dg(int, ...)"],
+        ["_D4test2dgDFiYd", "double delegate(int, ...) test.dg"],
+        ["_D4test2dgDxFNfiYd", "double delegate(int, ...) @safe const test.dg"],
         //["_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
         //["_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
         ["_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(\"abc\"w, \"def\"d).x"],
@@ -2117,7 +2242,7 @@ version(unittest)
         ["_D8demangle4testFNhG4fZv", "void demangle.test(__vector(float[4]))"],
         ["_D8demangle4testFNhG2dZv", "void demangle.test(__vector(double[2]))"],
         ["_D8demangle4testFNhG4fNhG4fZv", "void demangle.test(__vector(float[4]), __vector(float[4]))"],
-        ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(int bug11192.main().bar).foo()"],
+        ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(bug11192.main().bar).foo()"],
         ["_D13libd_demangle12__ModuleInfoZ", "libd_demangle.__ModuleInfo"],
         ["_D15TypeInfo_Struct6__vtblZ", "TypeInfo_Struct.__vtbl"],
         ["_D3std5stdio12__ModuleInfoZ", "std.stdio.__ModuleInfo"],

From 7a92a41d437c6cfe24f0fa46bf7f154401f037f8 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Fri, 2 Jun 2017 22:59:23 +0200
Subject: [PATCH 07/13] support template aliases to externally mangled symbols
 with 'X' instead of 'S'

---
 src/core/demangle.d | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index c50abb3f57..6772fde6cb 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -1553,7 +1553,7 @@ private struct Demangle
         T Type
         V Type Value
         S Number_opt QualifiedName
-        S LName
+        X ExternallyMangledName
     */
     void parseTemplateArgs()
     {
@@ -1637,6 +1637,11 @@ private struct Demangle
                 }
                 parseQualifiedName();
                 continue;
+            case 'X':
+                popFront();
+                putComma(n);
+                parseLName();
+                continue;
             default:
                 return;
             }
@@ -2269,6 +2274,8 @@ version(unittest)
          "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
         ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv",
          "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
+        ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter
+         "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"],
     ];
 
 

From fb6ebcfffa40ebf59aae51e1fbe2f5e113793847 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Fri, 2 Jun 2017 23:00:05 +0200
Subject: [PATCH 08/13] demangle with back references: add test cases

---
 src/core/demangle.d | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index 6772fde6cb..9f1281058b 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -2264,6 +2264,13 @@ version(unittest)
         ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"],
         ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"],
         ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"],
+        ["_D3std5range15__T4iotaTtTtTtZ4iotaFtttZ6Result7opIndexMNgFNaNbNiNfmZNgt",
+         "inout pure nothrow @nogc @safe inout(ushort) std.range.iota!(ushort, ushort, ushort).iota(ushort, ushort, ushort).Result.opIndex(ulong)"],
+        ["_D3std6format77__T6getNthVAyaa13_696e7465676572207769647468S233std6traits10isIntegralTiTkTkZ6getNthFNaNfkkkZi",
+         "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"],
+        ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv",
+         "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"],
+
         // back references
         ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference
         ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference
@@ -2274,8 +2281,37 @@ version(unittest)
          "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
         ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv",
          "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
+        // ambiguity on 'V', template value argument or pascal function
+        ["_D3std4conv__T7enumRepTyAaTEQBa12experimental9allocator15building_blocks15stats_collector7OptionsVQCti64ZQDnyQDh",
+         "immutable(char[]) std.conv.enumRep!(immutable(char[]), std.experimental.allocator.building_blocks.stats_collector.Options, 64).enumRep"],
+        // symbol back reference to location with symbol back reference
+        ["_D3std12experimental9allocator6common__T10reallocateTSQCaQBzQBo15building_blocks17kernighan_ritchie__T8KRRegionTSQEhQEgQDvQCh14null_allocator13NullAllocatorZQCdZQErFNaNbNiKQEpKAvmZb",
+         "pure nothrow @nogc bool std.experimental.allocator.common.reallocate!(std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!("
+        ~"std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion).reallocate(ref "
+        ~"std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion, ref void[], ulong)"],
+        ["_D3std9exception__T11doesPointToTASQBh5regex8internal2ir10NamedGroupTQBkTvZQCeFNaNbNiNeKxASQDlQCeQCbQBvQBvKxQtZb",
+         "pure nothrow @nogc @trusted bool std.exception.doesPointTo!(std.regex.internal.ir.NamedGroup[], "
+        ~"std.regex.internal.ir.NamedGroup[], void).doesPointTo(ref const(std.regex.internal.ir.NamedGroup[]), ref const(std.regex.internal.ir.NamedGroup[]))"],
+        ["_D3std9algorithm9iteration__T14SplitterResultS_DQBu3uni7isWhiteFNaNbNiNfwZbTAyaZQBz9__xtoHashFNbNeKxSQDvQDuQDn__TQDgS_DQEnQCtQCsQCnTQCeZQEdZm",
+         "nothrow @trusted ulong std.algorithm.iteration.SplitterResult!(std.uni.isWhite(dchar), immutable(char)[]).SplitterResult."
+        ~"__xtoHash(ref const(std.algorithm.iteration.SplitterResult!(std.uni.isWhite, immutable(char)[]).SplitterResult))"],
+        ["_D3std8typecons__T7TypedefTCQBaQz19__unittestL6513_208FNfZ7MyClassVQBonVAyanZQCh6__ctorMFNaNbNcNiNfQCuZSQDyQDx__TQDrTQDmVQDqnVQCcnZQEj",
+         "pure nothrow ref @nogc @safe std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef "
+        ~"std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef.__ctor(std.typecons.__unittestL6513_208().MyClass)"],
+        ["_D3std6getopt__TQkTAyaTDFNaNbNiNfQoZvTQtTDQsZQBnFNfKAQBiQBlQBkQBrQyZSQCpQCo12GetoptResult",
+         "@safe std.getopt.GetoptResult std.getopt.getopt!(immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
+        ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)."
+        ~"getopt(ref immutable(char)[][], immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
+        ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)"],
+        ["_D3std5regex8internal9kickstart__T7ShiftOrTaZQl11ShiftThread__T3setS_DQCqQCpQCmQCg__TQBzTaZQCfQBv10setInvMaskMFNaNbNiNfkkZvZQCjMFNaNfwZv",
+         "pure @safe void std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.set!(std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.setInvMask(uint, uint)).set(dchar)"],
         ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter
          "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"],
+        // back reference for type in template AA parameter value
+        ["_D3std9algorithm9iteration__T12FilterResultSQBq8typecons__T5TupleTiVAyaa1_61TiVQla1_62TiVQva1_63ZQBm__T6renameVHiQBtA2i0a1_63i2a1_61ZQBeMFNcZ9__lambda1TAiZQEw9__xtoHashFNbNeKxSQGsQGrQGk__TQGdSQHiQFs__TQFmTiVQFja1_61TiVQFua1_62TiVQGfa1_63ZQGx__TQFlVQFhA2i0a1_63i2a1_61ZQGjMFNcZQFfTQEyZQJvZm",
+         `nothrow @trusted ulong std.algorithm.iteration.FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").`
+        ~`Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult.__xtoHash(ref const(std.algorithm.iteration.`
+        ~`FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult))`],
     ];
 
 

From f54956f7bd4dcffe5a09a73f788775f8120545e4 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Thu, 22 Jun 2017 08:46:55 +0200
Subject: [PATCH 09/13] purify Demangle

---
 src/core/demangle.d | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index 9f1281058b..06c9b9f656 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -43,7 +43,7 @@ private struct Demangle
     //       allocation during the course of a parsing run, this is still
     //       faster than assembling the result piecemeal.
 
-
+pure:
     enum AddType { no, yes }
 
 
@@ -524,10 +524,7 @@ private struct Demangle
 
         tbuf[tlen] = 0;
         debug(info) printf( "got (%s)\n", tbuf.ptr );
-        import core.stdc.stdlib : strtold;
-        val = strtold( tbuf.ptr, null );
-        import core.stdc.stdio : snprintf;
-        tlen = snprintf( tbuf.ptr, tbuf.length, "%#Lg", val );
+        pureReprintReal( tbuf.ptr, tbuf.length );
         debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr );
         put( tbuf[0 .. tlen] );
     }
@@ -795,7 +792,7 @@ private struct Demangle
         auto beg = len;
         auto t = front;
 
-        char[] parseBackrefType(scope char[] delegate() parseDg)
+        char[] parseBackrefType(scope char[] delegate() pure parseDg) pure
         {
             if (pos == brp)
                 error("recursive back reference");
@@ -2386,3 +2383,23 @@ string decodeDmdString( const(char)[] ln, ref size_t p )
     }
     return s;
 }
+
+// locally purified for internal use here only
+extern (C) private
+{
+    pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") real pureReprintReal(char* nptr, size_t len);
+
+    char* fakePureReprintReal(char* nptr, size_t len)
+    {
+        import core.stdc.stdlib : strtold;
+        import core.stdc.stdio : snprintf;
+        import core.stdc.errno : errno;
+
+        const err = errno;
+        real val = strtold(nptr, null);
+        len = snprintf(nptr, len, "%#Lg", val);
+        errno = err;
+
+        return nptr;
+    }
+}

From 92cee12b8ac8c5bbc97b0ecdec0500df34e6cd26 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Thu, 22 Jun 2017 08:48:43 +0200
Subject: [PATCH 10/13] use DbI to reencode mangled string with identifier back
 references

---
 src/core/demangle.d | 213 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 207 insertions(+), 6 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index 06c9b9f656..191dfae571 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -24,7 +24,14 @@ else version (WatchOS)
 debug(trace) import core.stdc.stdio : printf;
 debug(info) import core.stdc.stdio : printf;
 
-private struct Demangle
+private struct NoHooks
+{
+    // supported hooks
+    // static bool parseLName(ref Demangle);
+    // static char[] parseType(ref Demangle, char[])
+}
+
+private struct Demangle(Hooks = NoHooks)
 {
     // NOTE: This implementation currently only works with mangled function
     //       names as they exist in an object file.  Type names mangled via
@@ -71,6 +78,7 @@ pure:
     size_t          brp     = 0; // current back reference pos
     AddType         addType = AddType.yes;
     bool            mute    = false;
+    Hooks           hooks;
 
     static class ParseException : Exception
     {
@@ -555,6 +563,10 @@ pure:
         debug(trace) printf( "parseLName+\n" );
         debug(trace) scope(success) printf( "parseLName-\n" );
 
+        static if(__traits(hasMember, Hooks, "parseLName"))
+            if (hooks.parseLName(this))
+                return;
+
         if( front == 'Q' )
         {
             // back reference to LName
@@ -787,6 +799,10 @@ pure:
             "dchar", // w
         ];
 
+        static if (__traits(hasMember, Hooks, "parseType"))
+            if (auto n = hooks.parseType(this, name))
+                return n;
+
         debug(trace) printf( "parseType+\n" );
         debug(trace) scope(success) printf( "parseType-\n" );
         auto beg = len;
@@ -1986,7 +2002,7 @@ pure:
 char[] demangle( const(char)[] buf, char[] dst = null )
 {
     //return Demangle(buf, dst)();
-    auto d = Demangle(buf, dst);
+    auto d = Demangle!()(buf, dst);
     return d.demangleName();
 }
 
@@ -2004,10 +2020,178 @@ char[] demangle( const(char)[] buf, char[] dst = null )
 */
 char[] demangleType( const(char)[] buf, char[] dst = null )
 {
-    auto d = Demangle(buf, dst);
+    auto d = Demangle!()(buf, dst);
     return d.demangleType();
 }
 
+/**
+* reencode a mangled symbol name that might include duplicate occurrences
+* of the same identifier by replacing all but the first occurence with
+* a back reference.
+*
+* Params:
+*  mangled = The mangled string representing the type
+*
+* Returns:
+*  The mangled name with deduplicated identifiers
+*/
+char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
+{
+    static struct PrependHooks
+    {
+        size_t lastpos;
+        char[] result;
+        size_t[const(char)[]] idpos; // identifier positions
+
+        static struct Replacement
+        {
+            size_t pos;    // postion in original mangled string
+            size_t respos; // postion in result string
+        }
+        Replacement [] replacements;
+
+    pure:
+        size_t positionInResult(size_t pos)
+        {
+            foreach_reverse (r; replacements)
+                if (pos >= r.pos)
+                    return r.respos + pos - r.pos;
+            return pos;
+        }
+
+        alias Remangle = Demangle!(PrependHooks);
+
+        void flushPosition(ref Remangle d)
+        {
+            if (lastpos < d.pos)
+            {
+                result ~= d.buf[lastpos .. d.pos];
+            }
+            else if (lastpos > d.pos)
+            {
+                // roll back to earlier position
+                while (replacements.length > 0 && replacements[$-1].pos > d.pos)
+                    replacements = replacements[0 .. $-1];
+
+                if (replacements.length > 0)
+                    result.length = replacements[$-1].respos + d.pos - replacements[$-1].pos;
+                else
+                    result.length = d.pos;
+            }
+        }
+
+        bool parseLName(ref Remangle d)
+        {
+            flushPosition(d);
+
+            auto reslen = result.length;
+            auto refpos = d.pos;
+            if (d.front == 'Q')
+            {
+                size_t npos;
+                {
+                    scope(exit) result.length = reslen; // remove all intermediate additions
+                    // only support identifier back references
+                    d.popFront();
+                    size_t n = d.decodeBackref();
+                    if (!n || n > refpos)
+                        d.error("invalid back reference");
+
+                    auto savepos = d.pos;
+                    scope(exit) d.pos = savepos;
+                    size_t srcpos = refpos - n;
+
+                    auto idlen = d.decodeNumber();
+                    if (d.pos + idlen > d.buf.length)
+                        d.error("invalid back reference");
+                    auto id = d.buf[d.pos .. d.pos + idlen];
+                    auto pid = id in idpos;
+                    if (!pid)
+                        d.error("invalid back reference");
+                    npos = positionInResult(*pid);
+                }
+                encodeBackref(reslen - npos);
+                replacements ~= Replacement(d.pos, result.length);
+            }
+            else
+            {
+                auto n = d.decodeNumber();
+                if(!n || n > d.buf.length || n > d.buf.length - d.pos)
+                    d.error("LName too shot or too long");
+                auto id = d.buf[d.pos .. d.pos + n];
+                d.pos += n;
+                if (auto pid = id in idpos)
+                {
+                    size_t npos = positionInResult(*pid);
+                    result.length = reslen;
+                    encodeBackref(reslen - npos);
+                    replacements ~= Replacement(d.pos, result.length);
+                }
+                else
+                {
+                    idpos[id] = refpos;
+                    result ~= d.buf[refpos .. d.pos];
+                }
+            }
+            lastpos = d.pos;
+            return true;
+        }
+
+        char[] parseType( ref Remangle d, char[] name = null )
+        {
+            if (d.front != 'Q')
+                return null;
+
+            flushPosition(d);
+
+            auto refPos = d.pos;
+            d.popFront();
+            auto n = d.decodeBackref();
+            if (n == 0 || n > refPos)
+                d.error("invalid back reference");
+
+            size_t npos = positionInResult(refPos - n);
+            size_t reslen = result.length;
+            encodeBackref(reslen - npos);
+
+            lastpos = d.pos;
+            return result[reslen .. $]; // anything but null
+        }
+
+        void encodeBackref(size_t relpos)
+        {
+            result ~= 'Q';
+            enum base = 26;
+            size_t div = 1;
+            while (relpos >= div * base)
+                div *= base;
+            while (div >= base)
+            {
+                auto dig = (relpos / div);
+                result ~= cast(char)('A' + dig);
+                relpos -= dig * div;
+                div /= base;
+            }
+            result ~= cast(char)('a' + relpos);
+        }
+    }
+
+    auto d = Demangle!(PrependHooks)(mangled, null);
+    d.hooks = PrependHooks();
+    d.mute = true; // no demangled output
+    try
+    {
+        () @trusted { d.parseMangledName(); }();
+        if (d.hooks.lastpos < d.pos)
+            d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos];
+        return d.hooks.result;
+    }
+    catch(Exception)
+    {
+        // overflow exception cannot occur
+        return mangled.dup;
+    }
+}
 
 /**
  * Mangles a D symbol.
@@ -2069,7 +2253,11 @@ char[] mangle(T)(const(char)[] fqn, char[] dst = null) @safe pure nothrow
     }
     dst[i .. i + T.mangleof.length] = T.mangleof[];
     i += T.mangleof.length;
-    return dst[0 .. i];
+
+    static if(hasTypeBackRef)
+        return reencodeMangled(dst[0 .. i]);
+    else
+        return dst[0 .. i];
 }
 
 
@@ -2129,12 +2317,25 @@ char[] mangleFunc(T:FT*, FT)(const(char)[] fqn, char[] dst = null) @safe pure no
     }
 }
 
+private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi";
 
 ///
 unittest
 {
     assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi");
-    assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFC6ObjectZi");
+    static if(hasTypeBackRef)
+    {
+        assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsZi");
+        assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsQdZi");
+    }
+    else
+    {
+        auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals");
+        assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi");
+        assert(reencodeMangled(mngl) == "_D6object6Object8opEqualsFCQtZi");
+    }
+    // trigger back tracking with ambiguity on '__T', template or identifier
+    assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi");
 }
 
 unittest
@@ -2373,7 +2574,7 @@ string decodeDmdString( const(char)[] ln, ref size_t p )
                 break;
             s ~= s[$ - zpos .. $ - zpos + zlen];
         }
-        else if( Demangle.isAlpha(cast(char)ch) || Demangle.isDigit(cast(char)ch) || ch == '_' )
+        else if( Demangle!().isAlpha(cast(char)ch) || Demangle!().isDigit(cast(char)ch) || ch == '_' )
             s ~= cast(char) ch;
         else
         {

From dc455719c50a433a3720bdeade0912eb386e5845 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Mon, 26 Jun 2017 08:30:52 +0200
Subject: [PATCH 11/13] support LName with length 0 as "__anonymous"

---
 src/core/demangle.d | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index 191dfae571..ffd14fe69c 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -585,7 +585,12 @@ pure:
             return;
         }
         auto n = decodeNumber();
-        if( !n || n > buf.length || n > buf.length - pos )
+        if( n == 0 )
+        {
+            put( "__anonymous" );
+            return;
+        }
+        if( n > buf.length || n > buf.length - pos )
             error( "LName must be at least 1 character" );
         if( '_' != front && !isAlpha( front ) )
             error( "Invalid character in LName" );
@@ -2332,7 +2337,8 @@ unittest
     {
         auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals");
         assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi");
-        assert(reencodeMangled(mngl) == "_D6object6Object8opEqualsFCQtZi");
+        auto remngl = reencodeMangled(mngl);
+        assert(remngl == "_D6object6Object8opEqualsFCQsZi");
     }
     // trigger back tracking with ambiguity on '__T', template or identifier
     assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi");
@@ -2468,6 +2474,10 @@ version(unittest)
          "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"],
         ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv",
          "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"],
+        // Lname '0'
+        ["_D3std9algorithm9iteration__T9MapResultSQBmQBlQBe005stripTAAyaZQBi7opSliceMFNaNbNiNfmmZSQDiQDhQDa__TQCtSQDyQDxQDq00QCmTQCjZQDq",
+         "pure nothrow @nogc @safe std.algorithm.iteration.MapResult!(std.algorithm.iteration.__anonymous.strip, "
+        ~"immutable(char)[][]).MapResult std.algorithm.iteration.MapResult!(std.algorithm.iteration.strip, immutable(char)[][]).MapResult.opSlice(ulong, ulong)"],
 
         // back references
         ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference

From 2e37769507517fb5f6600172e4ed8548c713a3df Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Mon, 3 Jul 2017 09:00:30 +0200
Subject: [PATCH 12/13] fix issue 16664 - restrict trusted code to a few small
 functions and make demangler @safe, pure and nothrow

---
 src/core/demangle.d | 58 +++++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 28 deletions(-)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index ffd14fe69c..d205b7d541 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -50,7 +50,7 @@ private struct Demangle(Hooks = NoHooks)
     //       allocation during the course of a parsing run, this is still
     //       faster than assembling the result piecemeal.
 
-pure:
+pure @safe:
     enum AddType { no, yes }
 
 
@@ -98,7 +98,7 @@ pure:
     }
 
 
-    static void error( string msg = "Invalid symbol" )
+    static void error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */
     {
         //throw new ParseException( msg );
         debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
@@ -108,7 +108,7 @@ pure:
     }
 
 
-    static void overflow( string msg = "Buffer overflow" )
+    static void overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */
     {
         //throw new OverflowException( msg );
         debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr );
@@ -161,7 +161,7 @@ pure:
     //////////////////////////////////////////////////////////////////////////
 
 
-    static bool contains( const(char)[] a, const(char)[] b )
+    static bool contains( const(char)[] a, const(char)[] b ) @trusted
     {
         if (a.length && b.length)
         {
@@ -183,7 +183,7 @@ pure:
 
             if (len + val.length > dst.length)
                 overflow();
-            size_t v = val.ptr - dst.ptr;
+            size_t v = &val[0] - &dst[0];
             dst[len .. len + val.length] = val[];
             for (size_t p = v; p < len; p++)
                 dst[p] = dst[p + val.length];
@@ -201,7 +201,7 @@ pure:
             assert( contains( dst[0 .. len], val ) );
             debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr );
 
-            size_t v = val.ptr - dst.ptr;
+            size_t v = &val[0] - &dst[0];
             for (size_t p = v; p < len; p++)
                 dst[p] = dst[p + val.length];
             len -= val.length;
@@ -217,7 +217,7 @@ pure:
             assert( !contains( dst[0 .. len], val ) );
             debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
 
-            if( dst.ptr + len == val.ptr &&
+            if( &dst[len] == &val[0] &&
                 dst.length - len >= val.length )
             {
                 // data is already in place
@@ -532,7 +532,7 @@ pure:
 
         tbuf[tlen] = 0;
         debug(info) printf( "got (%s)\n", tbuf.ptr );
-        pureReprintReal( tbuf.ptr, tbuf.length );
+        pureReprintReal( tbuf[] );
         debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr );
         put( tbuf[0 .. tlen] );
     }
@@ -813,7 +813,7 @@ pure:
         auto beg = len;
         auto t = front;
 
-        char[] parseBackrefType(scope char[] delegate() pure parseDg) pure
+        char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe
         {
             if (pos == brp)
                 error("recursive back reference");
@@ -1977,15 +1977,19 @@ pure:
                 dst[0 .. buf.length] = buf[];
                 return dst[0 .. buf.length];
             }
+            catch( Exception e )
+            {
+                assert( false ); // no other exceptions thrown
+            }
         }
     }
 
-    char[] demangleName()
+    char[] demangleName() nothrow
     {
         return doDemangle!parseMangledName();
     }
 
-    char[] demangleType()
+    char[] demangleType() nothrow
     {
         return doDemangle!parseType();
     }
@@ -2004,7 +2008,7 @@ pure:
  *  The demangled name or the original string if the name is not a mangled D
  *  name.
  */
-char[] demangle( const(char)[] buf, char[] dst = null )
+char[] demangle( const(char)[] buf, char[] dst = null ) nothrow pure @safe
 {
     //return Demangle(buf, dst)();
     auto d = Demangle!()(buf, dst);
@@ -2023,7 +2027,7 @@ char[] demangle( const(char)[] buf, char[] dst = null )
  *  The demangled type name or the original string if the name is not a
  *  mangled D type.
 */
-char[] demangleType( const(char)[] buf, char[] dst = null )
+char[] demangleType( const(char)[] buf, char[] dst = null ) nothrow pure @safe
 {
     auto d = Demangle!()(buf, dst);
     return d.demangleType();
@@ -2055,7 +2059,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
         }
         Replacement [] replacements;
 
-    pure:
+    pure @safe:
         size_t positionInResult(size_t pos)
         {
             foreach_reverse (r; replacements)
@@ -2186,7 +2190,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
     d.mute = true; // no demangled output
     try
     {
-        () @trusted { d.parseMangledName(); }();
+        d.parseMangledName();
         if (d.hooks.lastpos < d.pos)
             d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos];
         return d.hooks.result;
@@ -2267,14 +2271,14 @@ char[] mangle(T)(const(char)[] fqn, char[] dst = null) @safe pure nothrow
 
 
 ///
-unittest
+@safe pure nothrow unittest
 {
     assert(mangle!int("a.b") == "_D1a1bi");
     assert(mangle!(char[])("test.foo") == "_D4test3fooAa");
     assert(mangle!(int function(int))("a.b") == "_D1a1bPFiZi");
 }
 
-unittest
+@safe pure nothrow unittest
 {
     static assert(mangle!int("a.b") == "_D1a1bi");
 
@@ -2325,7 +2329,7 @@ char[] mangleFunc(T:FT*, FT)(const(char)[] fqn, char[] dst = null) @safe pure no
 private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi";
 
 ///
-unittest
+@safe pure nothrow unittest
 {
     assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi");
     static if(hasTypeBackRef)
@@ -2344,7 +2348,7 @@ unittest
     assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi");
 }
 
-unittest
+@safe pure nothrow unittest
 {
     int function(lazy int[], ...) fp;
     assert(mangle!(typeof(fp))("demangle.test") == "_D8demangle4testPFLAiYi");
@@ -2368,7 +2372,7 @@ private template hasPlainMangling(FT) if (is(FT == function))
     enum hasPlainMangling = c == 'U' || c == 'V' || c == 'W';
 }
 
-unittest
+@safe pure nothrow unittest
 {
     static extern(D) void fooD();
     static extern(C) void fooC();
@@ -2533,7 +2537,7 @@ version(unittest)
             alias staticIota = Seq!(staticIota!(x - 1), x - 1);
     }
 }
-unittest
+@safe pure nothrow unittest
 {
     foreach( i, name; table )
     {
@@ -2553,7 +2557,7 @@ unittest
 /*
  *
  */
-string decodeDmdString( const(char)[] ln, ref size_t p )
+string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe
 {
     string s;
     uint zlen, zpos;
@@ -2598,19 +2602,17 @@ string decodeDmdString( const(char)[] ln, ref size_t p )
 // locally purified for internal use here only
 extern (C) private
 {
-    pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") real pureReprintReal(char* nptr, size_t len);
+    pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") void pureReprintReal(char[] nptr);
 
-    char* fakePureReprintReal(char* nptr, size_t len)
+    void fakePureReprintReal(char[] nptr)
     {
         import core.stdc.stdlib : strtold;
         import core.stdc.stdio : snprintf;
         import core.stdc.errno : errno;
 
         const err = errno;
-        real val = strtold(nptr, null);
-        len = snprintf(nptr, len, "%#Lg", val);
+        real val = strtold(nptr.ptr, null);
+        snprintf(nptr.ptr, nptr.length, "%#Lg", val);
         errno = err;
-
-        return nptr;
     }
 }

From 81a06e731780a420e76a5b037fb34aa9e625eaf6 Mon Sep 17 00:00:00 2001
From: Rainer Schuetze <r.sagitario@gmx.de>
Date: Sun, 16 Jul 2017 08:31:40 +0200
Subject: [PATCH 13/13] disable some inlining to tame dmd inliner, it causes
 huge compilation times when optimizing

---
 src/core/demangle.d | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/core/demangle.d b/src/core/demangle.d
index d205b7d541..e80f9197bd 100644
--- a/src/core/demangle.d
+++ b/src/core/demangle.d
@@ -100,6 +100,8 @@ pure @safe:
 
     static void error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */
     {
+        pragma(inline, false); // tame dmd inliner
+
         //throw new ParseException( msg );
         debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
         throw __ctfe ? new ParseException(msg)
@@ -110,6 +112,8 @@ pure @safe:
 
     static void overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */
     {
+        pragma(inline, false); // tame dmd inliner
+
         //throw new OverflowException( msg );
         debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr );
         throw cast(OverflowException) cast(void*) typeid(OverflowException).initializer;
@@ -176,6 +180,8 @@ pure @safe:
     // move val to the end of the dst buffer
     char[] shift( const(char)[] val )
     {
+        pragma(inline, false); // tame dmd inliner
+
         if( val.length && !mute )
         {
             assert( contains( dst[0 .. len], val ) );
@@ -196,6 +202,8 @@ pure @safe:
     // remove val from dst buffer
     void remove( const(char)[] val )
     {
+        pragma(inline, false); // tame dmd inliner
+
         if( val.length )
         {
             assert( contains( dst[0 .. len], val ) );
@@ -210,6 +218,8 @@ pure @safe:
 
     char[] append( const(char)[] val )
     {
+        pragma(inline, false); // tame dmd inliner
+
         if( val.length && !mute )
         {
             if( !dst.length )
@@ -252,6 +262,8 @@ pure @safe:
 
     char[] put( const(char)[] val )
     {
+        pragma(inline, false); // tame dmd inliner
+
         if( val.length )
         {
             if( !contains( dst[0 .. len], val ) )