xref: /netbsd-src/external/gpl3/gcc/dist/gcc/d/dmd/cppmangle.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /**
2  * Do mangling for C++ linkage.
3  *
4  * This is the POSIX side of the implementation.
5  * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`.
6  *
7  * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
8  * Authors: Walter Bright, https://www.digitalmars.com
9  * License:   $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10  * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d, _cppmangle.d)
11  * Documentation:  https://dlang.org/phobos/dmd_cppmangle.html
12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmangle.d
13  *
14  * References:
15  *  Follows Itanium C++ ABI 1.86 section 5.1
16  *  http://refspecs.linux-foundation.org/cxxabi-1.86.html#mangling
17  *  which is where the grammar comments come from.
18  *
19  * Bugs:
20  *  https://issues.dlang.org/query.cgi
21  *  enter `C++, mangling` as the keywords.
22  */
23 
24 module dmd.cppmangle;
25 
26 import core.stdc.string;
27 import core.stdc.stdio;
28 
29 import dmd.arraytypes;
30 import dmd.astenums;
31 import dmd.attrib;
32 import dmd.declaration;
33 import dmd.dsymbol;
34 import dmd.dtemplate;
35 import dmd.errors;
36 import dmd.expression;
37 import dmd.func;
38 import dmd.globals;
39 import dmd.id;
40 import dmd.identifier;
41 import dmd.mtype;
42 import dmd.nspace;
43 import dmd.root.array;
44 import dmd.common.outbuffer;
45 import dmd.root.rootobject;
46 import dmd.root.string;
47 import dmd.target;
48 import dmd.tokens;
49 import dmd.typesem;
50 import dmd.visitor;
51 
52 
53 // helper to check if an identifier is a C++ operator
54 enum CppOperator { Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign, Unknown }
isCppOperator(Identifier id)55 package CppOperator isCppOperator(Identifier id)
56 {
57     __gshared const(Identifier)[] operators = null;
58     if (!operators)
59         operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign];
60     foreach (i, op; operators)
61     {
62         if (op == id)
63             return cast(CppOperator)i;
64     }
65     return CppOperator.Unknown;
66 }
67 
68 ///
toCppMangleItanium(Dsymbol s)69 extern(C++) const(char)* toCppMangleItanium(Dsymbol s)
70 {
71     //printf("toCppMangleItanium(%s)\n", s.toChars());
72     OutBuffer buf;
73     scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc);
74     v.mangleOf(s);
75     return buf.extractChars();
76 }
77 
78 ///
cppTypeInfoMangleItanium(Dsymbol s)79 extern(C++) const(char)* cppTypeInfoMangleItanium(Dsymbol s)
80 {
81     //printf("cppTypeInfoMangle(%s)\n", s.toChars());
82     OutBuffer buf;
83     buf.writestring("_ZTI");    // "TI" means typeinfo structure
84     scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc);
85     v.cpp_mangle_name(s, false);
86     return buf.extractChars();
87 }
88 
89 ///
cppThunkMangleItanium(FuncDeclaration fd,int offset)90 extern(C++) const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset)
91 {
92     //printf("cppThunkMangleItanium(%s)\n", fd.toChars());
93     OutBuffer buf;
94     buf.printf("_ZThn%u_", offset);  // "Th" means thunk, "n%u" is the call offset
95     scope CppMangleVisitor v = new CppMangleVisitor(&buf, fd.loc);
96     v.mangle_function_encoding(fd);
97     return buf.extractChars();
98 }
99 
100 /******************************
101  * Determine if sym is a full aggregate destructor.
102  * Params:
103  *      sym = Dsymbol
104  * Returns:
105  *      true if sym is an aggregate destructor
106  */
isAggregateDtor(const Dsymbol sym)107 bool isAggregateDtor(const Dsymbol sym)
108 {
109     const dtor = sym.isDtorDeclaration();
110     if (!dtor)
111         return false;
112     const ad = dtor.isMember();
113     assert(ad);
114     return dtor == ad.aggrDtor;
115 }
116 
117 /// Context used when processing pre-semantic AST
118 private struct Context
119 {
120     /// Template instance of the function being mangled
121     TemplateInstance ti;
122     /// Function declaration we're mangling
123     FuncDeclaration fd;
124     /// Current type / expression being processed (semantically analyzed)
125     RootObject res;
126 
127     @disable ref Context opAssign(ref Context other);
128     @disable ref Context opAssign(Context other);
129 
130     /**
131      * Helper function to track `res`
132      *
133      * Params:
134      *   next = Value to set `this.res` to.
135      *          If `this.res` is `null`, the expression is not evalutated.
136      *          This allow this code to be used even when no context is needed.
137      *
138      * Returns:
139      *   The previous state of this `Context` object
140      */
pushContext141     private Context push(lazy RootObject next)
142     {
143         auto r = this.res;
144         if (r !is null)
145             this.res = next;
146         return Context(this.ti, this.fd, r);
147     }
148 
149     /**
150      * Reset the context to a previous one, making any adjustment necessary
151      */
popContext152     private void pop(ref Context prev)
153     {
154         this.res = prev.res;
155     }
156 }
157 
158 private final class CppMangleVisitor : Visitor
159 {
160     /// Context used when processing pre-semantic AST
161     private Context context;
162 
163     ABITagContainer abiTags;    /// Container for already-written ABI tags
164     Objects components;         /// array of components available for substitution
165     OutBuffer* buf;             /// append the mangling to buf[]
166     Loc loc;                    /// location for use in error messages
167 
168     /**
169      * Constructor
170      *
171      * Params:
172      *   buf = `OutBuffer` to write the mangling to
173      *   loc = `Loc` of the symbol being mangled
174      */
this(OutBuffer * buf,Loc loc)175     this(OutBuffer* buf, Loc loc)
176     {
177         this.buf = buf;
178         this.loc = loc;
179     }
180 
181     /*****
182      * Entry point. Append mangling to buf[]
183      * Params:
184      *  s = symbol to mangle
185      */
mangleOf(Dsymbol s)186     void mangleOf(Dsymbol s)
187     {
188         if (VarDeclaration vd = s.isVarDeclaration())
189         {
190             mangle_variable(vd, vd.cppnamespace !is null);
191         }
192         else if (FuncDeclaration fd = s.isFuncDeclaration())
193         {
194             mangle_function(fd);
195         }
196         else
197         {
198             assert(0);
199         }
200     }
201 
202     /**
203      * Mangle the return type of a function
204      *
205      * This is called on a templated function type.
206      * Context is set to the `FuncDeclaration`.
207      *
208      * Params:
209      *   preSemantic = the `FuncDeclaration`'s `originalType`
210      */
mangleReturnType(TypeFunction preSemantic)211     void mangleReturnType(TypeFunction preSemantic)
212     {
213         auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
214         Type rt = preSemantic.nextOf();
215         if (tf.isref)
216             rt = rt.referenceTo();
217         auto prev = this.context.push(tf.nextOf());
218         scope (exit) this.context.pop(prev);
219         this.headOfType(rt);
220     }
221 
222     /**
223      * Write a seq-id from an index number, excluding the terminating '_'
224      *
225      * Params:
226      *   idx = the index in a substitution list.
227      *         Note that index 0 has no value, and `S0_` would be the
228      *         substitution at index 1 in the list.
229      *
230      * See-Also:
231      *  https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id
232      */
writeSequenceFromIndex(size_t idx)233     private void writeSequenceFromIndex(size_t idx)
234     {
235         if (idx)
236         {
237             void write_seq_id(size_t i)
238             {
239                 if (i >= 36)
240                 {
241                     write_seq_id(i / 36);
242                     i %= 36;
243                 }
244                 i += (i < 10) ? '0' : 'A' - 10;
245                 buf.writeByte(cast(char)i);
246             }
247 
248             write_seq_id(idx - 1);
249         }
250     }
251 
252     /**
253      * Attempt to perform substitution on `p`
254      *
255      * If `p` already appeared in the mangling, it is stored as
256      * a 'part', and short references in the form of `SX_` can be used.
257      * Note that `p` can be anything: template declaration, struct declaration,
258      * class declaration, namespace...
259      *
260      * Params:
261      *   p = The object to attempt to substitute
262      *   nested = Whether or not `p` is to be considered nested.
263      *            When `true`, `N` will be prepended before the substitution.
264      *
265      * Returns:
266      *   Whether `p` already appeared in the mangling,
267      *   and substitution has been written to `this.buf`.
268      */
269     bool substitute(RootObject p, bool nested = false)
270     {
271         //printf("substitute %s\n", p ? p.toChars() : null);
272         auto i = find(p);
273         if (i < 0)
274             return false;
275 
276         //printf("\tmatch\n");
277         /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ...
278          */
279         if (nested)
280             buf.writeByte('N');
281         buf.writeByte('S');
282         writeSequenceFromIndex(i);
283         buf.writeByte('_');
284         return true;
285     }
286 
287     /******
288      * See if `p` exists in components[]
289      *
290      * Note that components can contain `null` entries,
291      * as the index used in mangling is based on the index in the array.
292      *
293      * If called with an object whose dynamic type is `Nspace`,
294      * calls the `find(Nspace)` overload.
295      *
296      * Returns:
297      *  index if found, -1 if not
298      */
find(RootObject p)299     int find(RootObject p)
300     {
301         //printf("find %p %d %s\n", p, p.dyncast(), p ? p.toChars() : null);
302         scope v = new ComponentVisitor(p);
303         foreach (i, component; components)
304         {
305             if (component)
306                 component.visitObject(v);
307             if (v.result)
308                 return cast(int)i;
309         }
310         return -1;
311     }
312 
313     /*********************
314      * Append p to components[]
315      */
append(RootObject p)316     void append(RootObject p)
317     {
318         //printf("append %p %d %s\n", p, p.dyncast(), p ? p.toChars() : "null");
319         components.push(p);
320     }
321 
322     /**
323      * Write an identifier preceded by its length
324      *
325      * Params:
326      *   ident = `Identifier` to write to `this.buf`
327      */
writeIdentifier(const ref Identifier ident)328     void writeIdentifier(const ref Identifier ident)
329     {
330         const name = ident.toString();
331         this.buf.print(name.length);
332         this.buf.writestring(name);
333     }
334 
335     /**
336      * Insert the leftover ABI tags to the buffer
337      *
338      * This inset ABI tags that hasn't already been written
339      * after the mangled name of the function.
340      * For more details, see the `abiTags` variable.
341      *
342      * Params:
343      *   off  = Offset to insert at
344      *   fd   = Type of the function to mangle the return type of
345      */
writeRemainingTags(size_t off,TypeFunction tf)346     void writeRemainingTags(size_t off, TypeFunction tf)
347     {
348         scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written);
349         tf.next.accept(remainingVisitor);
350         OutBuffer b2;
351         foreach (se; remainingVisitor.toWrite)
352         {
353             auto tag = se.peekString();
354             // We can only insert a slice, and each insert is a memmove,
355             // so use a temporary buffer to keep it efficient.
356             b2.reset();
357             b2.writestring("B");
358             b2.print(tag.length);
359             b2.writestring(tag);
360             this.buf.insert(off, b2[]);
361             off += b2.length;
362         }
363     }
364 
365     /************************
366      * Determine if symbol is indeed the global ::std namespace.
367      * Params:
368      *  s = symbol to check
369      * Returns:
370      *  true if it is ::std
371      */
isStd(Dsymbol s)372     static bool isStd(Dsymbol s)
373     {
374         if (!s)
375             return false;
376 
377         if (auto cnd = s.isCPPNamespaceDeclaration())
378             return isStd(cnd);
379 
380         return (s.ident == Id.std &&    // the right name
381                 s.isNspace() &&         // g++ disallows global "std" for other than a namespace
382                 !getQualifier(s));      // at global level
383     }
384 
385     /// Ditto
isStd(CPPNamespaceDeclaration s)386     static bool isStd(CPPNamespaceDeclaration s)
387     {
388         return s && s.cppnamespace is null && s.ident == Id.std;
389     }
390 
391     /************************
392      * Determine if type is a C++ fundamental type.
393      * Params:
394      *  t = type to check
395      * Returns:
396      *  true if it is a fundamental type
397      */
isFundamentalType(Type t)398     static bool isFundamentalType(Type t)
399     {
400         // First check the target whether some specific ABI is being followed.
401         bool isFundamental = void;
402         if (target.cpp.fundamentalType(t, isFundamental))
403             return isFundamental;
404 
405         if (auto te = t.isTypeEnum())
406         {
407             // Peel off enum type from special types.
408             if (te.sym.isSpecial())
409                 t = te.memType();
410         }
411 
412         // Fundamental arithmetic types:
413         // 1. integral types: bool, char, int, ...
414         // 2. floating point types: float, double, real
415         // 3. void
416         // 4. null pointer: std::nullptr_t (since C++11)
417         if (t.ty == Tvoid || t.ty == Tbool)
418             return true;
419         else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11)
420             return true;
421         else
422             return t.isTypeBasic() && (t.isintegral() || t.isreal());
423     }
424 
425     /******************************
426      * Write the mangled representation of a template argument.
427      * Params:
428      *  ti  = the template instance
429      *  arg = the template argument index
430      */
template_arg(TemplateInstance ti,size_t arg)431     void template_arg(TemplateInstance ti, size_t arg)
432     {
433         TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
434         assert(td);
435         TemplateParameter tp = (*td.parameters)[arg];
436         RootObject o = (*ti.tiargs)[arg];
437 
438         auto prev = this.context.push({
439                 TemplateInstance parentti;
440                 if (this.context.res.dyncast() == DYNCAST.dsymbol)
441                     parentti = this.context.res.asFuncDecl().parent.isTemplateInstance();
442                 else
443                     parentti = this.context.res.asType().toDsymbol(null).parent.isTemplateInstance();
444                 return (*parentti.tiargs)[arg];
445             }());
446         scope (exit) this.context.pop(prev);
447 
448         if (tp.isTemplateTypeParameter())
449         {
450             Type t = isType(o);
451             assert(t);
452             t.accept(this);
453         }
454         else if (TemplateValueParameter tv = tp.isTemplateValueParameter())
455         {
456             // <expr-primary> ::= L <type> <value number> E  # integer literal
457             if (tv.valType.isintegral())
458             {
459                 Expression e = isExpression(o);
460                 assert(e);
461                 buf.writeByte('L');
462                 tv.valType.accept(this);
463                 auto val = e.toUInteger();
464                 if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0)
465                 {
466                     val = -val;
467                     buf.writeByte('n');
468                 }
469                 buf.print(val);
470                 buf.writeByte('E');
471             }
472             else
473             {
474                 ti.error("Internal Compiler Error: C++ `%s` template value parameter is not supported", tv.valType.toChars());
475                 fatal();
476             }
477         }
478         else if (tp.isTemplateAliasParameter())
479         {
480             // Passing a function as alias parameter is the same as passing
481             // `&function`
482             Dsymbol d = isDsymbol(o);
483             Expression e = isExpression(o);
484             if (d && d.isFuncDeclaration())
485             {
486                 // X .. E => template parameter is an expression
487                 // 'ad'   => unary operator ('&')
488                 // L .. E => is a <expr-primary>
489                 buf.writestring("XadL");
490                 mangle_function(d.isFuncDeclaration());
491                 buf.writestring("EE");
492             }
493             else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration())
494             {
495                 VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration();
496                 buf.writeByte('L');
497                 mangle_variable(vd, true);
498                 buf.writeByte('E');
499             }
500             else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember)
501             {
502                 if (!substitute(d))
503                 {
504                     cpp_mangle_name(d, false);
505                 }
506             }
507             else
508             {
509                 ti.error("Internal Compiler Error: C++ `%s` template alias parameter is not supported", o.toChars());
510                 fatal();
511             }
512         }
513         else if (tp.isTemplateThisParameter())
514         {
515             ti.error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o.toChars());
516             fatal();
517         }
518         else
519         {
520             assert(0);
521         }
522     }
523 
524     /******************************
525      * Write the mangled representation of the template arguments.
526      * Params:
527      *  ti = the template instance
528      *  firstArg = index of the first template argument to mangle
529      *             (used for operator overloading)
530      * Returns:
531      *  true if any arguments were written
532      */
533     bool template_args(TemplateInstance ti, int firstArg = 0)
534     {
535         /* <template-args> ::= I <template-arg>+ E
536          */
537         if (!ti || ti.tiargs.dim <= firstArg)   // could happen if std::basic_string is not a template
538             return false;
539         buf.writeByte('I');
540         foreach (i; firstArg .. ti.tiargs.dim)
541         {
542             TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
543             assert(td);
544             TemplateParameter tp = (*td.parameters)[i];
545 
546             /*
547              * <template-arg> ::= <type>               # type or template
548              *                ::= X <expression> E     # expression
549              *                ::= <expr-primary>       # simple expressions
550              *                ::= J <template-arg>* E  # argument pack
551              *
552              * Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.template-arg
553              */
554             if (TemplateTupleParameter tt = tp.isTemplateTupleParameter())
555             {
556                 buf.writeByte('J');     // argument pack
557 
558                 // mangle the rest of the arguments as types
559                 foreach (j; i .. (*ti.tiargs).dim)
560                 {
561                     Type t = isType((*ti.tiargs)[j]);
562                     assert(t);
563                     t.accept(this);
564                 }
565 
566                 buf.writeByte('E');
567                 break;
568             }
569 
570             template_arg(ti, i);
571         }
572         buf.writeByte('E');
573         return true;
574     }
575 
576     /**
577      * Write the symbol `p` if not null, then execute the delegate
578      *
579      * Params:
580      *   p = Symbol to write
581      *   dg = Delegate to execute
582      */
writeChained(Dsymbol p,scope void delegate ()dg)583     void writeChained(Dsymbol p, scope void delegate() dg)
584     {
585         if (p && !p.isModule())
586         {
587             buf.writestring("N");
588             source_name(p, true);
589             dg();
590             buf.writestring("E");
591         }
592         else
593             dg();
594     }
595 
596     /**
597      * Write the name of `s` to the buffer
598      *
599      * Params:
600      *   s = Symbol to write the name of
601      *   haveNE = Whether `N..E` is already part of the mangling
602      *            Because `Nspace` and `CPPNamespaceAttribute` can be
603      *            mixed, this is a mandatory hack.
604      */
605     void source_name(Dsymbol s, bool haveNE = false)
606     {
version(none)607         version (none)
608         {
609             printf("source_name(%s)\n", s.toChars());
610             auto sl = this.buf.peekSlice();
611             assert(sl.length == 0 || haveNE || s.cppnamespace is null || sl != "_ZN");
612         }
613         auto ti = s.isTemplateInstance();
614 
615         if (!ti)
616         {
617             auto ag = s.isAggregateDeclaration();
618             const ident = (ag && ag.mangleOverride) ? ag.mangleOverride.id : s.ident;
619             this.writeNamespace(s.cppnamespace, () {
620                 this.writeIdentifier(ident);
621                 this.abiTags.writeSymbol(s, this);
622                 },
623                 haveNE);
624             return;
625         }
626 
627         bool needsTa = false;
628 
629         // https://issues.dlang.org/show_bug.cgi?id=20413
630         // N..E is not needed when substituting members of the std namespace.
631         // This is observed in the GCC and Clang implementations.
632         // The Itanium specification is not clear enough on this specific case.
633         // References:
634         //   https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.name
635         //   https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression
636         Dsymbol q = getQualifier(ti.tempdecl);
637         Dsymbol ns = ti.tempdecl.cppnamespace;
638         const inStd = ns && isStd(ns) || q && isStd(q);
639         const isNested = !inStd && (ns || q);
640 
641         if (substitute(ti.tempdecl, !haveNE && isNested))
642         {
643             template_args(ti);
644             if (!haveNE && isNested)
645                 buf.writeByte('E');
646             return;
647         }
648         else if (this.writeStdSubstitution(ti, needsTa))
649         {
650             this.abiTags.writeSymbol(ti, this);
651             if (needsTa)
652                 template_args(ti);
653             return;
654         }
655 
656         auto ag = ti.aliasdecl ? ti.aliasdecl.isAggregateDeclaration() : null;
657         if (ag && ag.mangleOverride)
658         {
659             this.writeNamespace(
660                 ti.toAlias().cppnamespace, () {
661                     this.writeIdentifier(ag.mangleOverride.id);
662                     if (ag.mangleOverride.agg && ag.mangleOverride.agg.isInstantiated())
663                     {
664                         auto to = ag.mangleOverride.agg.isInstantiated();
665                         append(to);
666                         this.abiTags.writeSymbol(to.tempdecl, this);
667                         template_args(to);
668                     }
669               }, haveNE);
670         }
671         else
672         {
673             this.writeNamespace(
674                 s.cppnamespace, () {
675                     this.writeIdentifier(ti.tempdecl.toAlias().ident);
676                     append(ti.tempdecl);
677                     this.abiTags.writeSymbol(ti.tempdecl, this);
678                     template_args(ti);
679                 }, haveNE);
680         }
681     }
682 
683     /********
684      * See if s is actually an instance of a template
685      * Params:
686      *  s = symbol
687      * Returns:
688      *  if s is instance of a template, return the instance, otherwise return s
689      */
getInstance(Dsymbol s)690     static Dsymbol getInstance(Dsymbol s)
691     {
692         Dsymbol p = s.toParent();
693         if (p)
694         {
695             if (TemplateInstance ti = p.isTemplateInstance())
696                 return ti;
697         }
698         return s;
699     }
700 
701     /// Get the namespace of a template instance
getTiNamespace(TemplateInstance ti)702     CPPNamespaceDeclaration getTiNamespace(TemplateInstance ti)
703     {
704         // If we receive a pre-semantic `TemplateInstance`,
705         // `cppnamespace` is always `null`
706         return ti.tempdecl ? ti.cppnamespace
707             : this.context.res.asType().toDsymbol(null).cppnamespace;
708     }
709 
710     /********
711      * Get qualifier for `s`, meaning the symbol
712      * that s is in the symbol table of.
713      * The module does not count as a qualifier, because C++
714      * does not have modules.
715      * Params:
716      *  s = symbol that may have a qualifier
717      *      s is rewritten to be TemplateInstance if s is one
718      * Returns:
719      *  qualifier, null if none
720      */
getQualifier(Dsymbol s)721     static Dsymbol getQualifier(Dsymbol s)
722     {
723         Dsymbol p = s.toParent();
724         return (p && !p.isModule()) ? p : null;
725     }
726 
727     // Detect type char
isChar(RootObject o)728     static bool isChar(RootObject o)
729     {
730         Type t = isType(o);
731         return (t && t.equals(Type.tchar));
732     }
733 
734     // Detect type ::std::char_traits<char>
isChar_traits_char(RootObject o)735     bool isChar_traits_char(RootObject o)
736     {
737         return isIdent_char(Id.char_traits, o);
738     }
739 
740     // Detect type ::std::allocator<char>
isAllocator_char(RootObject o)741     bool isAllocator_char(RootObject o)
742     {
743         return isIdent_char(Id.allocator, o);
744     }
745 
746     // Detect type ::std::ident<char>
isIdent_char(Identifier ident,RootObject o)747     bool isIdent_char(Identifier ident, RootObject o)
748     {
749         Type t = isType(o);
750         if (!t || t.ty != Tstruct)
751             return false;
752         Dsymbol s = (cast(TypeStruct)t).toDsymbol(null);
753         if (s.ident != ident)
754             return false;
755         Dsymbol p = s.toParent();
756         if (!p)
757             return false;
758         TemplateInstance ti = p.isTemplateInstance();
759         if (!ti)
760             return false;
761         Dsymbol q = getQualifier(ti);
762         const bool inStd = isStd(q) || isStd(this.getTiNamespace(ti));
763         return inStd && ti.tiargs.dim == 1 && isChar((*ti.tiargs)[0]);
764     }
765 
766     /***
767      * Detect template args <char, ::std::char_traits<char>>
768      * and write st if found.
769      * Returns:
770      *  true if found
771      */
char_std_char_traits_char(TemplateInstance ti,string st)772     bool char_std_char_traits_char(TemplateInstance ti, string st)
773     {
774         if (ti.tiargs.dim == 2 &&
775             isChar((*ti.tiargs)[0]) &&
776             isChar_traits_char((*ti.tiargs)[1]))
777         {
778             buf.writestring(st.ptr);
779             return true;
780         }
781         return false;
782     }
783 
784 
prefix_name(Dsymbol s)785     void prefix_name(Dsymbol s)
786     {
787         //printf("prefix_name(%s)\n", s.toChars());
788         if (substitute(s))
789             return;
790         if (isStd(s))
791             return buf.writestring("St");
792 
793         auto si = getInstance(s);
794         Dsymbol p = getQualifier(si);
795         if (p)
796         {
797             if (isStd(p))
798             {
799                 bool needsTa;
800                 auto ti = si.isTemplateInstance();
801                 if (this.writeStdSubstitution(ti, needsTa))
802                 {
803                     this.abiTags.writeSymbol(ti, this);
804                     if (needsTa)
805                     {
806                         template_args(ti);
807                         append(ti);
808                     }
809                     return;
810                 }
811                 buf.writestring("St");
812             }
813             else
814                 prefix_name(p);
815         }
816         source_name(si, true);
817         if (!isStd(si))
818             /* Do this after the source_name() call to keep components[]
819              * in the right order.
820              * https://issues.dlang.org/show_bug.cgi?id=17947
821              */
822             append(si);
823     }
824 
825     /**
826      * Write common substitution for standard types, such as std::allocator
827      *
828      * This function assumes that the symbol `ti` is in the namespace `std`.
829      *
830      * Params:
831      *   ti = Template instance to consider
832      *   needsTa = If this function returns `true`, this value indicates
833      *             if additional template argument mangling is needed
834      *
835      * Returns:
836      *   `true` if a special std symbol was found
837      */
writeStdSubstitution(TemplateInstance ti,out bool needsTa)838     bool writeStdSubstitution(TemplateInstance ti, out bool needsTa)
839     {
840         if (!ti)
841             return false;
842         if (!isStd(this.getTiNamespace(ti)) && !isStd(getQualifier(ti)))
843             return false;
844 
845         if (ti.name == Id.allocator)
846         {
847             buf.writestring("Sa");
848             needsTa = true;
849             return true;
850         }
851         if (ti.name == Id.basic_string)
852         {
853             // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
854             if (ti.tiargs.dim == 3 &&
855                 isChar((*ti.tiargs)[0]) &&
856                 isChar_traits_char((*ti.tiargs)[1]) &&
857                 isAllocator_char((*ti.tiargs)[2]))
858 
859             {
860                 buf.writestring("Ss");
861                 return true;
862             }
863             buf.writestring("Sb");      // ::std::basic_string
864             needsTa = true;
865             return true;
866         }
867 
868         // ::std::basic_istream<char, ::std::char_traits<char>>
869         if (ti.name == Id.basic_istream &&
870             char_std_char_traits_char(ti, "Si"))
871             return true;
872 
873         // ::std::basic_ostream<char, ::std::char_traits<char>>
874         if (ti.name == Id.basic_ostream &&
875             char_std_char_traits_char(ti, "So"))
876             return true;
877 
878         // ::std::basic_iostream<char, ::std::char_traits<char>>
879         if (ti.name == Id.basic_iostream &&
880             char_std_char_traits_char(ti, "Sd"))
881             return true;
882 
883         return false;
884     }
885 
cpp_mangle_name(Dsymbol s,bool qualified)886     void cpp_mangle_name(Dsymbol s, bool qualified)
887     {
888         //printf("cpp_mangle_name(%s, %d)\n", s.toChars(), qualified);
889         Dsymbol p = s.toParent();
890         Dsymbol se = s;
891         bool write_prefix = true;
892         if (p && p.isTemplateInstance())
893         {
894             se = p;
895             if (find(p.isTemplateInstance().tempdecl) >= 0)
896                 write_prefix = false;
897             p = p.toParent();
898         }
899         if (!p || p.isModule())
900         {
901             source_name(se, false);
902             append(s);
903             return;
904         }
905 
906         if (!isStd(p) || qualified)
907         {
908             buf.writeByte('N');
909             if (write_prefix)
910             {
911                 if (isStd(p))
912                     buf.writestring("St");
913                 else
914                     prefix_name(p);
915             }
916             source_name(se, true);
917             buf.writeByte('E');
918             append(s);
919             return;
920         }
921         /* The N..E is not required if:
922          * 1. the parent is 'std'
923          * 2. 'std' is the initial qualifier
924          * 3. there is no CV-qualifier or a ref-qualifier for a member function
925          * ABI 5.1.8
926          */
927         TemplateInstance ti = se.isTemplateInstance();
928         if (s.ident == Id.allocator)
929         {
930             buf.writestring("Sa"); // "Sa" is short for ::std::allocator
931             template_args(ti);
932         }
933         else if (s.ident == Id.basic_string)
934         {
935             // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
936             if (ti.tiargs.dim == 3 &&
937                 isChar((*ti.tiargs)[0]) &&
938                 isChar_traits_char((*ti.tiargs)[1]) &&
939                 isAllocator_char((*ti.tiargs)[2]))
940             {
941                 buf.writestring("Ss");
942                 return;
943             }
944             buf.writestring("Sb");      // ::std::basic_string
945             template_args(ti);
946         }
947         else
948         {
949             // ::std::basic_istream<char, ::std::char_traits<char>>
950             if (s.ident == Id.basic_istream)
951             {
952                 if (char_std_char_traits_char(ti, "Si"))
953                     return;
954             }
955             else if (s.ident == Id.basic_ostream)
956             {
957                 if (char_std_char_traits_char(ti, "So"))
958                     return;
959             }
960             else if (s.ident == Id.basic_iostream)
961             {
962                 if (char_std_char_traits_char(ti, "Sd"))
963                     return;
964             }
965             buf.writestring("St");
966             source_name(se, true);
967         }
968         append(s);
969     }
970 
971     /**
972      * Write CV-qualifiers to the buffer
973      *
974      * CV-qualifiers are 'r': restrict (unused in D), 'V': volatile, 'K': const
975      *
976      * See_Also:
977      *   https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.CV-qualifiers
978      */
CV_qualifiers(const Type t)979     void CV_qualifiers(const Type t)
980     {
981         if (t.isConst())
982             buf.writeByte('K');
983     }
984 
985     /**
986      * Mangles a variable
987      *
988      * Params:
989      *   d = Variable declaration to mangle
990      *   isNested = Whether this variable is nested, e.g. a template parameter
991      *              or within a namespace
992      */
mangle_variable(VarDeclaration d,bool isNested)993     void mangle_variable(VarDeclaration d, bool isNested)
994     {
995         // fake mangling for fields to fix https://issues.dlang.org/show_bug.cgi?id=16525
996         if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared)))
997         {
998             d.error("Internal Compiler Error: C++ static non-`__gshared` non-`extern` variables not supported");
999             fatal();
1000         }
1001         Dsymbol p = d.toParent();
1002         if (p && !p.isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE"
1003         {
1004             buf.writestring("_ZN");
1005             prefix_name(p);
1006             source_name(d, true);
1007             buf.writeByte('E');
1008         }
1009         else if (isNested)
1010         {
1011             buf.writestring("_Z");
1012             source_name(d, false);
1013         }
1014         else
1015         {
1016             if (auto varTags = ABITagContainer.forSymbol(d))
1017             {
1018                 buf.writestring("_Z");
1019                 source_name(d, false);
1020                 return;
1021             }
1022             if (auto typeTags = ABITagContainer.forSymbol(d.type.toDsymbol(null)))
1023             {
1024                 buf.writestring("_Z");
1025                 source_name(d, false);
1026                 this.abiTags.write(*this.buf, typeTags);
1027                 return;
1028             }
1029             //char beta[6] should mangle as "beta"
1030             buf.writestring(d.ident.toString());
1031         }
1032     }
1033 
mangle_function(FuncDeclaration d)1034     void mangle_function(FuncDeclaration d)
1035     {
1036         //printf("mangle_function(%s)\n", d.toChars());
1037         /*
1038          * <mangled-name> ::= _Z <encoding>
1039          */
1040         buf.writestring("_Z");
1041         this.mangle_function_encoding(d);
1042     }
1043 
mangle_function_encoding(FuncDeclaration d)1044     void mangle_function_encoding(FuncDeclaration d)
1045     {
1046         //printf("mangle_function_encoding(%s)\n", d.toChars());
1047         /*
1048          * <encoding> ::= <function name> <bare-function-type>
1049          *            ::= <data name>
1050          *            ::= <special-name>
1051          */
1052         TypeFunction tf = cast(TypeFunction)d.type;
1053 
1054         if (TemplateDeclaration ftd = getFuncTemplateDecl(d))
1055         {
1056             /* It's an instance of a function template
1057              */
1058             TemplateInstance ti = d.parent.isTemplateInstance();
1059             assert(ti);
1060             this.mangleTemplatedFunction(d, tf, ftd, ti);
1061             return;
1062         }
1063 
1064         Dsymbol p = d.toParent();
1065         if (p && !p.isModule() && tf.linkage == LINK.cpp)
1066         {
1067             this.mangleNestedFuncPrefix(tf, p);
1068 
1069             if (auto ctor = d.isCtorDeclaration())
1070                 buf.writestring(ctor.isCpCtor ? "C2" : "C1");
1071             else if (d.isAggregateDtor())
1072                 buf.writestring("D1");
1073             else if (d.ident && d.ident == Id.assign)
1074                 buf.writestring("aS");
1075             else if (d.ident && d.ident == Id.eq)
1076                 buf.writestring("eq");
1077             else if (d.ident && d.ident == Id.index)
1078                 buf.writestring("ix");
1079             else if (d.ident && d.ident == Id.call)
1080                 buf.writestring("cl");
1081             else
1082                 source_name(d, true);
1083             buf.writeByte('E');
1084         }
1085         else
1086         {
1087             source_name(d, false);
1088         }
1089 
1090         // Save offset for potentially writing tags
1091         const size_t off = this.buf.length();
1092 
1093         // Template args accept extern "C" symbols with special mangling
1094         if (tf.linkage == LINK.cpp)
1095             mangleFunctionParameters(tf.parameterList);
1096 
1097         if (!tf.next.isTypeBasic())
1098             this.writeRemainingTags(off, tf);
1099     }
1100 
1101     /**
1102      * Recursively mangles a non-scoped namespace
1103      *
1104      * Parameters:
1105      *   ns = Namespace to mangle
1106      *   dg = A delegate to write the identifier in this namespace
1107      *   haveNE = When `false` (the default), surround the namespace / dg
1108      *            call with nested name qualifier (`N..E`).
1109      *            Otherwise, they are already present (e.g. `Nspace` was used).
1110      */
1111     void writeNamespace(CPPNamespaceDeclaration ns, scope void delegate() dg,
1112                         bool haveNE = false)
1113     {
runDg()1114         void runDg () { if (dg !is null) dg(); }
1115 
1116         if (ns is null || ns.ident is null)
1117             return runDg();
1118 
1119         if (isStd(ns))
1120         {
1121             if (!substitute(ns))
1122                 buf.writestring("St");
1123             runDg();
1124         }
1125         else if (dg !is null)
1126         {
1127             if (!haveNE)
1128                 buf.writestring("N");
1129             if (!substitute(ns))
1130             {
1131                 this.writeNamespace(ns.cppnamespace, null);
1132                 this.writeIdentifier(ns.ident);
1133                 append(ns);
1134             }
1135             dg();
1136             if (!haveNE)
1137                 buf.writestring("E");
1138         }
1139         else if (!substitute(ns))
1140         {
1141             this.writeNamespace(ns.cppnamespace, null);
1142             this.writeIdentifier(ns.ident);
1143             append(ns);
1144         }
1145     }
1146 
1147     /**
1148      * Mangles a function template to C++
1149      *
1150      * Params:
1151      *   d = Function declaration
1152      *   tf = Function type (casted d.type)
1153      *   ftd = Template declaration (ti.templdecl)
1154      *   ti = Template instance (d.parent)
1155      */
mangleTemplatedFunction(FuncDeclaration d,TypeFunction tf,TemplateDeclaration ftd,TemplateInstance ti)1156     void mangleTemplatedFunction(FuncDeclaration d, TypeFunction tf,
1157                                  TemplateDeclaration ftd, TemplateInstance ti)
1158     {
1159         Dsymbol p = ti.toParent();
1160         // Check if this function is *not* nested
1161         if (!p || p.isModule() || tf.linkage != LINK.cpp)
1162         {
1163             this.context.ti = ti;
1164             this.context.fd = d;
1165             this.context.res = d;
1166             TypeFunction preSemantic = cast(TypeFunction)d.originalType;
1167             auto nspace = ti.toParent();
1168             if (nspace && nspace.isNspace())
1169                 this.writeChained(ti.toParent(), () => source_name(ti, true));
1170             else
1171                 source_name(ti, false);
1172             this.mangleReturnType(preSemantic);
1173             this.mangleFunctionParameters(ParameterList(preSemantic.parameterList.parameters, tf.parameterList.varargs));
1174             return;
1175         }
1176 
1177         // It's a nested function (e.g. a member of an aggregate)
1178         this.mangleNestedFuncPrefix(tf, p);
1179 
1180         if (d.isCtorDeclaration())
1181         {
1182             buf.writestring("C1");
1183             mangleFunctionParameters(tf.parameterList);
1184             return;
1185         }
1186         else if (d.isAggregateDtor())
1187         {
1188             buf.writestring("D1");
1189             mangleFunctionParameters(tf.parameterList);
1190             return;
1191         }
1192 
1193         int firstTemplateArg = 0;
1194         bool appendReturnType = true;
1195         bool isConvertFunc = false;
1196         string symName;
1197 
1198         // test for special symbols
1199         CppOperator whichOp = isCppOperator(ti.name);
1200         final switch (whichOp)
1201         {
1202         case CppOperator.Unknown:
1203             break;
1204         case CppOperator.Cast:
1205             symName = "cv";
1206             firstTemplateArg = 1;
1207             isConvertFunc = true;
1208             appendReturnType = false;
1209             break;
1210         case CppOperator.Assign:
1211             symName = "aS";
1212             break;
1213         case CppOperator.Eq:
1214             symName = "eq";
1215             break;
1216         case CppOperator.Index:
1217             symName = "ix";
1218             break;
1219         case CppOperator.Call:
1220             symName = "cl";
1221             break;
1222         case CppOperator.Unary:
1223         case CppOperator.Binary:
1224         case CppOperator.OpAssign:
1225             TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
1226             assert(td);
1227             assert(ti.tiargs.dim >= 1);
1228             TemplateParameter tp = (*td.parameters)[0];
1229             TemplateValueParameter tv = tp.isTemplateValueParameter();
1230             if (!tv || !tv.valType.isString())
1231                 break; // expecting a string argument to operators!
1232             Expression exp = (*ti.tiargs)[0].isExpression();
1233             StringExp str = exp.toStringExp();
1234             switch (whichOp)
1235             {
1236             case CppOperator.Unary:
1237                 switch (str.peekString())
1238                 {
1239                 case "*":   symName = "de"; goto continue_template;
1240                 case "++":  symName = "pp"; goto continue_template;
1241                 case "--":  symName = "mm"; goto continue_template;
1242                 case "-":   symName = "ng"; goto continue_template;
1243                 case "+":   symName = "ps"; goto continue_template;
1244                 case "~":   symName = "co"; goto continue_template;
1245                 default:    break;
1246                 }
1247                 break;
1248             case CppOperator.Binary:
1249                 switch (str.peekString())
1250                 {
1251                 case ">>":  symName = "rs"; goto continue_template;
1252                 case "<<":  symName = "ls"; goto continue_template;
1253                 case "*":   symName = "ml"; goto continue_template;
1254                 case "-":   symName = "mi"; goto continue_template;
1255                 case "+":   symName = "pl"; goto continue_template;
1256                 case "&":   symName = "an"; goto continue_template;
1257                 case "/":   symName = "dv"; goto continue_template;
1258                 case "%":   symName = "rm"; goto continue_template;
1259                 case "^":   symName = "eo"; goto continue_template;
1260                 case "|":   symName = "or"; goto continue_template;
1261                 default:    break;
1262                 }
1263                 break;
1264             case CppOperator.OpAssign:
1265                 switch (str.peekString())
1266                 {
1267                 case "*":   symName = "mL"; goto continue_template;
1268                 case "+":   symName = "pL"; goto continue_template;
1269                 case "-":   symName = "mI"; goto continue_template;
1270                 case "/":   symName = "dV"; goto continue_template;
1271                 case "%":   symName = "rM"; goto continue_template;
1272                 case ">>":  symName = "rS"; goto continue_template;
1273                 case "<<":  symName = "lS"; goto continue_template;
1274                 case "&":   symName = "aN"; goto continue_template;
1275                 case "|":   symName = "oR"; goto continue_template;
1276                 case "^":   symName = "eO"; goto continue_template;
1277                 default:    break;
1278                 }
1279                 break;
1280             default:
1281                 assert(0);
1282             continue_template:
1283                 firstTemplateArg = 1;
1284                 break;
1285             }
1286             break;
1287         }
1288         if (symName.length == 0)
1289             source_name(ti, true);
1290         else
1291         {
1292             buf.writestring(symName);
1293             if (isConvertFunc)
1294                 template_arg(ti, 0);
1295             appendReturnType = template_args(ti, firstTemplateArg) && appendReturnType;
1296         }
1297         buf.writeByte('E');
1298         if (appendReturnType)
1299             headOfType(tf.nextOf());  // mangle return type
1300         mangleFunctionParameters(tf.parameterList);
1301     }
1302 
1303     /**
1304      * Mangle the parameters of a function
1305      *
1306      * For templated functions, `context.res` is set to the `FuncDeclaration`
1307      *
1308      * Params:
1309      *   parameters = Array of `Parameter` to mangle
1310      *   varargs = if != 0, this function has varargs parameters
1311      */
mangleFunctionParameters(ParameterList parameterList)1312     void mangleFunctionParameters(ParameterList parameterList)
1313     {
1314         int numparams = 0;
1315 
1316         foreach (n, fparam; parameterList)
1317         {
1318             Type t = fparam.type.merge2();
1319             if (fparam.isReference())
1320                 t = t.referenceTo();
1321             else if (fparam.storageClass & STC.lazy_)
1322             {
1323                 // Mangle as delegate
1324                 auto tf = new TypeFunction(ParameterList(), t, LINK.d);
1325                 auto td = new TypeDelegate(tf);
1326                 t = td.merge();
1327             }
1328             else if (Type cpptype = target.cpp.parameterType(t))
1329                 t = cpptype;
1330             if (t.ty == Tsarray)
1331             {
1332                 // Static arrays in D are passed by value; no counterpart in C++
1333                 .error(loc, "Internal Compiler Error: unable to pass static array `%s` to extern(C++) function, use pointer instead",
1334                     t.toChars());
1335                 fatal();
1336             }
1337             auto prev = this.context.push({
1338                     TypeFunction tf;
1339                     if (isDsymbol(this.context.res))
1340                         tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
1341                     else
1342                         tf = this.context.res.asType().isTypeFunction();
1343                     assert(tf);
1344                     return (*tf.parameterList.parameters)[n].type;
1345                 }());
1346             scope (exit) this.context.pop(prev);
1347 
1348             if (this.context.ti && global.params.cplusplus >= CppStdRevision.cpp11)
1349                 handleParamPack(t, this.context.ti.tempdecl.isTemplateDeclaration().parameters);
1350 
1351             headOfType(t);
1352             ++numparams;
1353         }
1354 
1355         if (parameterList.varargs == VarArg.variadic)
1356             buf.writeByte('z');
1357         else if (!numparams)
1358             buf.writeByte('v'); // encode (void) parameters
1359     }
1360 
1361     /****** The rest is type mangling ************/
1362 
error(Type t)1363     void error(Type t)
1364     {
1365         const(char)* p;
1366         if (t.isImmutable())
1367             p = "`immutable` ";
1368         else if (t.isShared())
1369             p = "`shared` ";
1370         else
1371             p = "";
1372         .error(loc, "Internal Compiler Error: %stype `%s` cannot be mapped to C++\n", p, t.toChars());
1373         fatal(); //Fatal, because this error should be handled in frontend
1374     }
1375 
1376     /****************************
1377      * Mangle a type,
1378      * treating it as a Head followed by a Tail.
1379      * Params:
1380      *  t = Head of a type
1381      */
headOfType(Type t)1382     void headOfType(Type t)
1383     {
1384         if (t.ty == Tclass)
1385         {
1386             mangleTypeClass(cast(TypeClass)t, true);
1387         }
1388         else
1389         {
1390             // For value types, strip const/immutable/shared from the head of the type
1391             auto prev = this.context.push(this.context.res.asType().mutableOf().unSharedOf());
1392             scope (exit) this.context.pop(prev);
1393             t.mutableOf().unSharedOf().accept(this);
1394         }
1395     }
1396 
1397     /******
1398      * Write out 1 or 2 character basic type mangling.
1399      * Handle const and substitutions.
1400      * Params:
1401      *  t = type to mangle
1402      *  p = if not 0, then character prefix
1403      *  c = mangling character
1404      */
writeBasicType(Type t,char p,char c)1405     void writeBasicType(Type t, char p, char c)
1406     {
1407         // Only do substitutions for non-fundamental types.
1408         if (!isFundamentalType(t) || t.isConst())
1409         {
1410             if (substitute(t))
1411                 return;
1412             else
1413                 append(t);
1414         }
1415         CV_qualifiers(t);
1416         if (p)
1417             buf.writeByte(p);
1418         buf.writeByte(c);
1419     }
1420 
1421 
1422     /****************
1423      * Write structs and enums.
1424      * Params:
1425      *  t = TypeStruct or TypeEnum
1426      */
doSymbol(Type t)1427     void doSymbol(Type t)
1428     {
1429         if (substitute(t))
1430             return;
1431         CV_qualifiers(t);
1432 
1433         // Handle any target-specific struct types.
1434         if (auto tm = target.cpp.typeMangle(t))
1435         {
1436             buf.writestring(tm);
1437         }
1438         else
1439         {
1440             Dsymbol s = t.toDsymbol(null);
1441             Dsymbol p = s.toParent();
1442             if (p && p.isTemplateInstance())
1443             {
1444                  /* https://issues.dlang.org/show_bug.cgi?id=17947
1445                   * Substitute the template instance symbol, not the struct/enum symbol
1446                   */
1447                 if (substitute(p))
1448                     return;
1449             }
1450             if (!substitute(s))
1451                 cpp_mangle_name(s, false);
1452         }
1453         if (t.isConst())
1454             append(t);
1455     }
1456 
1457 
1458 
1459     /************************
1460      * Mangle a class type.
1461      * If it's the head, treat the initial pointer as a value type.
1462      * Params:
1463      *  t = class type
1464      *  head = true for head of a type
1465      */
mangleTypeClass(TypeClass t,bool head)1466     void mangleTypeClass(TypeClass t, bool head)
1467     {
1468         if (t.isImmutable() || t.isShared())
1469             return error(t);
1470 
1471         /* Mangle as a <pointer to><struct>
1472          */
1473         if (substitute(t))
1474             return;
1475         if (!head)
1476             CV_qualifiers(t);
1477         buf.writeByte('P');
1478 
1479         CV_qualifiers(t);
1480 
1481         {
1482             Dsymbol s = t.toDsymbol(null);
1483             Dsymbol p = s.toParent();
1484             if (p && p.isTemplateInstance())
1485             {
1486                  /* https://issues.dlang.org/show_bug.cgi?id=17947
1487                   * Substitute the template instance symbol, not the class symbol
1488                   */
1489                 if (substitute(p))
1490                     return;
1491             }
1492         }
1493 
1494         if (!substitute(t.sym))
1495         {
1496             cpp_mangle_name(t.sym, false);
1497         }
1498         if (t.isConst())
1499             append(null);  // C++ would have an extra type here
1500         append(t);
1501     }
1502 
1503     /**
1504      * Mangle the prefix of a nested (e.g. member) function
1505      *
1506      * Params:
1507      *   tf = Type of the nested function
1508      *   parent = Parent in which the function is nested
1509      */
mangleNestedFuncPrefix(TypeFunction tf,Dsymbol parent)1510     void mangleNestedFuncPrefix(TypeFunction tf, Dsymbol parent)
1511     {
1512         /* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
1513          *               ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
1514          */
1515         buf.writeByte('N');
1516         CV_qualifiers(tf);
1517 
1518         /* <prefix> ::= <prefix> <unqualified-name>
1519          *          ::= <template-prefix> <template-args>
1520          *          ::= <template-param>
1521          *          ::= # empty
1522          *          ::= <substitution>
1523          *          ::= <prefix> <data-member-prefix>
1524          */
1525         prefix_name(parent);
1526     }
1527 
1528     /**
1529      * Write `Dp` (C++11 function parameter pack prefix) if 't' is a TemplateSequenceParameter (T...).
1530      *
1531      * Params:
1532      *   t      = Parameter type
1533      *   params = Template parameters of the function
1534      */
handleParamPack(Type t,TemplateParameters * params)1535     private void handleParamPack(Type t, TemplateParameters* params)
1536     {
1537         if (t.isTypeReference())
1538             t = t.nextOf();
1539         auto ti = t.isTypeIdentifier();
1540         if (!ti)
1541             return;
1542 
1543         auto idx = templateParamIndex(ti.ident, params);
1544         if (idx < params.length && (*params)[idx].isTemplateTupleParameter())
1545             buf.writestring("Dp");
1546     }
1547 
1548     /**
1549      * Helper function to write a `T..._` template index.
1550      *
1551      * Params:
1552      *   idx   = Index of `param` in the template argument list
1553      *   param = Template parameter to mangle
1554      */
writeTemplateArgIndex(size_t idx,TemplateParameter param)1555     private void writeTemplateArgIndex(size_t idx, TemplateParameter param)
1556     {
1557         // expressions are mangled in <X..E>
1558         if (param.isTemplateValueParameter())
1559             buf.writeByte('X');
1560         buf.writeByte('T');
1561         writeSequenceFromIndex(idx);
1562         buf.writeByte('_');
1563         if (param.isTemplateValueParameter())
1564             buf.writeByte('E');
1565     }
1566 
1567     /**
1568      * Given an array of template parameters and an identifier,
1569      * returns the index of the identifier in that array.
1570      *
1571      * Params:
1572      *   ident = Identifier for which substitution is attempted
1573      *           (e.g. `void func(T)(T param)` => `T` from `T param`)
1574      *   params = `TemplateParameters` of the enclosing symbol
1575      *           (in the previous example, `func`'s template parameters)
1576      *
1577      * Returns:
1578      *   The index of the identifier match in `params`,
1579      *   or `params.length` if there wasn't any match.
1580      */
templateParamIndex(const ref Identifier ident,TemplateParameters * params)1581     private static size_t templateParamIndex(
1582         const ref Identifier ident, TemplateParameters* params)
1583     {
1584         foreach (idx, param; *params)
1585             if (param.ident == ident)
1586                 return idx;
1587         return params.length;
1588     }
1589 
1590     /**
1591      * Given a template instance `t`, write its qualified name
1592      * without the template parameter list
1593      *
1594      * Params:
1595      *   t = Post-parsing `TemplateInstance` pointing to the symbol
1596      *       to mangle (one level deep)
1597      *   dg = Delegate to execute after writing the qualified symbol
1598      *
1599      */
writeQualified(TemplateInstance t,scope void delegate ()dg)1600     private void writeQualified(TemplateInstance t, scope void delegate() dg)
1601     {
1602         auto type = isType(this.context.res);
1603         if (!type)
1604         {
1605             this.writeIdentifier(t.name);
1606             return dg();
1607         }
1608         auto sym1 = type.toDsymbol(null);
1609         if (!sym1)
1610         {
1611             this.writeIdentifier(t.name);
1612             return dg();
1613         }
1614         // Get the template instance
1615         auto sym = getQualifier(sym1);
1616         auto sym2 = getQualifier(sym);
1617         if (sym2 && isStd(sym2)) // Nspace path
1618         {
1619             bool unused;
1620             assert(sym.isTemplateInstance());
1621             if (this.writeStdSubstitution(sym.isTemplateInstance(), unused))
1622                 return dg();
1623             // std names don't require `N..E`
1624             buf.writestring("St");
1625             this.writeIdentifier(t.name);
1626             this.append(t);
1627             return dg();
1628         }
1629         else if (sym2)
1630         {
1631             buf.writestring("N");
1632             if (!this.substitute(sym2))
1633                 sym2.accept(this);
1634         }
1635         this.writeNamespace(
1636             sym1.cppnamespace, () {
1637                 this.writeIdentifier(t.name);
1638                 this.append(t);
1639                 dg();
1640             });
1641         if (sym2)
1642             buf.writestring("E");
1643     }
1644 
1645 extern(C++):
1646 
1647     alias visit = Visitor.visit;
1648 
visit(TypeNull t)1649     override void visit(TypeNull t)
1650     {
1651         if (t.isImmutable() || t.isShared())
1652             return error(t);
1653 
1654         writeBasicType(t, 'D', 'n');
1655     }
1656 
visit(TypeNoreturn t)1657     override void visit(TypeNoreturn t)
1658     {
1659         if (t.isImmutable() || t.isShared())
1660             return error(t);
1661 
1662         writeBasicType(t, 0, 'v');      // mangle like `void`
1663     }
1664 
visit(TypeBasic t)1665     override void visit(TypeBasic t)
1666     {
1667         if (t.isImmutable() || t.isShared())
1668             return error(t);
1669 
1670         // Handle any target-specific basic types.
1671         if (auto tm = target.cpp.typeMangle(t))
1672         {
1673             // Only do substitutions for non-fundamental types.
1674             if (!isFundamentalType(t) || t.isConst())
1675             {
1676                 if (substitute(t))
1677                     return;
1678                 else
1679                     append(t);
1680             }
1681             CV_qualifiers(t);
1682             buf.writestring(tm);
1683             return;
1684         }
1685 
1686         /* <builtin-type>:
1687          * v        void
1688          * w        wchar_t
1689          * b        bool
1690          * c        char
1691          * a        signed char
1692          * h        unsigned char
1693          * s        short
1694          * t        unsigned short
1695          * i        int
1696          * j        unsigned int
1697          * l        long
1698          * m        unsigned long
1699          * x        long long, __int64
1700          * y        unsigned long long, __int64
1701          * n        __int128
1702          * o        unsigned __int128
1703          * f        float
1704          * d        double
1705          * e        long double, __float80
1706          * g        __float128
1707          * z        ellipsis
1708          * Dd       64 bit IEEE 754r decimal floating point
1709          * De       128 bit IEEE 754r decimal floating point
1710          * Df       32 bit IEEE 754r decimal floating point
1711          * Dh       16 bit IEEE 754r half-precision floating point
1712          * Di       char32_t
1713          * Ds       char16_t
1714          * u <source-name>  # vendor extended type
1715          */
1716         if (t.isimaginary() || t.iscomplex())
1717         {
1718             // https://issues.dlang.org/show_bug.cgi?id=22806
1719             // Complex and imaginary types are represented in the same way as
1720             // arrays or vectors in C++.  First substitute the outer type, then
1721             // write out the mangle string of the underlying type.
1722             if (substitute(t))
1723                 return;
1724             append(t);
1725             CV_qualifiers(t);
1726 
1727             if (t.isimaginary())
1728                 buf.writeByte('G'); // 'G' means imaginary
1729             else
1730                 buf.writeByte('C'); // 'C' means complex
1731 
1732             switch (t.ty)
1733             {
1734                 case Timaginary32:
1735                 case Tcomplex32:
1736                     return Type.tfloat32.accept(this);
1737                 case Timaginary64:
1738                 case Tcomplex64:
1739                     return Type.tfloat64.accept(this);
1740                 case Timaginary80:
1741                 case Tcomplex80:
1742                     return Type.tfloat80.accept(this);
1743                 default:
1744                     assert(0);
1745             }
1746         }
1747 
1748         char c;
1749         char p = 0;
1750         switch (t.ty)
1751         {
1752             case Tvoid:                 c = 'v';        break;
1753             case Tint8:                 c = 'a';        break;
1754             case Tuns8:                 c = 'h';        break;
1755             case Tint16:                c = 's';        break;
1756             case Tuns16:                c = 't';        break;
1757             case Tint32:                c = 'i';        break;
1758             case Tuns32:                c = 'j';        break;
1759             case Tfloat32:              c = 'f';        break;
1760             case Tint64:
1761                 c = target.c.longsize == 8 ? 'l' : 'x';
1762                 break;
1763             case Tuns64:
1764                 c = target.c.longsize == 8 ? 'm' : 'y';
1765                 break;
1766             case Tint128:                c = 'n';       break;
1767             case Tuns128:                c = 'o';       break;
1768             case Tfloat64:               c = 'd';       break;
1769             case Tfloat80:               c = 'e';       break;
1770             case Tbool:                  c = 'b';       break;
1771             case Tchar:                  c = 'c';       break;
1772             case Twchar:        p = 'D'; c = 's';       break;  // since C++11
1773             case Tdchar:        p = 'D'; c = 'i';       break;  // since C++11
1774 
1775             default:
1776                 return error(t);
1777         }
1778         writeBasicType(t, p, c);
1779     }
1780 
visit(TypeVector t)1781     override void visit(TypeVector t)
1782     {
1783         if (t.isImmutable() || t.isShared())
1784             return error(t);
1785 
1786         if (substitute(t))
1787             return;
1788         append(t);
1789         CV_qualifiers(t);
1790 
1791         // Handle any target-specific vector types.
1792         if (auto tm = target.cpp.typeMangle(t))
1793         {
1794             buf.writestring(tm);
1795         }
1796         else
1797         {
1798             assert(t.basetype && t.basetype.ty == Tsarray);
1799             auto tsa = t.basetype.isTypeSArray();
1800             assert(tsa.dim);
1801             buf.writestring("Dv");          // -- Gnu ABI v.4
1802             buf.print(tsa.dim.toInteger());
1803             buf.writeByte('_');
1804             t.basetype.nextOf().accept(this);
1805         }
1806     }
1807 
visit(TypeSArray t)1808     override void visit(TypeSArray t)
1809     {
1810         if (t.isImmutable() || t.isShared())
1811             return error(t);
1812 
1813         if (!substitute(t))
1814             append(t);
1815         CV_qualifiers(t);
1816         buf.writeByte('A');
1817         buf.print(t.dim ? t.dim.toInteger() : 0);
1818         buf.writeByte('_');
1819         t.next.accept(this);
1820     }
1821 
visit(TypePointer t)1822     override void visit(TypePointer t)
1823     {
1824         if (t.isImmutable() || t.isShared())
1825             return error(t);
1826 
1827         // Check for const - Since we cannot represent C++'s `char* const`,
1828         // and `const char* const` (a.k.a `const(char*)` in D) is mangled
1829         // the same as `const char*` (`const(char)*` in D), we need to add
1830         // an extra `K` if `nextOf()` is `const`, before substitution
1831         CV_qualifiers(t);
1832         if (substitute(t))
1833             return;
1834         buf.writeByte('P');
1835         auto prev = this.context.push(this.context.res.asType().nextOf());
1836         scope (exit) this.context.pop(prev);
1837         t.next.accept(this);
1838         append(t);
1839     }
1840 
visit(TypeReference t)1841     override void visit(TypeReference t)
1842     {
1843         if (substitute(t))
1844             return;
1845         buf.writeByte('R');
1846         CV_qualifiers(t.nextOf());
1847         headOfType(t.nextOf());
1848         if (t.nextOf().isConst())
1849             append(t.nextOf());
1850         append(t);
1851     }
1852 
visit(TypeFunction t)1853     override void visit(TypeFunction t)
1854     {
1855         /*
1856          *  <function-type> ::= F [Y] <bare-function-type> E
1857          *  <bare-function-type> ::= <signature type>+
1858          *  # types are possible return type, then parameter types
1859          */
1860         /* ABI says:
1861             "The type of a non-static member function is considered to be different,
1862             for the purposes of substitution, from the type of a namespace-scope or
1863             static member function whose type appears similar. The types of two
1864             non-static member functions are considered to be different, for the
1865             purposes of substitution, if the functions are members of different
1866             classes. In other words, for the purposes of substitution, the class of
1867             which the function is a member is considered part of the type of
1868             function."
1869 
1870             BUG: Right now, types of functions are never merged, so our simplistic
1871             component matcher always finds them to be different.
1872             We should use Type.equals on these, and use different
1873             TypeFunctions for non-static member functions, and non-static
1874             member functions of different classes.
1875          */
1876         if (substitute(t))
1877             return;
1878         buf.writeByte('F');
1879         if (t.linkage == LINK.c)
1880             buf.writeByte('Y');
1881         Type tn = t.next;
1882         if (t.isref)
1883             tn = tn.referenceTo();
1884         tn.accept(this);
1885         mangleFunctionParameters(t.parameterList);
1886         buf.writeByte('E');
1887         append(t);
1888     }
1889 
visit(TypeStruct t)1890     override void visit(TypeStruct t)
1891     {
1892         if (t.isImmutable() || t.isShared())
1893             return error(t);
1894         //printf("TypeStruct %s\n", t.toChars());
1895         doSymbol(t);
1896     }
1897 
visit(TypeEnum t)1898     override void visit(TypeEnum t)
1899     {
1900         if (t.isImmutable() || t.isShared())
1901             return error(t);
1902 
1903         /* __c_(u)long(long) and others get special mangling
1904          */
1905         const id = t.sym.ident;
1906         //printf("enum id = '%s'\n", id.toChars());
1907         if (id == Id.__c_long)
1908             return writeBasicType(t, 0, 'l');
1909         else if (id == Id.__c_ulong)
1910             return writeBasicType(t, 0, 'm');
1911         else if (id == Id.__c_char)
1912             return writeBasicType(t, 0, 'c');
1913         else if (id == Id.__c_wchar_t)
1914             return writeBasicType(t, 0, 'w');
1915         else if (id == Id.__c_longlong)
1916             return writeBasicType(t, 0, 'x');
1917         else if (id == Id.__c_ulonglong)
1918             return writeBasicType(t, 0, 'y');
1919         else if (id == Id.__c_complex_float)
1920             return Type.tcomplex32.accept(this);
1921         else if (id == Id.__c_complex_double)
1922             return Type.tcomplex64.accept(this);
1923         else if (id == Id.__c_complex_real)
1924             return Type.tcomplex80.accept(this);
1925 
1926         doSymbol(t);
1927     }
1928 
visit(TypeClass t)1929     override void visit(TypeClass t)
1930     {
1931         mangleTypeClass(t, false);
1932     }
1933 
1934     /**
1935      * Performs template parameter substitution
1936      *
1937      * Mangling is performed on a copy of the post-parsing AST before
1938      * any semantic pass is run.
1939      * There is no easy way to link a type to the template parameters
1940      * once semantic has run, because:
1941      * - the `TemplateInstance` installs aliases in its scope to its params
1942      * - `AliasDeclaration`s are resolved in many places
1943      * - semantic passes are destructive, so the `TypeIdentifier` gets lost
1944      *
1945      * As a result, the best approach with the current architecture is to:
1946      * - Run the visitor on the `originalType` of the function,
1947      *   looking up any `TypeIdentifier` at the template scope when found.
1948      * - Fallback to the post-semantic `TypeFunction` when the identifier is
1949      *   not a template parameter.
1950      */
visit(TypeIdentifier t)1951     override void visit(TypeIdentifier t)
1952     {
1953         auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl;
1954         assert(decl.parameters !is null);
1955         auto idx = templateParamIndex(t.ident, decl.parameters);
1956         // If not found, default to the post-semantic type
1957         if (idx >= decl.parameters.length)
1958             return this.context.res.visitObject(this);
1959 
1960         auto param = (*decl.parameters)[idx];
1961         if (auto type = this.context.res.isType())
1962             CV_qualifiers(type);
1963         // Otherwise, attempt substitution (`S_` takes precedence on `T_`)
1964         if (this.substitute(param))
1965             return;
1966 
1967         // If substitution failed, write `TX_` where `X` is the index
1968         this.writeTemplateArgIndex(idx, param);
1969         this.append(param);
1970         // Write the ABI tags, if any
1971         if (auto sym = this.context.res.isDsymbol())
1972             this.abiTags.writeSymbol(sym, this);
1973     }
1974 
1975     /// Ditto
visit(TypeInstance t)1976     override void visit(TypeInstance t)
1977     {
1978         assert(t.tempinst !is null);
1979         t.tempinst.accept(this);
1980     }
1981 
1982     /**
1983      * Mangles a `TemplateInstance`
1984      *
1985      * A `TemplateInstance` can be found either in the parameter,
1986      * or the return value.
1987      * Arguments to the template instance needs to be mangled but the template
1988      * can be partially substituted, so for example the following:
1989      * `Container!(T, Val) func16479_12 (alias Container, T, int Val) ()`
1990      * will mangle the return value part to "T_IT0_XT1_EE"
1991      */
visit(TemplateInstance t)1992     override void visit(TemplateInstance t)
1993     {
1994         // Template names are substituted, but args still need to be written
1995         void writeArgs ()
1996         {
1997             buf.writeByte('I');
1998             // When visiting the arguments, the context will be set to the
1999             // resolved type
2000             auto analyzed_ti = this.context.res.asType().toDsymbol(null).isInstantiated();
2001             auto prev = this.context;
2002             scope (exit) this.context.pop(prev);
2003             foreach (idx, RootObject o; *t.tiargs)
2004             {
2005                 this.context.res = (*analyzed_ti.tiargs)[idx];
2006                 o.visitObject(this);
2007             }
2008             if (analyzed_ti.tiargs.dim > t.tiargs.dim)
2009             {
2010                 // If the resolved AST has more args than the parse one,
2011                 // we have default arguments
2012                 auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters;
2013                 foreach (idx, arg; (*oparams)[t.tiargs.dim .. $])
2014                 {
2015                     this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.dim];
2016 
2017                     if (auto ttp = arg.isTemplateTypeParameter())
2018                         ttp.defaultType.accept(this);
2019                     else if (auto tvp = arg.isTemplateValueParameter())
2020                         tvp.defaultValue.accept(this);
2021                     else if (auto tvp = arg.isTemplateThisParameter())
2022                         tvp.defaultType.accept(this);
2023                     else if (auto tvp = arg.isTemplateAliasParameter())
2024                         tvp.defaultAlias.visitObject(this);
2025                     else
2026                         assert(0, arg.toString());
2027                 }
2028             }
2029             buf.writeByte('E');
2030         }
2031 
2032         // `name` is used, not `ident`
2033         assert(t.name !is null);
2034         assert(t.tiargs !is null);
2035 
2036         bool needsTa;
2037         auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl;
2038         // Attempt to substitute the template itself
2039         auto idx = templateParamIndex(t.name, decl.parameters);
2040         if (idx < decl.parameters.length)
2041         {
2042             auto param = (*decl.parameters)[idx];
2043             if (auto type = t.getType())
2044                 CV_qualifiers(type);
2045             if (this.substitute(param))
2046                 return;
2047             this.writeTemplateArgIndex(idx, param);
2048             this.append(param);
2049             writeArgs();
2050         }
2051         else if (this.writeStdSubstitution(t, needsTa))
2052         {
2053             if (needsTa)
2054                 writeArgs();
2055         }
2056         else if (!this.substitute(t))
2057             this.writeQualified(t, &writeArgs);
2058     }
2059 
2060     /// Ditto
visit(IntegerExp t)2061     override void visit(IntegerExp t)
2062     {
2063         this.buf.writeByte('L');
2064         t.type.accept(this);
2065         this.buf.print(t.getInteger());
2066         this.buf.writeByte('E');
2067     }
2068 
visit(Nspace t)2069     override void visit(Nspace t)
2070     {
2071         if (auto p = getQualifier(t))
2072             p.accept(this);
2073 
2074         if (isStd(t))
2075             buf.writestring("St");
2076         else
2077         {
2078             this.writeIdentifier(t.ident);
2079             this.append(t);
2080         }
2081     }
2082 
visit(Type t)2083     override void visit(Type t)
2084     {
2085         error(t);
2086     }
2087 
visit(Tuple t)2088     void visit(Tuple t)
2089     {
2090         assert(0);
2091     }
2092 }
2093 
2094 /// Helper code to visit `RootObject`, as it doesn't define `accept`,
2095 /// only its direct subtypes do.
2096 private void visitObject(V : Visitor)(RootObject o, V this_)
2097 {
2098     assert(o !is null);
2099     if (Type ta = isType(o))
2100         ta.accept(this_);
2101     else if (Expression ea = isExpression(o))
2102         ea.accept(this_);
2103     else if (Dsymbol sa = isDsymbol(o))
2104         sa.accept(this_);
2105     else if (TemplateParameter t = isTemplateParameter(o))
2106         t.accept(this_);
2107     else if (Tuple t = isTuple(o))
2108         // `Tuple` inherits `RootObject` and does not define accept
2109         // For this reason, this uses static dispatch on the visitor
2110         this_.visit(t);
2111     else
2112         assert(0, o.toString());
2113 }
2114 
2115 /// Helper function to safely get a type out of a `RootObject`
asType(RootObject o)2116 private Type asType(RootObject o)
2117 {
2118     Type ta = isType(o);
2119     // When called with context.res as argument, it can be `FuncDeclaration`
2120     if (!ta && o.asFuncDecl())
2121         ta = (cast(FuncDeclaration)o).type;
2122     assert(ta !is null, o.toString());
2123     return ta;
2124 }
2125 
2126 /// Helper function to safely get a `FuncDeclaration` out of a `RootObject`
asFuncDecl(RootObject o)2127 private FuncDeclaration asFuncDecl(RootObject o)
2128 {
2129     Dsymbol d = isDsymbol(o);
2130     assert(d !is null);
2131     auto fd = d.isFuncDeclaration();
2132     assert(fd !is null);
2133     return fd;
2134 }
2135 
2136 /// Helper class to compare entries in components
2137 private extern(C++) final class ComponentVisitor : Visitor
2138 {
2139     /// Only one of the following is not `null`, it's always
2140     /// the most specialized type, set from the ctor
2141     private Nspace namespace;
2142 
2143     /// Ditto
2144     private CPPNamespaceDeclaration namespace2;
2145 
2146     /// Ditto
2147     private TypePointer tpointer;
2148 
2149     /// Ditto
2150     private TypeReference tref;
2151 
2152     /// Ditto
2153     private TypeIdentifier tident;
2154 
2155     /// Least specialized type
2156     private RootObject object;
2157 
2158     /// Set to the result of the comparison
2159     private bool result;
2160 
this(RootObject base)2161     public this(RootObject base)
2162     {
2163         switch (base.dyncast())
2164         {
2165         case DYNCAST.dsymbol:
2166             if (auto ns = (cast(Dsymbol)base).isNspace())
2167                 this.namespace = ns;
2168             else if (auto ns = (cast(Dsymbol)base).isCPPNamespaceDeclaration())
2169                 this.namespace2 = ns;
2170             else
2171                 goto default;
2172             break;
2173 
2174         case DYNCAST.type:
2175             auto t = cast(Type)base;
2176             if (t.ty == Tpointer)
2177                 this.tpointer = cast(TypePointer)t;
2178             else if (t.ty == Treference)
2179                 this.tref = cast(TypeReference)t;
2180             else if (t.ty == Tident)
2181                 this.tident = cast(TypeIdentifier)t;
2182             else
2183                 goto default;
2184             break;
2185 
2186         // Note: ABI tags are also handled here (they are TupleExp of StringExp)
2187         default:
2188             this.object = base;
2189         }
2190     }
2191 
2192     /// Introduce base class overloads
2193     alias visit = Visitor.visit;
2194 
2195     /// Least specialized overload of each direct child of `RootObject`
visit(Dsymbol o)2196     public override void visit(Dsymbol o)
2197     {
2198         this.result = this.object && this.object == o;
2199     }
2200 
2201     /// Ditto
visit(Expression o)2202     public override void visit(Expression o)
2203     {
2204         this.result = this.object && this.object == o;
2205     }
2206 
2207     /// Ditto
visit(Tuple o)2208     public void visit(Tuple o)
2209     {
2210         this.result = this.object && this.object == o;
2211     }
2212 
2213     /// Ditto
visit(Type o)2214     public override void visit(Type o)
2215     {
2216         this.result = this.object && this.object == o;
2217     }
2218 
2219     /// Ditto
visit(TemplateParameter o)2220     public override void visit(TemplateParameter o)
2221     {
2222         this.result = this.object && this.object == o;
2223     }
2224 
2225     /**
2226      * This overload handles composed types including template parameters
2227      *
2228      * Components for substitutions include "next" type.
2229      * For example, if `ref T` is present, `ref T` and `T` will be present
2230      * in the substitution array.
2231      * But since we don't have the final/merged type, we cannot rely on
2232      * object comparison, and need to recurse instead.
2233      */
visit(TypeReference o)2234     public override void visit(TypeReference o)
2235     {
2236         if (!this.tref)
2237             return;
2238         if (this.tref == o)
2239             this.result = true;
2240         else
2241         {
2242             // It might be a reference to a template parameter that we already
2243             // saw, so we need to recurse
2244             scope v = new ComponentVisitor(this.tref.next);
2245             o.next.visitObject(v);
2246             this.result = v.result;
2247         }
2248     }
2249 
2250     /// Ditto
visit(TypePointer o)2251     public override void visit(TypePointer o)
2252     {
2253         if (!this.tpointer)
2254             return;
2255         if (this.tpointer == o)
2256             this.result = true;
2257         else
2258         {
2259             // It might be a pointer to a template parameter that we already
2260             // saw, so we need to recurse
2261             scope v = new ComponentVisitor(this.tpointer.next);
2262             o.next.visitObject(v);
2263             this.result = v.result;
2264         }
2265     }
2266 
2267     /// Ditto
visit(TypeIdentifier o)2268     public override void visit(TypeIdentifier o)
2269     {
2270         /// Since we know they are at the same level, scope resolution will
2271         /// give us the same symbol, thus we can just compare ident.
2272         this.result = (this.tident && (this.tident.ident == o.ident));
2273     }
2274 
2275     /**
2276      * Overload which accepts a Namespace
2277      *
2278      * It is very common for large C++ projects to have multiple files sharing
2279      * the same `namespace`. If any D project adopts the same approach
2280      * (e.g. separating data structures from functions), it will lead to two
2281      * `Nspace` objects being instantiated, with different addresses.
2282      * At the same time, we cannot compare just any Dsymbol via identifier,
2283      * because it messes with templates.
2284      *
2285      * See_Also:
2286      *  https://issues.dlang.org/show_bug.cgi?id=18922
2287      *
2288      * Params:
2289      *   ns = C++ namespace to do substitution for
2290      */
visit(Nspace ns)2291     public override void visit(Nspace ns)
2292     {
2293         this.result = isNamespaceEqual(this.namespace, ns)
2294             || isNamespaceEqual(this.namespace2, ns);
2295     }
2296 
2297     /// Ditto
visit(CPPNamespaceDeclaration ns)2298     public override void visit(CPPNamespaceDeclaration ns)
2299     {
2300         this.result = isNamespaceEqual(this.namespace, ns)
2301             || isNamespaceEqual(this.namespace2, ns);
2302     }
2303 }
2304 
2305 /// Transitional functions for `CPPNamespaceDeclaration` / `Nspace`
2306 /// Remove when `Nspace` is removed.
isNamespaceEqual(Nspace a,Nspace b)2307 private bool isNamespaceEqual (Nspace a, Nspace b)
2308 {
2309     if (a is null || b is null)
2310         return false;
2311     return a.equals(b);
2312 }
2313 
2314 /// Ditto
isNamespaceEqual(Nspace a,CPPNamespaceDeclaration b)2315 private bool isNamespaceEqual (Nspace a, CPPNamespaceDeclaration b)
2316 {
2317     return isNamespaceEqual(b, a);
2318 }
2319 
2320 /// Ditto
2321 private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = 0)
2322 {
2323     if ((a is null) != (b is null))
2324         return false;
2325     if (!a.ident.equals(b.ident))
2326         return false;
2327 
2328     // We need to see if there's more ident enclosing
2329     if (auto pb = b.toParent().isNspace())
2330         return isNamespaceEqual(a.cppnamespace, pb);
2331     else
2332         return a.cppnamespace is null;
2333 }
2334 
2335 /// Returns:
2336 ///   Whether  two `CPPNamespaceDeclaration` are equals
isNamespaceEqual(CPPNamespaceDeclaration a,CPPNamespaceDeclaration b)2337 private bool isNamespaceEqual (CPPNamespaceDeclaration a, CPPNamespaceDeclaration b)
2338 {
2339     if (a is null || b is null)
2340         return false;
2341 
2342     if ((a.cppnamespace is null) != (b.cppnamespace is null))
2343         return false;
2344     if (a.ident != b.ident)
2345         return false;
2346     return a.cppnamespace is null ? true : isNamespaceEqual(a.cppnamespace, b.cppnamespace);
2347 }
2348 
2349 /**
2350  * A container for ABI tags
2351  *
2352  * At its hearth, there is a sorted array of ABI tags having been written
2353  * already. ABI tags can be present on parameters, template parameters,
2354  * return value, and varaible. ABI tags for a given type needs to be written
2355  * sorted. When a function returns a type that has ABI tags, only the tags that
2356  * haven't been printed as part of the mangling (e.g. arguments) are written
2357  * directly after the function name.
2358  *
2359  * This means that:
2360  * ---
2361  * /++ C++ type definitions:
2362  * struct [[gnu::abi_tag("tag1")]] Struct1 {};
2363  * struct [[gnu::abi_tag("tag2")]] Struct2 {};
2364  * // Can also be: "tag2", "tag1", since tags are sorted.
2365  * struct [[gnu::abi_tag("tag1", "tag2")]] Struct3 {};
2366  * +/
2367  * // Functions definitions:
2368  * Struct3 func1 (Struct1);
2369  * Struct3 func2 (Struct2);
2370  * Struct3 func3 (Struct2, Struct1);
2371  * ---
2372  * Will be respectively pseudo-mangled (part of interest between stars) as:
2373  * "_Z4 func1 *B4tag2* ParamsMangling" (ParamsMangling includes tag1),
2374  * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes tag2),
2375  * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes both).
2376  *
2377  * This is why why need to keep a list of tags that were written,
2378  * and insert the missing one after parameter mangling has been written.
2379  * Since there's a lot of operations that are not easily doable in DMD
2380  * (since we can't use Phobos), this special container is implemented.
2381  */
2382 private struct ABITagContainer
2383 {
2384     private Array!StringExp written;
2385 
forSymbolABITagContainer2386     static ArrayLiteralExp forSymbol (Dsymbol s)
2387     {
2388         if (!s)
2389             return null;
2390         // If this is a template instance, we want the declaration,
2391         // as that's where the UDAs are
2392         if (auto ti = s.isTemplateInstance())
2393             s = ti.tempdecl;
2394         if (!s.userAttribDecl || !s.userAttribDecl.atts)
2395             return null;
2396 
2397         foreach (exp; *s.userAttribDecl.atts)
2398         {
2399             if (UserAttributeDeclaration.isGNUABITag(exp))
2400                 return (*exp.isStructLiteralExp().elements)[0]
2401                     .isArrayLiteralExp();
2402         }
2403         return null;
2404     }
2405 
writeSymbolABITagContainer2406     void writeSymbol(Dsymbol s, CppMangleVisitor self)
2407     {
2408         auto tale = forSymbol(s);
2409         if (!tale) return;
2410         if (self.substitute(tale))
2411             return;
2412         this.write(*self.buf, tale);
2413     }
2414 
2415     /**
2416      * Write an ArrayLiteralExp (expected to be an ABI tag) to the buffer
2417      *
2418      * Params:
2419      *   buf = Buffer to write mangling to
2420      *   ale = GNU ABI tag array literal expression, semantically analyzed
2421      */
2422     void write (ref OutBuffer buf, ArrayLiteralExp ale, bool skipKnown = false)
2423     {
writeElemABITagContainer2424         void writeElem (StringExp exp)
2425         {
2426             const tag = exp.peekString();
2427             buf.writestring("B");
2428             buf.print(tag.length);
2429             buf.writestring(tag);
2430         }
2431 
2432         bool match;
2433         foreach (exp; *ale.elements)
2434         {
2435             auto elem = exp.toStringExp();
2436             auto idx = closestIndex(this.written[], elem, match);
2437             if (!match)
2438             {
2439                 writeElem(elem);
2440                 this.written.insert(idx, elem);
2441             }
2442             else if (!skipKnown)
2443                 writeElem(elem);
2444         }
2445     }
2446 }
2447 
2448 /**
2449  * Returns the closest index to to `exp` in `slice`
2450  *
2451  * Performs a binary search on `slice` (assumes `slice` is sorted),
2452  * and returns either `exp`'s index in `slice` if `exact` is `true`,
2453  * or the index at which `exp` can be inserted in `slice` if `exact is `false`.
2454  * Inserting `exp` at the return value will keep the array sorted.
2455  *
2456  * Params:
2457  *   slice = The sorted slice to search into
2458  *   exp   = The string expression to search for
2459  *   exact = If `true` on return, `exp` was found in `slice`
2460  *
2461  * Returns:
2462  *   Either the index to insert `exp` at (if `exact == false`),
2463  *   or the index of `exp` in `slice`.
2464  */
closestIndex(const (StringExp)[]slice,StringExp exp,out bool exact)2465 private size_t closestIndex (const(StringExp)[] slice, StringExp exp, out bool exact)
2466 {
2467     if (!slice.length) return 0;
2468 
2469     const StringExp* first = slice.ptr;
2470     while (true)
2471     {
2472         int res = dstrcmp(exp.peekString(), slice[$ / 2].peekString());
2473         if (res == 0)
2474         {
2475             exact = true;
2476             return (&slice[$/2] - first);
2477         }
2478 
2479         if (slice.length == 1)
2480             return (slice.ptr - first) + (res > 0);
2481         slice = slice[(res > 0 ? $ / 2 : 0) .. (res > 0 ? $ : $ / 2)];
2482     }
2483 }
2484 
2485 //
2486 unittest
2487 {
2488     bool match;
2489     auto s1 = new StringExp(Loc.initial, "Amande");
2490     auto s2 = new StringExp(Loc.initial, "Baguette");
2491     auto s3 = new StringExp(Loc.initial, "Croissant");
2492     auto s4 = new StringExp(Loc.initial, "Framboises");
2493     auto s5 = new StringExp(Loc.initial, "Proscuitto");
2494 
2495     // Found, odd size
2496     assert(closestIndex([s1, s2, s3, s4, s5], s1, match) == 0 && match);
2497     assert(closestIndex([s1, s2, s3, s4, s5], s2, match) == 1 && match);
2498     assert(closestIndex([s1, s2, s3, s4, s5], s3, match) == 2 && match);
2499     assert(closestIndex([s1, s2, s3, s4, s5], s4, match) == 3 && match);
2500     assert(closestIndex([s1, s2, s3, s4, s5], s5, match) == 4 && match);
2501 
2502     // Not found, even size
2503     assert(closestIndex([s2, s3, s4, s5], s1, match) == 0 && !match);
2504     assert(closestIndex([s1, s3, s4, s5], s2, match) == 1 && !match);
2505     assert(closestIndex([s1, s2, s4, s5], s3, match) == 2 && !match);
2506     assert(closestIndex([s1, s2, s3, s5], s4, match) == 3 && !match);
2507     assert(closestIndex([s1, s2, s3, s4], s5, match) == 4 && !match);
2508 
2509     // Found, even size
2510     assert(closestIndex([s1, s2, s3, s4], s1, match) == 0 && match);
2511     assert(closestIndex([s1, s2, s3, s4], s2, match) == 1 && match);
2512     assert(closestIndex([s1, s2, s3, s4], s3, match) == 2 && match);
2513     assert(closestIndex([s1, s2, s3, s4], s4, match) == 3 && match);
2514     assert(closestIndex([s1, s3, s4, s5], s5, match) == 3 && match);
2515 
2516     // Not found, odd size
2517     assert(closestIndex([s2, s4, s5], s1, match) == 0 && !match);
2518     assert(closestIndex([s1, s4, s5], s2, match) == 1 && !match);
2519     assert(closestIndex([s1, s2, s4], s3, match) == 2 && !match);
2520     assert(closestIndex([s1, s3, s5], s4, match) == 2 && !match);
2521     assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match);
2522 }
2523 
2524 /**
2525  * Visits the return type of a function and writes leftover ABI tags
2526  */
2527 extern(C++) private final class LeftoverVisitor : Visitor
2528 {
2529     /// List of tags to write
2530     private Array!StringExp toWrite;
2531     /// List of tags to ignore
2532     private const(Array!StringExp)* ignore;
2533 
2534     ///
2535     public this(const(Array!StringExp)* previous)
2536     {
2537         this.ignore = previous;
2538     }
2539 
2540     /// Reintroduce base class overloads
2541     public alias visit = Visitor.visit;
2542 
2543     /// Least specialized overload of each direct child of `RootObject`
visit(Dsymbol o)2544     public override void visit(Dsymbol o)
2545     {
2546         auto ale = ABITagContainer.forSymbol(o);
2547         if (!ale) return;
2548 
2549         bool match;
2550         foreach (elem; *ale.elements)
2551         {
2552             auto se = elem.toStringExp();
2553             closestIndex((*this.ignore)[], se, match);
2554             if (match) continue;
2555             auto idx = closestIndex(this.toWrite[], se, match);
2556             if (!match)
2557                 this.toWrite.insert(idx, se);
2558         }
2559     }
2560 
2561     /// Ditto
visit(Type o)2562     public override void visit(Type o)
2563     {
2564         if (auto sym = o.toDsymbol(null))
2565             sym.accept(this);
2566     }
2567 
2568     /// Composite type
visit(TypePointer o)2569     public override void visit(TypePointer o)
2570     {
2571         o.next.accept(this);
2572     }
2573 
visit(TypeReference o)2574     public override void visit(TypeReference o)
2575     {
2576         o.next.accept(this);
2577     }
2578 }
2579