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