1 /**
2  * Do mangling for C++ linkage for Digital Mars C++ and Microsoft Visual C++.
3  *
4  * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5  * Authors: Walter Bright, http://www.digitalmars.com
6  * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmanglewin.d, _cppmanglewin.d)
8  * Documentation:  https://dlang.org/phobos/dmd_cppmanglewin.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmanglewin.d
10  */
11 
12 module dmd.cppmanglewin;
13 
14 import core.stdc.string;
15 import core.stdc.stdio;
16 
17 import dmd.arraytypes;
18 import dmd.cppmangle : isPrimaryDtor, isCppOperator, CppOperator;
19 import dmd.dclass;
20 import dmd.declaration;
21 import dmd.denum : isSpecialEnumIdent;
22 import dmd.dstruct;
23 import dmd.dsymbol;
24 import dmd.dtemplate;
25 import dmd.errors;
26 import dmd.expression;
27 import dmd.func;
28 import dmd.globals;
29 import dmd.id;
30 import dmd.identifier;
31 import dmd.mtype;
32 import dmd.root.outbuffer;
33 import dmd.root.rootobject;
34 import dmd.target;
35 import dmd.tokens;
36 import dmd.typesem;
37 import dmd.visitor;
38 
39 extern (C++):
40 
41 
42 const(char)* toCppMangleMSVC(Dsymbol s)
43 {
44     scope VisualCPPMangler v = new VisualCPPMangler(!global.params.mscoff);
45     return v.mangleOf(s);
46 }
47 
48 const(char)* cppTypeInfoMangleMSVC(Dsymbol s)
49 {
50     //printf("cppTypeInfoMangle(%s)\n", s.toChars());
51     assert(0);
52 }
53 
54 /**
55  * Issues an ICE and returns true if `type` is shared or immutable
56  *
57  * Params:
58  *      type = type to check
59  *
60  * Returns:
61  *      true if type is shared or immutable
62  *      false otherwise
63  */
64 private bool checkImmutableShared(Type type)
65 {
66     if (type.isImmutable() || type.isShared())
67     {
68         error(Loc.initial, "Internal Compiler Error: `shared` or `immutable` types cannot be mapped to C++ (%s)", type.toChars());
69         fatal();
70         return true;
71     }
72     return false;
73 }
74 private final class VisualCPPMangler : Visitor
75 {
76     enum VC_SAVED_TYPE_CNT = 10u;
77     enum VC_SAVED_IDENT_CNT = 10u;
78 
79     alias visit = Visitor.visit;
80     Identifier[VC_SAVED_IDENT_CNT] saved_idents;
81     Type[VC_SAVED_TYPE_CNT] saved_types;
82 
83     // IS_NOT_TOP_TYPE: when we mangling one argument, we can call visit several times (for base types of arg type)
84     // but we must save only arg type:
85     // For example: if we have an int** argument, we should save "int**" but visit will be called for "int**", "int*", "int"
86     // This flag is set up by the visit(NextType, ) function  and should be reset when the arg type output is finished.
87     // MANGLE_RETURN_TYPE: return type shouldn't be saved and substituted in arguments
88     // IGNORE_CONST: in some cases we should ignore CV-modifiers.
89     // ESCAPE: toplevel const non-pointer types need a '$$C' escape in addition to a cv qualifier.
90 
91     enum Flags : int
92     {
93         IS_NOT_TOP_TYPE = 0x1,
94         MANGLE_RETURN_TYPE = 0x2,
95         IGNORE_CONST = 0x4,
96         IS_DMC = 0x8,
97         ESCAPE = 0x10,
98     }
99 
100     alias IS_NOT_TOP_TYPE = Flags.IS_NOT_TOP_TYPE;
101     alias MANGLE_RETURN_TYPE = Flags.MANGLE_RETURN_TYPE;
102     alias IGNORE_CONST = Flags.IGNORE_CONST;
103     alias IS_DMC = Flags.IS_DMC;
104     alias ESCAPE = Flags.ESCAPE;
105 
106     int flags;
107     OutBuffer buf;
108 
109     extern (D) this(VisualCPPMangler rvl)
110     {
111         flags |= (rvl.flags & IS_DMC);
112         saved_idents[] = rvl.saved_idents[];
113         saved_types[] = rvl.saved_types[];
114     }
115 
116 public:
117     extern (D) this(bool isdmc)
118     {
119         if (isdmc)
120         {
121             flags |= IS_DMC;
122         }
123         saved_idents[] = null;
124         saved_types[] = null;
125     }
126 
127     override void visit(Type type)
128     {
129         if (checkImmutableShared(type))
130             return;
131 
132         error(Loc.initial, "Internal Compiler Error: type `%s` cannot be mapped to C++\n", type.toChars());
133         fatal(); //Fatal, because this error should be handled in frontend
134     }
135 
136     override void visit(TypeNull type)
137     {
138         if (checkImmutableShared(type))
139             return;
140         if (checkTypeSaved(type))
141             return;
142 
143         buf.writestring("$$T");
144         flags &= ~IS_NOT_TOP_TYPE;
145         flags &= ~IGNORE_CONST;
146     }
147 
148     override void visit(TypeNoreturn type)
149     {
150         if (checkImmutableShared(type))
151             return;
152         if (checkTypeSaved(type))
153             return;
154 
155         buf.writeByte('X');             // yes, mangle it like `void`
156         flags &= ~IS_NOT_TOP_TYPE;
157         flags &= ~IGNORE_CONST;
158     }
159 
160     override void visit(TypeBasic type)
161     {
162         //printf("visit(TypeBasic); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE));
163         if (checkImmutableShared(type))
164             return;
165 
166         if (type.isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC)))
167         {
168             if (checkTypeSaved(type))
169                 return;
170         }
171         if ((type.ty == Tbool) && checkTypeSaved(type)) // try to replace long name with number
172         {
173             return;
174         }
175         if (!(flags & IS_DMC))
176         {
177             switch (type.ty)
178             {
179             case Tint64:
180             case Tuns64:
181             case Tint128:
182             case Tuns128:
183             case Tfloat80:
184             case Twchar:
185                 if (checkTypeSaved(type))
186                     return;
187                 break;
188 
189             default:
190                 break;
191             }
192         }
193         mangleModifier(type);
194         switch (type.ty)
195         {
196         case Tvoid:
197             buf.writeByte('X');
198             break;
199         case Tint8:
200             buf.writeByte('C');
201             break;
202         case Tuns8:
203             buf.writeByte('E');
204             break;
205         case Tint16:
206             buf.writeByte('F');
207             break;
208         case Tuns16:
209             buf.writeByte('G');
210             break;
211         case Tint32:
212             buf.writeByte('H');
213             break;
214         case Tuns32:
215             buf.writeByte('I');
216             break;
217         case Tfloat32:
218             buf.writeByte('M');
219             break;
220         case Tint64:
221             buf.writestring("_J");
222             break;
223         case Tuns64:
224             buf.writestring("_K");
225             break;
226         case Tint128:
227             buf.writestring("_L");
228             break;
229         case Tuns128:
230             buf.writestring("_M");
231             break;
232         case Tfloat64:
233             buf.writeByte('N');
234             break;
235         case Tfloat80:
236             if (flags & IS_DMC)
237                 buf.writestring("_Z"); // DigitalMars long double
238             else
239                 buf.writestring("_T"); // Intel long double
240             break;
241         case Tbool:
242             buf.writestring("_N");
243             break;
244         case Tchar:
245             buf.writeByte('D');
246             break;
247         case Twchar:
248             buf.writestring("_S"); // Visual C++ char16_t (since C++11)
249             break;
250         case Tdchar:
251             buf.writestring("_U"); // Visual C++ char32_t (since C++11)
252             break;
253         default:
254             visit(cast(Type)type);
255             return;
256         }
257         flags &= ~IS_NOT_TOP_TYPE;
258         flags &= ~IGNORE_CONST;
259     }
260 
261     override void visit(TypeVector type)
262     {
263         //printf("visit(TypeVector); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE));
264         if (checkTypeSaved(type))
265             return;
266         mangleModifier(type);
267         buf.writestring("T__m128@@"); // may be better as __m128i or __m128d?
268         flags &= ~IS_NOT_TOP_TYPE;
269         flags &= ~IGNORE_CONST;
270     }
271 
272     override void visit(TypeSArray type)
273     {
274         // This method can be called only for static variable type mangling.
275         //printf("visit(TypeSArray); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE));
276         if (checkTypeSaved(type))
277             return;
278         // first dimension always mangled as const pointer
279         if (flags & IS_DMC)
280             buf.writeByte('Q');
281         else
282             buf.writeByte('P');
283         flags |= IS_NOT_TOP_TYPE;
284         assert(type.next);
285         if (type.next.ty == Tsarray)
286         {
287             mangleArray(cast(TypeSArray)type.next);
288         }
289         else
290         {
291             type.next.accept(this);
292         }
293     }
294 
295     // attention: D int[1][2]* arr mapped to C++ int arr[][2][1]; (because it's more typical situation)
296     // There is not way to map int C++ (*arr)[2][1] to D
297     override void visit(TypePointer type)
298     {
299         //printf("visit(TypePointer); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE));
300         if (checkImmutableShared(type))
301             return;
302 
303         assert(type.next);
304         if (type.next.ty == Tfunction)
305         {
306             const(char)* arg = mangleFunctionType(cast(TypeFunction)type.next); // compute args before checking to save; args should be saved before function type
307             // If we've mangled this function early, previous call is meaningless.
308             // However we should do it before checking to save types of function arguments before function type saving.
309             // If this function was already mangled, types of all it arguments are save too, thus previous can't save
310             // anything if function is saved.
311             if (checkTypeSaved(type))
312                 return;
313             if (type.isConst())
314                 buf.writeByte('Q'); // const
315             else
316                 buf.writeByte('P'); // mutable
317             buf.writeByte('6'); // pointer to a function
318             buf.writestring(arg);
319             flags &= ~IS_NOT_TOP_TYPE;
320             flags &= ~IGNORE_CONST;
321             return;
322         }
323         else if (type.next.ty == Tsarray)
324         {
325             if (checkTypeSaved(type))
326                 return;
327             mangleModifier(type);
328             if (type.isConst() || !(flags & IS_DMC))
329                 buf.writeByte('Q'); // const
330             else
331                 buf.writeByte('P'); // mutable
332             if (global.params.is64bit)
333                 buf.writeByte('E');
334             flags |= IS_NOT_TOP_TYPE;
335             mangleArray(cast(TypeSArray)type.next);
336             return;
337         }
338         else
339         {
340             if (checkTypeSaved(type))
341                 return;
342             mangleModifier(type);
343             if (type.isConst())
344             {
345                 buf.writeByte('Q'); // const
346             }
347             else
348             {
349                 buf.writeByte('P'); // mutable
350             }
351             if (global.params.is64bit)
352                 buf.writeByte('E');
353             flags |= IS_NOT_TOP_TYPE;
354             type.next.accept(this);
355         }
356     }
357 
358     override void visit(TypeReference type)
359     {
360         //printf("visit(TypeReference); type = %s\n", type.toChars());
361         if (checkTypeSaved(type))
362             return;
363 
364         if (checkImmutableShared(type))
365             return;
366 
367         buf.writeByte('A'); // mutable
368         if (global.params.is64bit)
369             buf.writeByte('E');
370         flags |= IS_NOT_TOP_TYPE;
371         assert(type.next);
372         if (type.next.ty == Tsarray)
373         {
374             mangleArray(cast(TypeSArray)type.next);
375         }
376         else
377         {
378             type.next.accept(this);
379         }
380     }
381 
382     override void visit(TypeFunction type)
383     {
384         const(char)* arg = mangleFunctionType(type);
385         if ((flags & IS_DMC))
386         {
387             if (checkTypeSaved(type))
388                 return;
389         }
390         else
391         {
392             buf.writestring("$$A6");
393         }
394         buf.writestring(arg);
395         flags &= ~(IS_NOT_TOP_TYPE | IGNORE_CONST);
396     }
397 
398     override void visit(TypeStruct type)
399     {
400         if (checkTypeSaved(type))
401             return;
402         //printf("visit(TypeStruct); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE));
403         mangleModifier(type);
404         const agg = type.sym.isStructDeclaration();
405         if (type.sym.isUnionDeclaration())
406             buf.writeByte('T');
407         else
408             buf.writeByte(agg.cppmangle == CPPMANGLE.asClass ? 'V' : 'U');
409         mangleIdent(type.sym);
410         flags &= ~IS_NOT_TOP_TYPE;
411         flags &= ~IGNORE_CONST;
412     }
413 
414     override void visit(TypeEnum type)
415     {
416         //printf("visit(TypeEnum); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE));
417         const id = type.sym.ident;
418         string c;
419         if (id == Id.__c_long_double)
420             c = "O"; // VC++ long double
421         else if (id == Id.__c_long)
422             c = "J"; // VC++ long
423         else if (id == Id.__c_ulong)
424             c = "K"; // VC++ unsigned long
425         else if (id == Id.__c_longlong)
426             c = "_J"; // VC++ long long
427         else if (id == Id.__c_ulonglong)
428             c = "_K"; // VC++ unsigned long long
429         else if (id == Id.__c_wchar_t)
430         {
431             c = (flags & IS_DMC) ? "_Y" : "_W";
432         }
433 
434         if (c.length)
435         {
436             if (checkImmutableShared(type))
437                 return;
438 
439             if (type.isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC)))
440             {
441                 if (checkTypeSaved(type))
442                     return;
443             }
444             mangleModifier(type);
445             buf.writestring(c);
446         }
447         else
448         {
449             if (checkTypeSaved(type))
450                 return;
451             mangleModifier(type);
452             buf.writestring("W4");
453             mangleIdent(type.sym);
454         }
455         flags &= ~IS_NOT_TOP_TYPE;
456         flags &= ~IGNORE_CONST;
457     }
458 
459     // D class mangled as pointer to C++ class
460     // const(Object) mangled as Object const* const
461     override void visit(TypeClass type)
462     {
463         //printf("visit(TypeClass); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE));
464         if (checkTypeSaved(type))
465             return;
466         if (flags & IS_NOT_TOP_TYPE)
467             mangleModifier(type);
468         if (type.isConst())
469             buf.writeByte('Q');
470         else
471             buf.writeByte('P');
472         if (global.params.is64bit)
473             buf.writeByte('E');
474         flags |= IS_NOT_TOP_TYPE;
475         mangleModifier(type);
476         const cldecl = type.sym.isClassDeclaration();
477         buf.writeByte(cldecl.cppmangle == CPPMANGLE.asStruct ? 'U' : 'V');
478         mangleIdent(type.sym);
479         flags &= ~IS_NOT_TOP_TYPE;
480         flags &= ~IGNORE_CONST;
481     }
482 
483     const(char)* mangleOf(Dsymbol s)
484     {
485         VarDeclaration vd = s.isVarDeclaration();
486         FuncDeclaration fd = s.isFuncDeclaration();
487         if (vd)
488         {
489             mangleVariable(vd);
490         }
491         else if (fd)
492         {
493             mangleFunction(fd);
494         }
495         else
496         {
497             assert(0);
498         }
499         return buf.extractChars();
500     }
501 
502 private:
503 extern(D):
504     void mangleFunction(FuncDeclaration d)
505     {
506         // <function mangle> ? <qualified name> <flags> <return type> <arg list>
507         assert(d);
508         buf.writeByte('?');
509         mangleIdent(d);
510         if (d.needThis()) // <flags> ::= <virtual/protection flag> <const/volatile flag> <calling convention flag>
511         {
512             // Pivate methods always non-virtual in D and it should be mangled as non-virtual in C++
513             //printf("%s: isVirtualMethod = %d, isVirtual = %d, vtblIndex = %d, interfaceVirtual = %p\n",
514                 //d.toChars(), d.isVirtualMethod(), d.isVirtual(), cast(int)d.vtblIndex, d.interfaceVirtual);
515             if ((d.isVirtual() && (d.vtblIndex != -1 || d.interfaceVirtual || d.overrideInterface())) || (d.isDtorDeclaration() && d.parent.isClassDeclaration() && !d.isFinal()))
516             {
517                 switch (d.visibility.kind)
518                 {
519                 case Visibility.Kind.private_:
520                     buf.writeByte('E');
521                     break;
522                 case Visibility.Kind.protected_:
523                     buf.writeByte('M');
524                     break;
525                 default:
526                     buf.writeByte('U');
527                     break;
528                 }
529             }
530             else
531             {
532                 switch (d.visibility.kind)
533                 {
534                 case Visibility.Kind.private_:
535                     buf.writeByte('A');
536                     break;
537                 case Visibility.Kind.protected_:
538                     buf.writeByte('I');
539                     break;
540                 default:
541                     buf.writeByte('Q');
542                     break;
543                 }
544             }
545             if (global.params.is64bit)
546                 buf.writeByte('E');
547             if (d.type.isConst())
548             {
549                 buf.writeByte('B');
550             }
551             else
552             {
553                 buf.writeByte('A');
554             }
555         }
556         else if (d.isMember2()) // static function
557         {
558             // <flags> ::= <virtual/protection flag> <calling convention flag>
559             switch (d.visibility.kind)
560             {
561             case Visibility.Kind.private_:
562                 buf.writeByte('C');
563                 break;
564             case Visibility.Kind.protected_:
565                 buf.writeByte('K');
566                 break;
567             default:
568                 buf.writeByte('S');
569                 break;
570             }
571         }
572         else // top-level function
573         {
574             // <flags> ::= Y <calling convention flag>
575             buf.writeByte('Y');
576         }
577         const(char)* args = mangleFunctionType(cast(TypeFunction)d.type, d.needThis(), d.isCtorDeclaration() || isPrimaryDtor(d));
578         buf.writestring(args);
579     }
580 
581     void mangleVariable(VarDeclaration d)
582     {
583         // <static variable mangle> ::= ? <qualified name> <protection flag> <const/volatile flag> <type>
584         assert(d);
585         // fake mangling for fields to fix https://issues.dlang.org/show_bug.cgi?id=16525
586         if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared)))
587         {
588             d.error("Internal Compiler Error: C++ static non-__gshared non-extern variables not supported");
589             fatal();
590         }
591         buf.writeByte('?');
592         mangleIdent(d);
593         assert((d.storage_class & STC.field) || !d.needThis());
594         Dsymbol parent = d.toParent();
595         while (parent && parent.isNspace())
596         {
597             parent = parent.toParent();
598         }
599         if (parent && parent.isModule()) // static member
600         {
601             buf.writeByte('3');
602         }
603         else
604         {
605             switch (d.visibility.kind)
606             {
607             case Visibility.Kind.private_:
608                 buf.writeByte('0');
609                 break;
610             case Visibility.Kind.protected_:
611                 buf.writeByte('1');
612                 break;
613             default:
614                 buf.writeByte('2');
615                 break;
616             }
617         }
618         char cv_mod = 0;
619         Type t = d.type;
620 
621         if (checkImmutableShared(t))
622             return;
623 
624         if (t.isConst())
625         {
626             cv_mod = 'B'; // const
627         }
628         else
629         {
630             cv_mod = 'A'; // mutable
631         }
632         if (t.ty != Tpointer)
633             t = t.mutableOf();
634         t.accept(this);
635         if ((t.ty == Tpointer || t.ty == Treference || t.ty == Tclass) && global.params.is64bit)
636         {
637             buf.writeByte('E');
638         }
639         buf.writeByte(cv_mod);
640     }
641 
642     /**
643      * Computes mangling for symbols with special mangling.
644      * Params:
645      *      sym = symbol to mangle
646      * Returns:
647      *      mangling for special symbols,
648      *      null if not a special symbol
649      */
650     static string mangleSpecialName(Dsymbol sym)
651     {
652         string mangle;
653         if (sym.isCtorDeclaration())
654             mangle = "?0";
655         else if (sym.isPrimaryDtor())
656             mangle = "?1";
657         else if (!sym.ident)
658             return null;
659         else if (sym.ident == Id.assign)
660             mangle = "?4";
661         else if (sym.ident == Id.eq)
662             mangle = "?8";
663         else if (sym.ident == Id.index)
664             mangle = "?A";
665         else if (sym.ident == Id.call)
666             mangle = "?R";
667         else if (sym.ident == Id.cppdtor)
668             mangle = "?_G";
669         else
670             return null;
671 
672         return mangle;
673     }
674 
675     /**
676      * Mangles an operator, if any
677      *
678      * Params:
679      *      ti                  = associated template instance of the operator
680      *      symName             = symbol name
681      *      firstTemplateArg    = index if the first argument of the template (because the corresponding c++ operator is not a template)
682      * Returns:
683      *      true if sym has no further mangling needed
684      *      false otherwise
685      */
686     bool mangleOperator(TemplateInstance ti, ref const(char)[] symName, ref int firstTemplateArg)
687     {
688         auto whichOp = isCppOperator(ti.name);
689         final switch (whichOp)
690         {
691         case CppOperator.Unknown:
692             return false;
693         case CppOperator.Cast:
694             buf.writestring("?B");
695             return true;
696         case CppOperator.Assign:
697             symName = "?4";
698             return false;
699         case CppOperator.Eq:
700             symName = "?8";
701             return false;
702         case CppOperator.Index:
703             symName = "?A";
704             return false;
705         case CppOperator.Call:
706             symName = "?R";
707             return false;
708 
709         case CppOperator.Unary:
710         case CppOperator.Binary:
711         case CppOperator.OpAssign:
712             TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
713             assert(td);
714             assert(ti.tiargs.dim >= 1);
715             TemplateParameter tp = (*td.parameters)[0];
716             TemplateValueParameter tv = tp.isTemplateValueParameter();
717             if (!tv || !tv.valType.isString())
718                 return false; // expecting a string argument to operators!
719             Expression exp = (*ti.tiargs)[0].isExpression();
720             StringExp str = exp.toStringExp();
721             switch (whichOp)
722             {
723             case CppOperator.Unary:
724                 switch (str.peekString())
725                 {
726                     case "*":   symName = "?D";     goto continue_template;
727                     case "++":  symName = "?E";     goto continue_template;
728                     case "--":  symName = "?F";     goto continue_template;
729                     case "-":   symName = "?G";     goto continue_template;
730                     case "+":   symName = "?H";     goto continue_template;
731                     case "~":   symName = "?S";     goto continue_template;
732                     default:    return false;
733                 }
734             case CppOperator.Binary:
735                 switch (str.peekString())
736                 {
737                     case ">>":  symName = "?5";     goto continue_template;
738                     case "<<":  symName = "?6";     goto continue_template;
739                     case "*":   symName = "?D";     goto continue_template;
740                     case "-":   symName = "?G";     goto continue_template;
741                     case "+":   symName = "?H";     goto continue_template;
742                     case "&":   symName = "?I";     goto continue_template;
743                     case "/":   symName = "?K";     goto continue_template;
744                     case "%":   symName = "?L";     goto continue_template;
745                     case "^":   symName = "?T";     goto continue_template;
746                     case "|":   symName = "?U";     goto continue_template;
747                     default:    return false;
748                     }
749             case CppOperator.OpAssign:
750                 switch (str.peekString())
751                 {
752                     case "*":   symName = "?X";     goto continue_template;
753                     case "+":   symName = "?Y";     goto continue_template;
754                     case "-":   symName = "?Z";     goto continue_template;
755                     case "/":   symName = "?_0";    goto continue_template;
756                     case "%":   symName = "?_1";    goto continue_template;
757                     case ">>":  symName = "?_2";    goto continue_template;
758                     case "<<":  symName = "?_3";    goto continue_template;
759                     case "&":   symName = "?_4";    goto continue_template;
760                     case "|":   symName = "?_5";    goto continue_template;
761                     case "^":   symName = "?_6";    goto continue_template;
762                     default:    return false;
763                 }
764             default: assert(0);
765             }
766         }
767         continue_template:
768         if (ti.tiargs.dim == 1)
769         {
770             buf.writestring(symName);
771             return true;
772         }
773         firstTemplateArg = 1;
774         return false;
775     }
776 
777     /**
778      * Mangles a template value
779      *
780      * Params:
781      *      o               = expression that represents the value
782      *      tv              = template value
783      *      is_dmc_template = use DMC mangling
784      */
785     void manlgeTemplateValue(RootObject o,TemplateValueParameter tv, Dsymbol sym,bool is_dmc_template)
786     {
787         if (!tv.valType.isintegral())
788         {
789             sym.error("Internal Compiler Error: C++ %s template value parameter is not supported", tv.valType.toChars());
790             fatal();
791             return;
792         }
793         buf.writeByte('$');
794         buf.writeByte('0');
795         Expression e = isExpression(o);
796         assert(e);
797         if (tv.valType.isunsigned())
798         {
799             mangleNumber(e.toUInteger());
800         }
801         else if (is_dmc_template)
802         {
803             // NOTE: DMC mangles everything based on
804             // unsigned int
805             mangleNumber(e.toInteger());
806         }
807         else
808         {
809             sinteger_t val = e.toInteger();
810             if (val < 0)
811             {
812                 val = -val;
813                 buf.writeByte('?');
814             }
815             mangleNumber(val);
816         }
817     }
818 
819     /**
820      * Mangles a template alias parameter
821      *
822      * Params:
823      *      o   = the alias value, a symbol or expression
824      */
825     void mangleTemplateAlias(RootObject o, Dsymbol sym)
826     {
827         Dsymbol d = isDsymbol(o);
828         Expression e = isExpression(o);
829 
830         if (d && d.isFuncDeclaration())
831         {
832             buf.writeByte('$');
833             buf.writeByte('1');
834             mangleFunction(d.isFuncDeclaration());
835         }
836         else if (e && e.op == TOK.variable && (cast(VarExp)e).var.isVarDeclaration())
837         {
838             buf.writeByte('$');
839             if (flags & IS_DMC)
840                 buf.writeByte('1');
841             else
842                 buf.writeByte('E');
843             mangleVariable((cast(VarExp)e).var.isVarDeclaration());
844         }
845         else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember)
846         {
847             Dsymbol ds = d.isTemplateDeclaration().onemember;
848             if (flags & IS_DMC)
849             {
850                 buf.writeByte('V');
851             }
852             else
853             {
854                 if (ds.isUnionDeclaration())
855                 {
856                     buf.writeByte('T');
857                 }
858                 else if (ds.isStructDeclaration())
859                 {
860                     buf.writeByte('U');
861                 }
862                 else if (ds.isClassDeclaration())
863                 {
864                     buf.writeByte('V');
865                 }
866                 else
867                 {
868                     sym.error("Internal Compiler Error: C++ templates support only integral value, type parameters, alias templates and alias function parameters");
869                     fatal();
870                 }
871             }
872             mangleIdent(d);
873         }
874         else
875         {
876             sym.error("Internal Compiler Error: `%s` is unsupported parameter for C++ template", o.toChars());
877             fatal();
878         }
879     }
880 
881     /**
882      * Mangles a template alias parameter
883      *
884      * Params:
885      *      o   = type
886      */
887     void mangleTemplateType(RootObject o)
888     {
889         flags |= ESCAPE;
890         Type t = isType(o);
891         assert(t);
892         t.accept(this);
893         flags &= ~ESCAPE;
894     }
895 
896     /**
897      * Mangles the name of a symbol
898      *
899      * Params:
900      *      sym   = symbol to mangle
901      *      dont_use_back_reference = dont use back referencing
902      */
903     void mangleName(Dsymbol sym, bool dont_use_back_reference)
904     {
905         //printf("mangleName('%s')\n", sym.toChars());
906         bool is_dmc_template = false;
907 
908         if (string s = mangleSpecialName(sym))
909         {
910             buf.writestring(s);
911             return;
912         }
913 
914         void writeName(Identifier name)
915         {
916             assert(name);
917             if (!is_dmc_template && dont_use_back_reference)
918                 saveIdent(name);
919             else if (checkAndSaveIdent(name))
920                 return;
921 
922             buf.writestring(name.toString());
923             buf.writeByte('@');
924         }
925         auto ti = sym.isTemplateInstance();
926         if (!ti)
927         {
928             writeName(sym.ident);
929             return;
930         }
931         auto id = ti.tempdecl.ident;
932         auto symName = id.toString();
933 
934         int firstTemplateArg = 0;
935 
936         // test for special symbols
937         if (mangleOperator(ti,symName,firstTemplateArg))
938             return;
939 
940         scope VisualCPPMangler tmp = new VisualCPPMangler((flags & IS_DMC) ? true : false);
941         tmp.buf.writeByte('?');
942         tmp.buf.writeByte('$');
943         tmp.buf.writestring(symName);
944         tmp.saved_idents[0] = id;
945         if (symName == id.toString())
946             tmp.buf.writeByte('@');
947         if (flags & IS_DMC)
948         {
949             tmp.mangleIdent(sym.parent, true);
950             is_dmc_template = true;
951         }
952         bool is_var_arg = false;
953         for (size_t i = firstTemplateArg; i < ti.tiargs.dim; i++)
954         {
955             RootObject o = (*ti.tiargs)[i];
956             TemplateParameter tp = null;
957             TemplateValueParameter tv = null;
958             TemplateTupleParameter tt = null;
959             if (!is_var_arg)
960             {
961                 TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
962                 assert(td);
963                 tp = (*td.parameters)[i];
964                 tv = tp.isTemplateValueParameter();
965                 tt = tp.isTemplateTupleParameter();
966             }
967             if (tt)
968             {
969                 is_var_arg = true;
970                 tp = null;
971             }
972             if (tv)
973             {
974                 tmp.manlgeTemplateValue(o, tv, sym, is_dmc_template);
975             }
976             else if (!tp || tp.isTemplateTypeParameter())
977             {
978                 tmp.mangleTemplateType(o);
979             }
980             else if (tp.isTemplateAliasParameter())
981             {
982                 tmp.mangleTemplateAlias(o, sym);
983             }
984             else
985             {
986                 sym.error("Internal Compiler Error: C++ templates support only integral value, type parameters, alias templates and alias function parameters");
987                 fatal();
988             }
989         }
990         writeName(Identifier.idPool(tmp.buf.extractSlice()));
991     }
992 
993     // returns true if name already saved
994     bool checkAndSaveIdent(Identifier name)
995     {
996         foreach (i; 0 .. VC_SAVED_IDENT_CNT)
997         {
998             if (!saved_idents[i]) // no saved same name
999             {
1000                 saved_idents[i] = name;
1001                 break;
1002             }
1003             if (saved_idents[i] == name) // ok, we've found same name. use index instead of name
1004             {
1005                 buf.writeByte(i + '0');
1006                 return true;
1007             }
1008         }
1009         return false;
1010     }
1011 
1012     void saveIdent(Identifier name)
1013     {
1014         foreach (i; 0 .. VC_SAVED_IDENT_CNT)
1015         {
1016             if (!saved_idents[i]) // no saved same name
1017             {
1018                 saved_idents[i] = name;
1019                 break;
1020             }
1021             if (saved_idents[i] == name) // ok, we've found same name. use index instead of name
1022             {
1023                 return;
1024             }
1025         }
1026     }
1027 
1028     void mangleIdent(Dsymbol sym, bool dont_use_back_reference = false)
1029     {
1030         // <qualified name> ::= <sub-name list> @
1031         // <sub-name list>  ::= <sub-name> <name parts>
1032         //                  ::= <sub-name>
1033         // <sub-name> ::= <identifier> @
1034         //            ::= ?$ <identifier> @ <template args> @
1035         //            :: <back reference>
1036         // <back reference> ::= 0-9
1037         // <template args> ::= <template arg> <template args>
1038         //                ::= <template arg>
1039         // <template arg>  ::= <type>
1040         //                ::= $0<encoded integral number>
1041         //printf("mangleIdent('%s')\n", sym.toChars());
1042         Dsymbol p = sym;
1043         if (p.toParent() && p.toParent().isTemplateInstance())
1044         {
1045             p = p.toParent();
1046         }
1047         while (p && !p.isModule())
1048         {
1049             mangleName(p, dont_use_back_reference);
1050             // Mangle our string namespaces as well
1051             for (auto ns = p.cppnamespace; ns !is null && ns.ident !is null; ns = ns.cppnamespace)
1052                 mangleName(ns, dont_use_back_reference);
1053 
1054             p = p.toParent();
1055             if (p.toParent() && p.toParent().isTemplateInstance())
1056             {
1057                 p = p.toParent();
1058             }
1059         }
1060         if (!dont_use_back_reference)
1061             buf.writeByte('@');
1062     }
1063 
1064     void mangleNumber(dinteger_t num)
1065     {
1066         if (!num) // 0 encoded as "A@"
1067         {
1068             buf.writeByte('A');
1069             buf.writeByte('@');
1070             return;
1071         }
1072         if (num <= 10) // 5 encoded as "4"
1073         {
1074             buf.writeByte(cast(char)(num - 1 + '0'));
1075             return;
1076         }
1077         char[17] buff;
1078         buff[16] = 0;
1079         size_t i = 16;
1080         while (num)
1081         {
1082             --i;
1083             buff[i] = num % 16 + 'A';
1084             num /= 16;
1085         }
1086         buf.writestring(&buff[i]);
1087         buf.writeByte('@');
1088     }
1089 
1090     bool checkTypeSaved(Type type)
1091     {
1092         if (flags & IS_NOT_TOP_TYPE)
1093             return false;
1094         if (flags & MANGLE_RETURN_TYPE)
1095             return false;
1096         for (uint i = 0; i < VC_SAVED_TYPE_CNT; i++)
1097         {
1098             if (!saved_types[i]) // no saved same type
1099             {
1100                 saved_types[i] = type;
1101                 return false;
1102             }
1103             if (saved_types[i].equals(type)) // ok, we've found same type. use index instead of type
1104             {
1105                 buf.writeByte(i + '0');
1106                 flags &= ~IS_NOT_TOP_TYPE;
1107                 flags &= ~IGNORE_CONST;
1108                 return true;
1109             }
1110         }
1111         return false;
1112     }
1113 
1114     void mangleModifier(Type type)
1115     {
1116         if (flags & IGNORE_CONST)
1117             return;
1118         if (checkImmutableShared(type))
1119             return;
1120 
1121         if (type.isConst())
1122         {
1123             // Template parameters that are not pointers and are const need an $$C escape
1124             // in addition to 'B' (const).
1125             if ((flags & ESCAPE) && type.ty != Tpointer)
1126                 buf.writestring("$$CB");
1127             else if (flags & IS_NOT_TOP_TYPE)
1128                 buf.writeByte('B'); // const
1129             else if ((flags & IS_DMC) && type.ty != Tpointer)
1130                 buf.writestring("_O");
1131         }
1132         else if (flags & IS_NOT_TOP_TYPE)
1133             buf.writeByte('A'); // mutable
1134 
1135         flags &= ~ESCAPE;
1136     }
1137 
1138     void mangleArray(TypeSArray type)
1139     {
1140         mangleModifier(type);
1141         size_t i = 0;
1142         Type cur = type;
1143         while (cur && cur.ty == Tsarray)
1144         {
1145             i++;
1146             cur = cur.nextOf();
1147         }
1148         buf.writeByte('Y');
1149         mangleNumber(i); // count of dimensions
1150         cur = type;
1151         while (cur && cur.ty == Tsarray) // sizes of dimensions
1152         {
1153             TypeSArray sa = cast(TypeSArray)cur;
1154             mangleNumber(sa.dim ? sa.dim.toInteger() : 0);
1155             cur = cur.nextOf();
1156         }
1157         flags |= IGNORE_CONST;
1158         cur.accept(this);
1159     }
1160 
1161     const(char)* mangleFunctionType(TypeFunction type, bool needthis = false, bool noreturn = false)
1162     {
1163         scope VisualCPPMangler tmp = new VisualCPPMangler(this);
1164         // Calling convention
1165         if (global.params.is64bit) // always Microsoft x64 calling convention
1166         {
1167             tmp.buf.writeByte('A');
1168         }
1169         else
1170         {
1171             final switch (type.linkage)
1172             {
1173             case LINK.c:
1174                 tmp.buf.writeByte('A');
1175                 break;
1176             case LINK.cpp:
1177                 if (needthis && type.parameterList.varargs != VarArg.variadic)
1178                     tmp.buf.writeByte('E'); // thiscall
1179                 else
1180                     tmp.buf.writeByte('A'); // cdecl
1181                 break;
1182             case LINK.windows:
1183                 tmp.buf.writeByte('G'); // stdcall
1184                 break;
1185             case LINK.d:
1186             case LINK.default_:
1187             case LINK.system:
1188             case LINK.objc:
1189                 tmp.visit(cast(Type)type);
1190                 break;
1191             }
1192         }
1193         tmp.flags &= ~IS_NOT_TOP_TYPE;
1194         if (noreturn)
1195         {
1196             tmp.buf.writeByte('@');
1197         }
1198         else
1199         {
1200             Type rettype = type.next;
1201             if (type.isref)
1202                 rettype = rettype.referenceTo();
1203             flags &= ~IGNORE_CONST;
1204             if (rettype.ty == Tstruct)
1205             {
1206                 tmp.buf.writeByte('?');
1207                 tmp.buf.writeByte('A');
1208             }
1209             else if (rettype.ty == Tenum)
1210             {
1211                 const id = rettype.toDsymbol(null).ident;
1212                 if (!isSpecialEnumIdent(id))
1213                 {
1214                     tmp.buf.writeByte('?');
1215                     tmp.buf.writeByte('A');
1216                 }
1217             }
1218             tmp.flags |= MANGLE_RETURN_TYPE;
1219             rettype.accept(tmp);
1220             tmp.flags &= ~MANGLE_RETURN_TYPE;
1221         }
1222         if (!type.parameterList.parameters || !type.parameterList.parameters.dim)
1223         {
1224             if (type.parameterList.varargs == VarArg.variadic)
1225                 tmp.buf.writeByte('Z');
1226             else
1227                 tmp.buf.writeByte('X');
1228         }
1229         else
1230         {
1231             foreach (n, p; type.parameterList)
1232             {
1233                 Type t = p.type;
1234                 if (p.isReference())
1235                 {
1236                     t = t.referenceTo();
1237                 }
1238                 else if (p.storageClass & STC.lazy_)
1239                 {
1240                     // Mangle as delegate
1241                     Type td = new TypeFunction(ParameterList(), t, LINK.d);
1242                     td = new TypeDelegate(td);
1243                     t = merge(t);
1244                 }
1245                 if (t.ty == Tsarray)
1246                 {
1247                     error(Loc.initial, "Internal Compiler Error: unable to pass static array to `extern(C++)` function.");
1248                     error(Loc.initial, "Use pointer instead.");
1249                     assert(0);
1250                 }
1251                 tmp.flags &= ~IS_NOT_TOP_TYPE;
1252                 tmp.flags &= ~IGNORE_CONST;
1253                 t.accept(tmp);
1254             }
1255 
1256             if (type.parameterList.varargs == VarArg.variadic)
1257             {
1258                 tmp.buf.writeByte('Z');
1259             }
1260             else
1261             {
1262                 tmp.buf.writeByte('@');
1263             }
1264         }
1265         tmp.buf.writeByte('Z');
1266         const(char)* ret = tmp.buf.extractChars();
1267         saved_idents[] = tmp.saved_idents[];
1268         saved_types[] = tmp.saved_types[];
1269         return ret;
1270     }
1271 }