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