d: Merge dmd, druntime d8e3976a58, phobos 7a6e95688
[official-gcc.git] / gcc / d / dmd / cppmangle.d
blob90b6295ab2fd77673b2e8bedda7006d0fe9fb847
1 /**
2 * Do mangling for C++ linkage.
4 * This is the POSIX side of the implementation.
5 * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`.
7 * Copyright: Copyright (C) 1999-2024 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
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.
19 * Bugs:
20 * https://issues.dlang.org/query.cgi
21 * enter `C++, mangling` as the keywords.
24 module dmd.cppmangle;
26 import core.stdc.stdio;
28 import dmd.arraytypes;
29 import dmd.astenums;
30 import dmd.attrib;
31 import dmd.declaration;
32 import dmd.dsymbol;
33 import dmd.dtemplate;
34 import dmd.errors;
35 import dmd.expression;
36 import dmd.func;
37 import dmd.globals;
38 import dmd.id;
39 import dmd.identifier;
40 import dmd.location;
41 import dmd.mtype;
42 import dmd.nspace;
43 import dmd.root.array;
44 import dmd.common.outbuffer;
45 import dmd.rootobject;
46 import dmd.root.string;
47 import dmd.target;
48 import dmd.typesem;
49 import dmd.visitor;
52 // helper to check if an identifier is a C++ operator
53 enum CppOperator { Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign, Unknown }
54 package CppOperator isCppOperator(Identifier id)
56 __gshared const(Identifier)[] operators = null;
57 if (!operators)
58 operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign];
59 foreach (i, op; operators)
61 if (op == id)
62 return cast(CppOperator)i;
64 return CppOperator.Unknown;
67 ///
68 extern(C++) const(char)* toCppMangleItanium(Dsymbol s)
70 //printf("toCppMangleItanium(%s)\n", s.toChars());
71 OutBuffer buf;
72 scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc);
73 v.mangleOf(s);
74 return buf.extractChars();
77 ///
78 extern(C++) const(char)* cppTypeInfoMangleItanium(Dsymbol s)
80 //printf("cppTypeInfoMangle(%s)\n", s.toChars());
81 OutBuffer buf;
82 buf.writestring("_ZTI"); // "TI" means typeinfo structure
83 scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc);
84 v.cpp_mangle_name(s, false);
85 return buf.extractChars();
88 ///
89 extern(C++) const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset)
91 //printf("cppThunkMangleItanium(%s)\n", fd.toChars());
92 OutBuffer buf;
93 buf.printf("_ZThn%u_", offset); // "Th" means thunk, "n%u" is the call offset
94 scope CppMangleVisitor v = new CppMangleVisitor(&buf, fd.loc);
95 v.mangle_function_encoding(fd);
96 return buf.extractChars();
99 /******************************
100 * Determine if sym is a full aggregate destructor.
101 * Params:
102 * sym = Dsymbol
103 * Returns:
104 * true if sym is an aggregate destructor
106 bool isAggregateDtor(const Dsymbol sym)
108 const dtor = sym.isDtorDeclaration();
109 if (!dtor)
110 return false;
111 const ad = dtor.isMember();
112 assert(ad);
113 return dtor == ad.aggrDtor;
116 /// Context used when processing pre-semantic AST
117 private struct Context
119 /// Template instance of the function being mangled
120 TemplateInstance ti;
121 /// Function declaration we're mangling
122 FuncDeclaration fd;
123 /// Current type / expression being processed (semantically analyzed)
124 RootObject res;
126 @disable ref Context opAssign(ref Context other);
127 @disable ref Context opAssign(Context other);
130 * Helper function to track `res`
132 * Params:
133 * next = Value to set `this.res` to.
134 * If `this.res` is `null`, the expression is not evalutated.
135 * This allow this code to be used even when no context is needed.
137 * Returns:
138 * The previous state of this `Context` object
140 private Context push(lazy RootObject next) @safe
142 auto r = this.res;
143 if (r !is null)
144 this.res = next;
145 return Context(this.ti, this.fd, r);
149 * Reset the context to a previous one, making any adjustment necessary
151 private void pop(ref Context prev) @safe
153 this.res = prev.res;
157 private final class CppMangleVisitor : Visitor
159 /// Context used when processing pre-semantic AST
160 private Context context;
162 ABITagContainer abiTags; /// Container for already-written ABI tags
163 Objects components; /// array of components available for substitution
164 OutBuffer* buf; /// append the mangling to buf[]
165 Loc loc; /// location for use in error messages
168 * Constructor
170 * Params:
171 * buf = `OutBuffer` to write the mangling to
172 * loc = `Loc` of the symbol being mangled
174 this(OutBuffer* buf, Loc loc) scope
176 this.buf = buf;
177 this.loc = loc;
180 /*****
181 * Entry point. Append mangling to buf[]
182 * Params:
183 * s = symbol to mangle
185 void mangleOf(Dsymbol s)
187 if (VarDeclaration vd = s.isVarDeclaration())
189 mangle_variable(vd, vd.cppnamespace !is null);
191 else if (FuncDeclaration fd = s.isFuncDeclaration())
193 mangle_function(fd);
195 else
197 assert(0);
202 * Mangle the return type of a function
204 * This is called on a templated function type.
205 * Context is set to the `FuncDeclaration`.
207 * Params:
208 * preSemantic = the `FuncDeclaration`'s `originalType`
210 void mangleReturnType(TypeFunction preSemantic)
212 auto tf = this.context.res.asFuncDecl().type.isTypeFunction();
213 Type rt = preSemantic.nextOf();
214 // https://issues.dlang.org/show_bug.cgi?id=22739
215 // auto return type means that rt is null.
216 // if so, just pick up the type from the instance
217 if (!rt)
218 rt = tf.nextOf();
219 if (tf.isref)
220 rt = rt.referenceTo();
221 auto prev = this.context.push(tf.nextOf());
222 scope (exit) this.context.pop(prev);
223 this.headOfType(rt);
227 * Write a seq-id from an index number, excluding the terminating '_'
229 * Params:
230 * idx = the index in a substitution list.
231 * Note that index 0 has no value, and `S0_` would be the
232 * substitution at index 1 in the list.
234 * See-Also:
235 * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id
237 private void writeSequenceFromIndex(size_t idx) @safe
239 if (idx)
241 void write_seq_id(size_t i)
243 if (i >= 36)
245 write_seq_id(i / 36);
246 i %= 36;
248 i += (i < 10) ? '0' : 'A' - 10;
249 buf.writeByte(cast(char)i);
252 write_seq_id(idx - 1);
257 * Attempt to perform substitution on `p`
259 * If `p` already appeared in the mangling, it is stored as
260 * a 'part', and short references in the form of `SX_` can be used.
261 * Note that `p` can be anything: template declaration, struct declaration,
262 * class declaration, namespace...
264 * Params:
265 * p = The object to attempt to substitute
266 * nested = Whether or not `p` is to be considered nested.
267 * When `true`, `N` will be prepended before the substitution.
269 * Returns:
270 * Whether `p` already appeared in the mangling,
271 * and substitution has been written to `this.buf`.
273 bool substitute(RootObject p, bool nested = false)
275 //printf("substitute %s\n", p ? p.toChars() : null);
276 auto i = find(p);
277 if (i < 0)
278 return false;
280 //printf("\tmatch\n");
281 /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ...
283 if (nested)
284 buf.writeByte('N');
285 buf.writeByte('S');
286 writeSequenceFromIndex(i);
287 buf.writeByte('_');
288 return true;
291 /******
292 * See if `p` exists in components[]
294 * Note that components can contain `null` entries,
295 * as the index used in mangling is based on the index in the array.
297 * If called with an object whose dynamic type is `Nspace`,
298 * calls the `find(Nspace)` overload.
300 * Returns:
301 * index if found, -1 if not
303 int find(RootObject p)
305 //printf("find %p %d %s\n", p, p.dyncast(), p ? p.toChars() : null);
306 scope v = new ComponentVisitor(p);
307 foreach (i, component; components)
309 if (component)
310 component.visitObject(v);
311 if (v.result)
312 return cast(int)i;
314 return -1;
317 /*********************
318 * Append p to components[]
320 void append(RootObject p)
322 //printf("append %p %d %s\n", p, p.dyncast(), p ? p.toChars() : "null");
323 components.push(p);
327 * Write an identifier preceded by its length
329 * Params:
330 * ident = `Identifier` to write to `this.buf`
332 void writeIdentifier(const ref Identifier ident)
334 const name = ident.toString();
335 this.buf.print(name.length);
336 this.buf.writestring(name);
340 * Insert the leftover ABI tags to the buffer
342 * This inset ABI tags that hasn't already been written
343 * after the mangled name of the function.
344 * For more details, see the `abiTags` variable.
346 * Params:
347 * off = Offset to insert at
348 * tf = Type of the function to mangle the return type of
350 void writeRemainingTags(size_t off, TypeFunction tf)
352 Array!StringExp toWrite;
353 leftOver(tf, &this.abiTags.written, &toWrite);
354 OutBuffer b2;
355 foreach (se; toWrite)
357 auto tag = se.peekString();
358 // We can only insert a slice, and each insert is a memmove,
359 // so use a temporary buffer to keep it efficient.
360 b2.reset();
361 b2.writestring("B");
362 b2.print(tag.length);
363 b2.writestring(tag);
364 this.buf.insert(off, b2[]);
365 off += b2.length;
369 /************************
370 * Determine if symbol is indeed the global ::std namespace.
371 * Params:
372 * s = symbol to check
373 * Returns:
374 * true if it is ::std
376 static bool isStd(Dsymbol s)
378 if (!s)
379 return false;
381 if (auto cnd = s.isCPPNamespaceDeclaration())
382 return isStd(cnd);
384 return (s.ident == Id.std && // the right name
385 s.isNspace() && // g++ disallows global "std" for other than a namespace
386 !getQualifier(s)); // at global level
389 /// Ditto
390 static bool isStd(CPPNamespaceDeclaration s)
392 return s && s.cppnamespace is null && s.ident == Id.std;
395 /************************
396 * Determine if type is a C++ fundamental type.
397 * Params:
398 * t = type to check
399 * Returns:
400 * true if it is a fundamental type
402 static bool isFundamentalType(Type t)
404 // First check the target whether some specific ABI is being followed.
405 bool isFundamental = void;
406 if (target.cpp.fundamentalType(t, isFundamental))
407 return isFundamental;
409 if (auto te = t.isTypeEnum())
411 // Peel off enum type from special types.
412 if (te.sym.isSpecial())
413 t = te.memType();
416 // Fundamental arithmetic types:
417 // 1. integral types: bool, char, int, ...
418 // 2. floating point types: float, double, real
419 // 3. void
420 // 4. null pointer: std::nullptr_t (since C++11)
421 if (t.ty == Tvoid || t.ty == Tbool)
422 return true;
423 else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11)
424 return true;
425 else
426 return t.isTypeBasic() && (t.isintegral() || t.isreal());
429 /******************************
430 * Write the mangled representation of a template argument.
431 * Params:
432 * ti = the template instance
433 * arg = the template argument index
435 void template_arg(TemplateInstance ti, size_t arg)
437 TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
438 assert(td);
439 TemplateParameter tp = (*td.parameters)[arg];
440 RootObject o = (*ti.tiargs)[arg];
442 auto prev = this.context.push({
443 TemplateInstance parentti;
444 if (this.context.res.dyncast() == DYNCAST.dsymbol)
445 parentti = this.context.res.asFuncDecl().parent.isTemplateInstance();
446 else
448 auto parent = this.context.res.asType().toDsymbol(null).parent;
449 parentti = parent.isTemplateInstance();
450 // https://issues.dlang.org/show_bug.cgi?id=22760
451 // The template instance may sometimes have the form
452 // S1!int.S1, therefore the above instruction might yield null
453 if (parentti is null && parent.parent)
454 parentti = parent.parent.isTemplateInstance();
456 return (*parentti.tiargs)[arg];
457 }());
458 scope (exit) this.context.pop(prev);
460 if (tp.isTemplateTypeParameter())
462 Type t = isType(o);
463 assert(t);
464 t.accept(this);
466 else if (TemplateValueParameter tv = tp.isTemplateValueParameter())
468 // <expr-primary> ::= L <type> <value number> E # integer literal
469 if (tv.valType.isintegral())
471 Expression e = isExpression(o);
472 assert(e);
473 buf.writeByte('L');
474 tv.valType.accept(this);
475 auto val = e.toUInteger();
476 if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0)
478 val = -val;
479 buf.writeByte('n');
481 buf.print(val);
482 buf.writeByte('E');
484 else
486 .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template value parameter is not supported", ti.kind, ti.toPrettyChars, tv.valType.toChars());
487 fatal();
490 else if (tp.isTemplateAliasParameter())
492 // Passing a function as alias parameter is the same as passing
493 // `&function`
494 Dsymbol d = isDsymbol(o);
495 Expression e = isExpression(o);
496 if (d && d.isFuncDeclaration())
498 // X .. E => template parameter is an expression
499 // 'ad' => unary operator ('&')
500 // L .. E => is a <expr-primary>
501 buf.writestring("XadL");
502 mangle_function(d.isFuncDeclaration());
503 buf.writestring("EE");
505 else if (e && e.isVarExp() && e.isVarExp().var.isVarDeclaration())
507 VarDeclaration vd = e.isVarExp().var.isVarDeclaration();
508 buf.writeByte('L');
509 mangle_variable(vd, true);
510 buf.writeByte('E');
512 else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember)
514 if (!substitute(d))
516 cpp_mangle_name(d, false);
519 else
521 .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template alias parameter is not supported", ti.kind, ti.toPrettyChars, o.toChars());
522 fatal();
525 else if (tp.isTemplateThisParameter())
527 .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template this parameter is not supported", ti.kind, ti.toPrettyChars, o.toChars());
528 fatal();
530 else
532 assert(0);
536 /******************************
537 * Write the mangled representation of the template arguments.
538 * Params:
539 * ti = the template instance
540 * firstArg = index of the first template argument to mangle
541 * (used for operator overloading)
542 * Returns:
543 * true if any arguments were written
545 bool template_args(TemplateInstance ti, int firstArg = 0)
547 /* <template-args> ::= I <template-arg>+ E
549 if (!ti || ti.tiargs.length <= firstArg) // could happen if std::basic_string is not a template
550 return false;
551 buf.writeByte('I');
552 foreach (i; firstArg .. ti.tiargs.length)
554 TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
555 assert(td);
556 TemplateParameter tp = (*td.parameters)[i];
559 * <template-arg> ::= <type> # type or template
560 * ::= X <expression> E # expression
561 * ::= <expr-primary> # simple expressions
562 * ::= J <template-arg>* E # argument pack
564 * Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.template-arg
566 if (TemplateTupleParameter tt = tp.isTemplateTupleParameter())
568 buf.writeByte('J'); // argument pack
570 // mangle the rest of the arguments as types
571 foreach (j; i .. (*ti.tiargs).length)
573 Type t = isType((*ti.tiargs)[j]);
574 if (t is null)
576 .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template value parameter is not supported", ti.kind, ti.toPrettyChars, (*ti.tiargs)[j].toChars());
577 fatal();
579 t.accept(this);
582 buf.writeByte('E');
583 break;
586 template_arg(ti, i);
588 buf.writeByte('E');
589 return true;
593 * Write the symbol `p` if not null, then execute the delegate
595 * Params:
596 * p = Symbol to write
597 * dg = Delegate to execute
599 void writeChained(Dsymbol p, scope void delegate() dg)
601 if (p && !p.isModule())
603 buf.writestring("N");
604 source_name(p, true);
605 dg();
606 buf.writestring("E");
608 else
609 dg();
613 * Write the name of `s` to the buffer
615 * Params:
616 * s = Symbol to write the name of
617 * haveNE = Whether `N..E` is already part of the mangling
618 * Because `Nspace` and `CPPNamespaceAttribute` can be
619 * mixed, this is a mandatory hack.
621 void source_name(Dsymbol s, bool haveNE = false)
623 version (none)
625 printf("source_name(%s)\n", s.toChars());
626 auto sl = this.buf.peekSlice();
627 assert(sl.length == 0 || haveNE || s.cppnamespace is null || sl != "_ZN");
629 auto ti = s.isTemplateInstance();
631 if (!ti)
633 auto ag = s.isAggregateDeclaration();
634 const ident = (ag && ag.pMangleOverride) ? ag.pMangleOverride.id : s.ident;
635 this.writeNamespace(s.cppnamespace, () {
636 this.writeIdentifier(ident);
637 this.abiTags.writeSymbol(s, this);
639 haveNE);
640 return;
643 bool needsTa = false;
645 // https://issues.dlang.org/show_bug.cgi?id=20413
646 // N..E is not needed when substituting members of the std namespace.
647 // This is observed in the GCC and Clang implementations.
648 // The Itanium specification is not clear enough on this specific case.
649 // References:
650 // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.name
651 // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression
652 Dsymbol q = getQualifier(ti.tempdecl);
653 Dsymbol ns = ti.tempdecl.cppnamespace;
654 const inStd = ns && isStd(ns) || q && isStd(q);
655 const isNested = !inStd && (ns || q);
657 if (substitute(ti.tempdecl, !haveNE && isNested))
659 template_args(ti);
660 if (!haveNE && isNested)
661 buf.writeByte('E');
662 return;
664 else if (this.writeStdSubstitution(ti, needsTa))
666 this.abiTags.writeSymbol(ti, this);
667 if (needsTa)
668 template_args(ti);
669 return;
672 auto ag = ti.aliasdecl ? ti.aliasdecl.isAggregateDeclaration() : null;
673 if (ag && ag.pMangleOverride)
675 this.writeNamespace(
676 ti.toAlias().cppnamespace, () {
677 this.writeIdentifier(ag.pMangleOverride.id);
678 if (ag.pMangleOverride.agg && ag.pMangleOverride.agg.isInstantiated())
680 auto to = ag.pMangleOverride.agg.isInstantiated();
681 append(to);
682 this.abiTags.writeSymbol(to.tempdecl, this);
683 template_args(to);
685 }, haveNE);
687 else
689 this.writeNamespace(
690 s.cppnamespace, () {
691 this.writeIdentifier(ti.tempdecl.toAlias().ident);
692 append(ti.tempdecl);
693 this.abiTags.writeSymbol(ti.tempdecl, this);
694 template_args(ti);
695 }, haveNE);
699 /********
700 * See if s is actually an instance of a template
701 * Params:
702 * s = symbol
703 * Returns:
704 * if s is instance of a template, return the instance, otherwise return s
706 static Dsymbol getInstance(Dsymbol s)
708 Dsymbol p = s.toParent();
709 if (p)
711 if (TemplateInstance ti = p.isTemplateInstance())
712 return ti;
714 return s;
717 /// Get the namespace of a template instance
718 CPPNamespaceDeclaration getTiNamespace(TemplateInstance ti)
720 // If we receive a pre-semantic `TemplateInstance`,
721 // `cppnamespace` is always `null`
722 return ti.tempdecl ? ti.cppnamespace
723 : this.context.res.asType().toDsymbol(null).cppnamespace;
726 /********
727 * Get qualifier for `s`, meaning the symbol
728 * that s is in the symbol table of.
729 * The module does not count as a qualifier, because C++
730 * does not have modules.
731 * Params:
732 * s = symbol that may have a qualifier
733 * s is rewritten to be TemplateInstance if s is one
734 * Returns:
735 * qualifier, null if none
737 static Dsymbol getQualifier(Dsymbol s)
739 Dsymbol p = s.toParent();
740 return (p && !p.isModule()) ? p : null;
743 // Detect type char
744 static bool isChar(RootObject o)
746 Type t = isType(o);
747 return (t && t.equals(Type.tchar));
750 // Detect type ::std::char_traits<char>
751 bool isChar_traits_char(RootObject o)
753 return isIdent_char(Id.char_traits, o);
756 // Detect type ::std::allocator<char>
757 bool isAllocator_char(RootObject o)
759 return isIdent_char(Id.allocator, o);
762 // Detect type ::std::ident<char>
763 bool isIdent_char(Identifier ident, RootObject o)
765 Type t = isType(o);
766 if (!t || !t.isTypeStruct())
767 return false;
768 Dsymbol s = t.toDsymbol(null);
769 if (s.ident != ident)
770 return false;
771 Dsymbol p = s.toParent();
772 if (!p)
773 return false;
774 TemplateInstance ti = p.isTemplateInstance();
775 if (!ti)
776 return false;
777 Dsymbol q = getQualifier(ti);
778 const bool inStd = isStd(q) || isStd(this.getTiNamespace(ti));
779 return inStd && ti.tiargs.length == 1 && isChar((*ti.tiargs)[0]);
782 /***
783 * Detect template args <char, ::std::char_traits<char>>
784 * and write st if found.
785 * Returns:
786 * true if found
788 bool char_std_char_traits_char(TemplateInstance ti, string st)
790 if (ti.tiargs.length == 2 &&
791 isChar((*ti.tiargs)[0]) &&
792 isChar_traits_char((*ti.tiargs)[1]))
794 buf.writestring(st.ptr);
795 return true;
797 return false;
801 void prefix_name(Dsymbol s)
803 //printf("prefix_name(%s)\n", s.toChars());
804 if (substitute(s))
805 return;
806 if (isStd(s))
807 return buf.writestring("St");
809 auto si = getInstance(s);
810 Dsymbol p = getQualifier(si);
811 if (p)
813 if (isStd(p))
815 bool needsTa;
816 auto ti = si.isTemplateInstance();
817 if (this.writeStdSubstitution(ti, needsTa))
819 this.abiTags.writeSymbol(ti, this);
820 if (needsTa)
822 template_args(ti);
823 append(ti);
825 return;
827 buf.writestring("St");
829 else
830 prefix_name(p);
832 source_name(si, true);
833 if (!isStd(si))
834 /* Do this after the source_name() call to keep components[]
835 * in the right order.
836 * https://issues.dlang.org/show_bug.cgi?id=17947
838 append(si);
842 * Write common substitution for standard types, such as std::allocator
844 * This function assumes that the symbol `ti` is in the namespace `std`.
846 * Params:
847 * ti = Template instance to consider
848 * needsTa = If this function returns `true`, this value indicates
849 * if additional template argument mangling is needed
851 * Returns:
852 * `true` if a special std symbol was found
854 bool writeStdSubstitution(TemplateInstance ti, out bool needsTa)
856 if (!ti)
857 return false;
858 if (!isStd(this.getTiNamespace(ti)) && !isStd(getQualifier(ti)))
859 return false;
861 if (ti.name == Id.allocator)
863 buf.writestring("Sa");
864 needsTa = true;
865 return true;
867 if (ti.name == Id.basic_string)
869 // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
870 if (ti.tiargs.length == 3 &&
871 isChar((*ti.tiargs)[0]) &&
872 isChar_traits_char((*ti.tiargs)[1]) &&
873 isAllocator_char((*ti.tiargs)[2]))
876 buf.writestring("Ss");
877 return true;
879 buf.writestring("Sb"); // ::std::basic_string
880 needsTa = true;
881 return true;
884 // ::std::basic_istream<char, ::std::char_traits<char>>
885 if (ti.name == Id.basic_istream &&
886 char_std_char_traits_char(ti, "Si"))
887 return true;
889 // ::std::basic_ostream<char, ::std::char_traits<char>>
890 if (ti.name == Id.basic_ostream &&
891 char_std_char_traits_char(ti, "So"))
892 return true;
894 // ::std::basic_iostream<char, ::std::char_traits<char>>
895 if (ti.name == Id.basic_iostream &&
896 char_std_char_traits_char(ti, "Sd"))
897 return true;
899 return false;
902 void cpp_mangle_name(Dsymbol s, bool qualified)
904 //printf("cpp_mangle_name(%s, %d)\n", s.toChars(), qualified);
905 Dsymbol p = s.toParent();
906 Dsymbol se = s;
907 bool write_prefix = true;
908 if (p && p.isTemplateInstance())
910 se = p;
911 if (find(p.isTemplateInstance().tempdecl) >= 0)
912 write_prefix = false;
913 p = p.toParent();
915 if (!p || p.isModule())
917 source_name(se, false);
918 append(s);
919 return;
922 if (!isStd(p) || qualified)
924 buf.writeByte('N');
925 if (write_prefix)
927 if (isStd(p))
928 buf.writestring("St");
929 else
930 prefix_name(p);
932 source_name(se, true);
933 buf.writeByte('E');
934 append(s);
935 return;
937 /* The N..E is not required if:
938 * 1. the parent is 'std'
939 * 2. 'std' is the initial qualifier
940 * 3. there is no CV-qualifier or a ref-qualifier for a member function
941 * ABI 5.1.8
943 TemplateInstance ti = se.isTemplateInstance();
944 if (s.ident == Id.allocator)
946 buf.writestring("Sa"); // "Sa" is short for ::std::allocator
947 template_args(ti);
949 else if (s.ident == Id.basic_string)
951 // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
952 if (ti.tiargs.length == 3 &&
953 isChar((*ti.tiargs)[0]) &&
954 isChar_traits_char((*ti.tiargs)[1]) &&
955 isAllocator_char((*ti.tiargs)[2]))
957 buf.writestring("Ss");
958 return;
960 buf.writestring("Sb"); // ::std::basic_string
961 template_args(ti);
963 else
965 // ::std::basic_istream<char, ::std::char_traits<char>>
966 if (s.ident == Id.basic_istream)
968 if (char_std_char_traits_char(ti, "Si"))
969 return;
971 else if (s.ident == Id.basic_ostream)
973 if (char_std_char_traits_char(ti, "So"))
974 return;
976 else if (s.ident == Id.basic_iostream)
978 if (char_std_char_traits_char(ti, "Sd"))
979 return;
981 buf.writestring("St");
982 source_name(se, true);
984 append(s);
988 * Write CV-qualifiers to the buffer
990 * CV-qualifiers are 'r': restrict (unused in D), 'V': volatile, 'K': const
992 * See_Also:
993 * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.CV-qualifiers
995 void CV_qualifiers(const Type t)
997 if (t.isConst())
998 buf.writeByte('K');
1002 * Mangles a variable
1004 * Params:
1005 * d = Variable declaration to mangle
1006 * isNested = Whether this variable is nested, e.g. a template parameter
1007 * or within a namespace
1009 void mangle_variable(VarDeclaration d, bool isNested)
1011 // fake mangling for fields to fix https://issues.dlang.org/show_bug.cgi?id=16525
1012 if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared)))
1014 .error(d.loc, "%s `%s` internal compiler error: C++ static non-`__gshared` non-`extern` variables not supported", d.kind, d.toPrettyChars);
1015 fatal();
1017 Dsymbol p = d.toParent();
1018 if (p && !p.isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE"
1020 buf.writestring("_ZN");
1021 prefix_name(p);
1022 source_name(d, true);
1023 buf.writeByte('E');
1025 else if (isNested)
1027 buf.writestring("_Z");
1028 source_name(d, false);
1030 else
1032 if (auto varTags = ABITagContainer.forSymbol(d))
1034 buf.writestring("_Z");
1035 source_name(d, false);
1036 return;
1038 if (auto typeTags = ABITagContainer.forSymbol(d.type.toDsymbol(null)))
1040 buf.writestring("_Z");
1041 source_name(d, false);
1042 this.abiTags.write(*this.buf, typeTags);
1043 return;
1045 //char beta[6] should mangle as "beta"
1046 buf.writestring(d.ident.toString());
1050 void mangle_function(FuncDeclaration d)
1052 //printf("mangle_function(%s)\n", d.toChars());
1054 * <mangled-name> ::= _Z <encoding>
1056 buf.writestring("_Z");
1057 this.mangle_function_encoding(d);
1060 void mangle_function_encoding(FuncDeclaration d)
1062 //printf("mangle_function_encoding(%s)\n", d.toChars());
1064 * <encoding> ::= <function name> <bare-function-type>
1065 * ::= <data name>
1066 * ::= <special-name>
1068 TypeFunction tf = d.type.isTypeFunction();
1070 if (TemplateDeclaration ftd = getFuncTemplateDecl(d))
1072 /* It's an instance of a function template
1074 TemplateInstance ti = d.parent.isTemplateInstance();
1075 assert(ti);
1076 this.mangleTemplatedFunction(d, tf, ftd, ti);
1077 return;
1080 Dsymbol p = d.toParent();
1081 if (p && !p.isModule() && tf.linkage == LINK.cpp)
1083 this.mangleNestedFuncPrefix(tf, p);
1085 if (auto ctor = d.isCtorDeclaration())
1086 buf.writestring(ctor.isCpCtor ? "C2" : "C1");
1087 else if (d.isAggregateDtor())
1088 buf.writestring("D1");
1089 else if (d.ident && d.ident == Id.assign)
1090 buf.writestring("aS");
1091 else if (d.ident && d.ident == Id.eq)
1092 buf.writestring("eq");
1093 else if (d.ident && d.ident == Id.index)
1094 buf.writestring("ix");
1095 else if (d.ident && d.ident == Id.call)
1096 buf.writestring("cl");
1097 else
1098 source_name(d, true);
1099 buf.writeByte('E');
1101 else
1103 source_name(d, false);
1106 // Save offset for potentially writing tags
1107 const size_t off = this.buf.length();
1109 // Template args accept extern "C" symbols with special mangling
1110 if (tf.linkage == LINK.cpp)
1111 mangleFunctionParameters(tf.parameterList);
1113 if (!tf.next.isTypeBasic())
1114 this.writeRemainingTags(off, tf);
1118 * Recursively mangles a non-scoped namespace
1120 * Parameters:
1121 * ns = Namespace to mangle
1122 * dg = A delegate to write the identifier in this namespace
1123 * haveNE = When `false` (the default), surround the namespace / dg
1124 * call with nested name qualifier (`N..E`).
1125 * Otherwise, they are already present (e.g. `Nspace` was used).
1127 void writeNamespace(CPPNamespaceDeclaration ns, scope void delegate() dg,
1128 bool haveNE = false)
1130 void runDg () { if (dg !is null) dg(); }
1132 if (ns is null || ns.ident is null)
1133 return runDg();
1135 if (isStd(ns))
1137 if (!substitute(ns))
1138 buf.writestring("St");
1139 runDg();
1141 else if (dg !is null)
1143 if (!haveNE)
1144 buf.writestring("N");
1145 if (!substitute(ns))
1147 this.writeNamespace(ns.cppnamespace, null);
1148 this.writeIdentifier(ns.ident);
1149 append(ns);
1151 dg();
1152 if (!haveNE)
1153 buf.writestring("E");
1155 else if (!substitute(ns))
1157 this.writeNamespace(ns.cppnamespace, null);
1158 this.writeIdentifier(ns.ident);
1159 append(ns);
1164 * Mangles a function template to C++
1166 * Params:
1167 * d = Function declaration
1168 * tf = Function type (casted d.type)
1169 * ftd = Template declaration (ti.templdecl)
1170 * ti = Template instance (d.parent)
1172 void mangleTemplatedFunction(FuncDeclaration d, TypeFunction tf,
1173 TemplateDeclaration ftd, TemplateInstance ti)
1175 Dsymbol p = ti.toParent();
1176 // Check if this function is *not* nested
1177 if (!p || p.isModule() || tf.linkage != LINK.cpp)
1179 this.context.ti = ti;
1180 this.context.fd = d;
1181 this.context.res = d;
1182 TypeFunction preSemantic = d.originalType.isTypeFunction();
1183 auto nspace = ti.toParent();
1184 if (nspace && nspace.isNspace())
1185 this.writeChained(ti.toParent(), () => source_name(ti, true));
1186 else
1187 source_name(ti, false);
1188 this.mangleReturnType(preSemantic);
1189 this.mangleFunctionParameters(ParameterList(preSemantic.parameterList.parameters, tf.parameterList.varargs));
1190 return;
1193 // It's a nested function (e.g. a member of an aggregate)
1194 this.mangleNestedFuncPrefix(tf, p);
1196 if (d.isCtorDeclaration())
1198 buf.writestring("C1");
1199 mangleFunctionParameters(tf.parameterList);
1200 return;
1202 else if (d.isAggregateDtor())
1204 buf.writestring("D1");
1205 mangleFunctionParameters(tf.parameterList);
1206 return;
1209 int firstTemplateArg = 0;
1210 bool appendReturnType = true;
1211 bool isConvertFunc = false;
1212 string symName;
1214 // test for special symbols
1215 CppOperator whichOp = isCppOperator(ti.name);
1216 final switch (whichOp)
1218 case CppOperator.Unknown:
1219 break;
1220 case CppOperator.Cast:
1221 symName = "cv";
1222 firstTemplateArg = 1;
1223 isConvertFunc = true;
1224 appendReturnType = false;
1225 break;
1226 case CppOperator.Assign:
1227 symName = "aS";
1228 break;
1229 case CppOperator.Eq:
1230 symName = "eq";
1231 break;
1232 case CppOperator.Index:
1233 symName = "ix";
1234 break;
1235 case CppOperator.Call:
1236 symName = "cl";
1237 break;
1238 case CppOperator.Unary:
1239 case CppOperator.Binary:
1240 case CppOperator.OpAssign:
1241 TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
1242 assert(td);
1243 assert(ti.tiargs.length >= 1);
1244 TemplateParameter tp = (*td.parameters)[0];
1245 TemplateValueParameter tv = tp.isTemplateValueParameter();
1246 if (!tv || !tv.valType.isString())
1247 break; // expecting a string argument to operators!
1248 Expression exp = (*ti.tiargs)[0].isExpression();
1249 StringExp str = exp.toStringExp();
1250 switch (whichOp)
1252 case CppOperator.Unary:
1253 switch (str.peekString())
1255 case "*": symName = "de"; goto continue_template;
1256 case "++": symName = "pp"; goto continue_template;
1257 case "--": symName = "mm"; goto continue_template;
1258 case "-": symName = "ng"; goto continue_template;
1259 case "+": symName = "ps"; goto continue_template;
1260 case "~": symName = "co"; goto continue_template;
1261 default: break;
1263 break;
1264 case CppOperator.Binary:
1265 switch (str.peekString())
1267 case ">>": symName = "rs"; goto continue_template;
1268 case "<<": symName = "ls"; goto continue_template;
1269 case "*": symName = "ml"; goto continue_template;
1270 case "-": symName = "mi"; goto continue_template;
1271 case "+": symName = "pl"; goto continue_template;
1272 case "&": symName = "an"; goto continue_template;
1273 case "/": symName = "dv"; goto continue_template;
1274 case "%": symName = "rm"; goto continue_template;
1275 case "^": symName = "eo"; goto continue_template;
1276 case "|": symName = "or"; goto continue_template;
1277 default: break;
1279 break;
1280 case CppOperator.OpAssign:
1281 switch (str.peekString())
1283 case "*": symName = "mL"; goto continue_template;
1284 case "+": symName = "pL"; goto continue_template;
1285 case "-": symName = "mI"; goto continue_template;
1286 case "/": symName = "dV"; goto continue_template;
1287 case "%": symName = "rM"; goto continue_template;
1288 case ">>": symName = "rS"; goto continue_template;
1289 case "<<": symName = "lS"; goto continue_template;
1290 case "&": symName = "aN"; goto continue_template;
1291 case "|": symName = "oR"; goto continue_template;
1292 case "^": symName = "eO"; goto continue_template;
1293 default: break;
1295 break;
1296 default:
1297 assert(0);
1298 continue_template:
1299 firstTemplateArg = 1;
1300 break;
1302 break;
1304 if (symName.length == 0)
1305 source_name(ti, true);
1306 else
1308 buf.writestring(symName);
1309 if (isConvertFunc)
1310 template_arg(ti, 0);
1311 appendReturnType = template_args(ti, firstTemplateArg) && appendReturnType;
1313 buf.writeByte('E');
1314 if (appendReturnType)
1315 headOfType(tf.nextOf()); // mangle return type
1316 mangleFunctionParameters(tf.parameterList);
1320 * Mangle the parameters of a function
1322 * For templated functions, `context.res` is set to the `FuncDeclaration`
1324 * Params:
1325 * parameters = Array of `Parameter` to mangle
1326 * varargs = if != 0, this function has varargs parameters
1328 void mangleFunctionParameters(ParameterList parameterList)
1330 int numparams = 0;
1332 foreach (n, fparam; parameterList)
1334 Type t = fparam.type.merge2();
1335 if (fparam.isReference())
1336 t = t.referenceTo();
1337 else if (fparam.isLazy())
1339 // Mangle as delegate
1340 auto tf = new TypeFunction(ParameterList(), t, LINK.d);
1341 auto td = new TypeDelegate(tf);
1342 t = td.merge();
1344 else if (Type cpptype = target.cpp.parameterType(t))
1345 t = cpptype;
1346 if (t.ty == Tsarray)
1348 // Static arrays in D are passed by value; no counterpart in C++
1349 .error(loc, "internal compiler error: unable to pass static array `%s` to extern(C++) function, use pointer instead",
1350 t.toChars());
1351 fatal();
1353 auto prev = this.context.push({
1354 TypeFunction tf;
1355 if (isDsymbol(this.context.res))
1356 tf = this.context.res.asFuncDecl().type.isTypeFunction();
1357 else
1358 tf = this.context.res.asType().isTypeFunction();
1359 assert(tf);
1360 return (*tf.parameterList.parameters)[n].type;
1361 }());
1362 scope (exit) this.context.pop(prev);
1364 if (this.context.ti && global.params.cplusplus >= CppStdRevision.cpp11)
1365 handleParamPack(t, this.context.ti.tempdecl.isTemplateDeclaration().parameters);
1367 headOfType(t);
1368 ++numparams;
1371 if (parameterList.varargs == VarArg.variadic)
1372 buf.writeByte('z');
1373 else if (!numparams)
1374 buf.writeByte('v'); // encode (void) parameters
1377 /****** The rest is type mangling ************/
1379 void error(Type t)
1381 const(char)* p;
1382 if (t.isImmutable())
1383 p = "`immutable` ";
1384 else if (t.isShared())
1385 p = "`shared` ";
1386 else
1387 p = "";
1388 .error(loc, "internal compiler error: %stype `%s` cannot be mapped to C++\n", p, t.toChars());
1389 fatal(); //Fatal, because this error should be handled in frontend
1392 /****************************
1393 * Mangle a type,
1394 * treating it as a Head followed by a Tail.
1395 * Params:
1396 * t = Head of a type
1398 void headOfType(Type t)
1400 if (auto tc = t.isTypeClass())
1402 mangleTypeClass(tc, true);
1404 else
1406 // For value types, strip const/immutable/shared from the head of the type
1407 auto prev = this.context.push(this.context.res.asType().mutableOf().unSharedOf());
1408 scope (exit) this.context.pop(prev);
1409 t.mutableOf().unSharedOf().accept(this);
1413 /******
1414 * Write out 1 or 2 character basic type mangling.
1415 * Handle const and substitutions.
1416 * Params:
1417 * t = type to mangle
1418 * p = if not 0, then character prefix
1419 * c = mangling character
1421 void writeBasicType(Type t, char p, char c)
1423 // Only do substitutions for non-fundamental types.
1424 if (!isFundamentalType(t) || t.isConst())
1426 if (substitute(t))
1427 return;
1428 else
1429 append(t);
1431 CV_qualifiers(t);
1432 if (p)
1433 buf.writeByte(p);
1434 buf.writeByte(c);
1438 /****************
1439 * Write structs and enums.
1440 * Params:
1441 * t = TypeStruct or TypeEnum
1443 void doSymbol(Type t)
1445 if (substitute(t))
1446 return;
1447 CV_qualifiers(t);
1449 // Handle any target-specific struct types.
1450 if (auto tm = target.cpp.typeMangle(t))
1452 buf.writestring(tm);
1454 else
1456 Dsymbol s = t.toDsymbol(null);
1457 Dsymbol p = s.toParent();
1458 if (p && p.isTemplateInstance())
1460 /* https://issues.dlang.org/show_bug.cgi?id=17947
1461 * Substitute the template instance symbol, not the struct/enum symbol
1463 if (substitute(p))
1464 return;
1466 if (!substitute(s))
1467 cpp_mangle_name(s, false);
1469 if (t.isConst())
1470 append(t);
1475 /************************
1476 * Mangle a class type.
1477 * If it's the head, treat the initial pointer as a value type.
1478 * Params:
1479 * t = class type
1480 * head = true for head of a type
1482 void mangleTypeClass(TypeClass t, bool head)
1484 if (t.isImmutable() || t.isShared())
1485 return error(t);
1487 /* Mangle as a <pointer to><struct>
1489 if (substitute(t))
1490 return;
1491 if (!head)
1492 CV_qualifiers(t);
1493 buf.writeByte('P');
1495 CV_qualifiers(t);
1498 Dsymbol s = t.toDsymbol(null);
1499 Dsymbol p = s.toParent();
1500 if (p && p.isTemplateInstance())
1502 /* https://issues.dlang.org/show_bug.cgi?id=17947
1503 * Substitute the template instance symbol, not the class symbol
1505 if (substitute(p))
1506 return;
1510 if (!substitute(t.sym))
1512 cpp_mangle_name(t.sym, false);
1514 if (t.isConst())
1515 append(null); // C++ would have an extra type here
1516 append(t);
1520 * Mangle the prefix of a nested (e.g. member) function
1522 * Params:
1523 * tf = Type of the nested function
1524 * parent = Parent in which the function is nested
1526 void mangleNestedFuncPrefix(TypeFunction tf, Dsymbol parent)
1528 /* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
1529 * ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
1531 buf.writeByte('N');
1532 CV_qualifiers(tf);
1534 /* <prefix> ::= <prefix> <unqualified-name>
1535 * ::= <template-prefix> <template-args>
1536 * ::= <template-param>
1537 * ::= # empty
1538 * ::= <substitution>
1539 * ::= <prefix> <data-member-prefix>
1541 prefix_name(parent);
1545 * Write `Dp` (C++11 function parameter pack prefix) if 't' is a TemplateSequenceParameter (T...).
1547 * Params:
1548 * t = Parameter type
1549 * params = Template parameters of the function
1551 private void handleParamPack(Type t, TemplateParameters* params)
1553 if (t.isTypeReference())
1554 t = t.nextOf();
1555 auto ti = t.isTypeIdentifier();
1556 if (!ti)
1557 return;
1559 auto idx = templateParamIndex(ti.ident, params);
1560 if (idx < params.length && (*params)[idx].isTemplateTupleParameter())
1561 buf.writestring("Dp");
1565 * Helper function to write a `T..._` template index.
1567 * Params:
1568 * idx = Index of `param` in the template argument list
1569 * param = Template parameter to mangle
1571 private void writeTemplateArgIndex(size_t idx, TemplateParameter param)
1573 // expressions are mangled in <X..E>
1574 if (param.isTemplateValueParameter())
1575 buf.writeByte('X');
1576 buf.writeByte('T');
1577 writeSequenceFromIndex(idx);
1578 buf.writeByte('_');
1579 if (param.isTemplateValueParameter())
1580 buf.writeByte('E');
1584 * Given an array of template parameters and an identifier,
1585 * returns the index of the identifier in that array.
1587 * Params:
1588 * ident = Identifier for which substitution is attempted
1589 * (e.g. `void func(T)(T param)` => `T` from `T param`)
1590 * params = `TemplateParameters` of the enclosing symbol
1591 * (in the previous example, `func`'s template parameters)
1593 * Returns:
1594 * The index of the identifier match in `params`,
1595 * or `params.length` if there wasn't any match.
1597 private static size_t templateParamIndex(
1598 const ref Identifier ident, TemplateParameters* params) @safe
1600 foreach (idx, param; *params)
1601 if (param.ident == ident)
1602 return idx;
1603 return params.length;
1607 * Given a template instance `t`, write its qualified name
1608 * without the template parameter list
1610 * Params:
1611 * t = Post-parsing `TemplateInstance` pointing to the symbol
1612 * to mangle (one level deep)
1613 * dg = Delegate to execute after writing the qualified symbol
1616 private void writeQualified(TemplateInstance t, scope void delegate() dg)
1618 auto type = isType(this.context.res);
1619 if (!type)
1621 this.writeIdentifier(t.name);
1622 return dg();
1624 auto sym1 = type.toDsymbol(null);
1625 if (!sym1)
1627 this.writeIdentifier(t.name);
1628 return dg();
1630 // Get the template instance
1631 auto sym = getQualifier(sym1);
1632 auto sym2 = getQualifier(sym);
1633 if (sym2 && isStd(sym2)) // Nspace path
1635 bool unused;
1636 assert(sym.isTemplateInstance());
1637 if (this.writeStdSubstitution(sym.isTemplateInstance(), unused))
1638 return dg();
1639 // std names don't require `N..E`
1640 buf.writestring("St");
1641 this.writeIdentifier(t.name);
1642 this.append(t);
1643 return dg();
1645 else if (sym2)
1647 buf.writestring("N");
1648 if (!this.substitute(sym2))
1649 sym2.accept(this);
1651 this.writeNamespace(
1652 sym1.cppnamespace, () {
1653 this.writeIdentifier(t.name);
1654 this.append(t);
1655 dg();
1657 if (sym2)
1658 buf.writestring("E");
1661 extern(C++):
1663 alias visit = Visitor.visit;
1665 override void visit(TypeNull t)
1667 if (t.isImmutable() || t.isShared())
1668 return error(t);
1670 writeBasicType(t, 'D', 'n');
1673 override void visit(TypeNoreturn t)
1675 if (t.isImmutable() || t.isShared())
1676 return error(t);
1678 writeBasicType(t, 0, 'v'); // mangle like `void`
1681 override void visit(TypeBasic t)
1683 if (t.isImmutable() || t.isShared())
1684 return error(t);
1686 // Handle any target-specific basic types.
1687 if (auto tm = target.cpp.typeMangle(t))
1689 // Only do substitutions for non-fundamental types.
1690 if (!isFundamentalType(t) || t.isConst())
1692 if (substitute(t))
1693 return;
1694 else
1695 append(t);
1697 CV_qualifiers(t);
1698 buf.writestring(tm);
1699 return;
1702 /* <builtin-type>:
1703 * v void
1704 * w wchar_t
1705 * b bool
1706 * c char
1707 * a signed char
1708 * h unsigned char
1709 * s short
1710 * t unsigned short
1711 * i int
1712 * j unsigned int
1713 * l long
1714 * m unsigned long
1715 * x long long, __int64
1716 * y unsigned long long, __int64
1717 * n __int128
1718 * o unsigned __int128
1719 * f float
1720 * d double
1721 * e long double, __float80
1722 * g __float128
1723 * z ellipsis
1724 * Dd 64 bit IEEE 754r decimal floating point
1725 * De 128 bit IEEE 754r decimal floating point
1726 * Df 32 bit IEEE 754r decimal floating point
1727 * Dh 16 bit IEEE 754r half-precision floating point
1728 * Di char32_t
1729 * Ds char16_t
1730 * u <source-name> # vendor extended type
1732 if (t.isimaginary() || t.iscomplex())
1734 // https://issues.dlang.org/show_bug.cgi?id=22806
1735 // Complex and imaginary types are represented in the same way as
1736 // arrays or vectors in C++. First substitute the outer type, then
1737 // write out the mangle string of the underlying type.
1738 if (substitute(t))
1739 return;
1740 append(t);
1741 CV_qualifiers(t);
1743 if (t.isimaginary())
1744 buf.writeByte('G'); // 'G' means imaginary
1745 else
1746 buf.writeByte('C'); // 'C' means complex
1748 switch (t.ty)
1750 case Timaginary32:
1751 case Tcomplex32:
1752 return Type.tfloat32.accept(this);
1753 case Timaginary64:
1754 case Tcomplex64:
1755 return Type.tfloat64.accept(this);
1756 case Timaginary80:
1757 case Tcomplex80:
1758 return Type.tfloat80.accept(this);
1759 default:
1760 assert(0);
1764 char c;
1765 char p = 0;
1766 switch (t.ty)
1768 case Tvoid: c = 'v'; break;
1769 case Tint8: c = 'a'; break;
1770 case Tuns8: c = 'h'; break;
1771 case Tint16: c = 's'; break;
1772 case Tuns16: c = 't'; break;
1773 case Tint32: c = 'i'; break;
1774 case Tuns32: c = 'j'; break;
1775 case Tfloat32: c = 'f'; break;
1776 case Tint64:
1777 c = target.c.longsize == 8 ? 'l' : 'x';
1778 break;
1779 case Tuns64:
1780 c = target.c.longsize == 8 ? 'm' : 'y';
1781 break;
1782 case Tint128: c = 'n'; break;
1783 case Tuns128: c = 'o'; break;
1784 case Tfloat64: c = 'd'; break;
1785 case Tfloat80: c = 'e'; break;
1786 case Tbool: c = 'b'; break;
1787 case Tchar: c = 'c'; break;
1788 case Twchar: p = 'D'; c = 's'; break; // since C++11
1789 case Tdchar: p = 'D'; c = 'i'; break; // since C++11
1791 default:
1792 return error(t);
1794 writeBasicType(t, p, c);
1797 override void visit(TypeVector t)
1799 if (t.isImmutable() || t.isShared())
1800 return error(t);
1802 if (substitute(t))
1803 return;
1804 append(t);
1805 CV_qualifiers(t);
1807 // Handle any target-specific vector types.
1808 if (auto tm = target.cpp.typeMangle(t))
1810 buf.writestring(tm);
1812 else
1814 assert(t.basetype && t.basetype.ty == Tsarray);
1815 auto tsa = t.basetype.isTypeSArray();
1816 assert(tsa.dim);
1817 buf.writestring("Dv"); // -- Gnu ABI v.4
1818 buf.print(tsa.dim.toInteger());
1819 buf.writeByte('_');
1820 t.basetype.nextOf().accept(this);
1824 override void visit(TypeSArray t)
1826 if (t.isImmutable() || t.isShared())
1827 return error(t);
1829 if (!substitute(t))
1830 append(t);
1831 CV_qualifiers(t);
1832 buf.writeByte('A');
1833 buf.print(t.dim ? t.dim.toInteger() : 0);
1834 buf.writeByte('_');
1835 t.next.accept(this);
1838 override void visit(TypePointer t)
1840 if (t.isImmutable() || t.isShared())
1841 return error(t);
1843 // Check for const - Since we cannot represent C++'s `char* const`,
1844 // and `const char* const` (a.k.a `const(char*)` in D) is mangled
1845 // the same as `const char*` (`const(char)*` in D), we need to add
1846 // an extra `K` if `nextOf()` is `const`, before substitution
1847 CV_qualifiers(t);
1848 if (substitute(t))
1849 return;
1850 buf.writeByte('P');
1851 auto prev = this.context.push(this.context.res.asType().nextOf());
1852 scope (exit) this.context.pop(prev);
1853 t.next.accept(this);
1854 append(t);
1857 override void visit(TypeReference t)
1859 if (substitute(t))
1860 return;
1861 buf.writeByte('R');
1862 CV_qualifiers(t.nextOf());
1863 headOfType(t.nextOf());
1864 if (t.nextOf().isConst())
1865 append(t.nextOf());
1866 append(t);
1869 override void visit(TypeFunction t)
1872 * <function-type> ::= F [Y] <bare-function-type> E
1873 * <bare-function-type> ::= <signature type>+
1874 * # types are possible return type, then parameter types
1876 /* ABI says:
1877 "The type of a non-static member function is considered to be different,
1878 for the purposes of substitution, from the type of a namespace-scope or
1879 static member function whose type appears similar. The types of two
1880 non-static member functions are considered to be different, for the
1881 purposes of substitution, if the functions are members of different
1882 classes. In other words, for the purposes of substitution, the class of
1883 which the function is a member is considered part of the type of
1884 function."
1886 BUG: Right now, types of functions are never merged, so our simplistic
1887 component matcher always finds them to be different.
1888 We should use Type.equals on these, and use different
1889 TypeFunctions for non-static member functions, and non-static
1890 member functions of different classes.
1892 if (substitute(t))
1893 return;
1894 buf.writeByte('F');
1895 if (t.linkage == LINK.c)
1896 buf.writeByte('Y');
1897 Type tn = t.next;
1898 if (t.isref)
1899 tn = tn.referenceTo();
1900 tn.accept(this);
1901 mangleFunctionParameters(t.parameterList);
1902 buf.writeByte('E');
1903 append(t);
1906 override void visit(TypeStruct t)
1908 if (t.isImmutable() || t.isShared())
1909 return error(t);
1910 //printf("TypeStruct %s\n", t.toChars());
1911 doSymbol(t);
1914 override void visit(TypeEnum t)
1916 if (t.isImmutable() || t.isShared())
1917 return error(t);
1919 /* __c_(u)long(long) and others get special mangling
1921 const id = t.sym.ident;
1922 //printf("enum id = '%s'\n", id.toChars());
1923 if (id == Id.__c_long)
1924 return writeBasicType(t, 0, 'l');
1925 else if (id == Id.__c_ulong)
1926 return writeBasicType(t, 0, 'm');
1927 else if (id == Id.__c_char)
1928 return writeBasicType(t, 0, 'c');
1929 else if (id == Id.__c_wchar_t)
1930 return writeBasicType(t, 0, 'w');
1931 else if (id == Id.__c_longlong)
1932 return writeBasicType(t, 0, 'x');
1933 else if (id == Id.__c_ulonglong)
1934 return writeBasicType(t, 0, 'y');
1935 else if (id == Id.__c_complex_float)
1936 return Type.tcomplex32.accept(this);
1937 else if (id == Id.__c_complex_double)
1938 return Type.tcomplex64.accept(this);
1939 else if (id == Id.__c_complex_real)
1940 return Type.tcomplex80.accept(this);
1942 doSymbol(t);
1945 override void visit(TypeClass t)
1947 mangleTypeClass(t, false);
1951 * Performs template parameter substitution
1953 * Mangling is performed on a copy of the post-parsing AST before
1954 * any semantic pass is run.
1955 * There is no easy way to link a type to the template parameters
1956 * once semantic has run, because:
1957 * - the `TemplateInstance` installs aliases in its scope to its params
1958 * - `AliasDeclaration`s are resolved in many places
1959 * - semantic passes are destructive, so the `TypeIdentifier` gets lost
1961 * As a result, the best approach with the current architecture is to:
1962 * - Run the visitor on the `originalType` of the function,
1963 * looking up any `TypeIdentifier` at the template scope when found.
1964 * - Fallback to the post-semantic `TypeFunction` when the identifier is
1965 * not a template parameter.
1967 override void visit(TypeIdentifier t)
1969 auto decl = this.context.ti.tempdecl.isTemplateDeclaration();
1970 assert(decl.parameters !is null);
1971 auto idx = templateParamIndex(t.ident, decl.parameters);
1972 // If not found, default to the post-semantic type
1973 if (idx >= decl.parameters.length)
1974 return this.context.res.visitObject(this);
1976 auto param = (*decl.parameters)[idx];
1977 if (auto type = this.context.res.isType())
1978 CV_qualifiers(type);
1979 // Otherwise, attempt substitution (`S_` takes precedence on `T_`)
1980 if (this.substitute(param))
1981 return;
1983 // If substitution failed, write `TX_` where `X` is the index
1984 this.writeTemplateArgIndex(idx, param);
1985 this.append(param);
1986 // Write the ABI tags, if any
1987 if (auto sym = this.context.res.isDsymbol())
1988 this.abiTags.writeSymbol(sym, this);
1991 /// Ditto
1992 override void visit(TypeInstance t)
1994 assert(t.tempinst !is null);
1995 t.tempinst.accept(this);
1999 * Mangles a `TemplateInstance`
2001 * A `TemplateInstance` can be found either in the parameter,
2002 * or the return value.
2003 * Arguments to the template instance needs to be mangled but the template
2004 * can be partially substituted, so for example the following:
2005 * `Container!(T, Val) func16479_12 (alias Container, T, int Val) ()`
2006 * will mangle the return value part to "T_IT0_XT1_EE"
2008 override void visit(TemplateInstance t)
2010 // Template names are substituted, but args still need to be written
2011 void writeArgs ()
2013 buf.writeByte('I');
2014 // When visiting the arguments, the context will be set to the
2015 // resolved type
2016 auto analyzed_ti = this.context.res.asType().toDsymbol(null).isInstantiated();
2017 auto prev = this.context;
2018 scope (exit) this.context.pop(prev);
2019 foreach (idx, RootObject o; *t.tiargs)
2021 this.context.res = (*analyzed_ti.tiargs)[idx];
2022 o.visitObject(this);
2024 if (analyzed_ti.tiargs.length > t.tiargs.length)
2026 // If the resolved AST has more args than the parse one,
2027 // we have default arguments
2028 auto oparams = analyzed_ti.tempdecl.isTemplateDeclaration().origParameters;
2029 foreach (idx, arg; (*oparams)[t.tiargs.length .. $])
2031 this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.length];
2033 if (auto ttp = arg.isTemplateTypeParameter())
2034 ttp.defaultType.accept(this);
2035 else if (auto tvp = arg.isTemplateValueParameter())
2036 tvp.defaultValue.accept(this);
2037 else if (auto tvp = arg.isTemplateThisParameter())
2038 tvp.defaultType.accept(this);
2039 else if (auto tvp = arg.isTemplateAliasParameter())
2040 tvp.defaultAlias.visitObject(this);
2041 else
2042 assert(0, arg.toString());
2045 buf.writeByte('E');
2048 // `name` is used, not `ident`
2049 assert(t.name !is null);
2050 assert(t.tiargs !is null);
2052 bool needsTa;
2053 auto decl = this.context.ti.tempdecl.isTemplateDeclaration();
2054 // Attempt to substitute the template itself
2055 auto idx = templateParamIndex(t.name, decl.parameters);
2056 if (idx < decl.parameters.length)
2058 auto param = (*decl.parameters)[idx];
2059 if (auto type = t.getType())
2060 CV_qualifiers(type);
2061 if (this.substitute(param))
2062 return;
2063 this.writeTemplateArgIndex(idx, param);
2064 this.append(param);
2065 writeArgs();
2067 else if (this.writeStdSubstitution(t, needsTa))
2069 if (needsTa)
2070 writeArgs();
2072 else if (!this.substitute(t))
2073 this.writeQualified(t, &writeArgs);
2076 /// Ditto
2077 override void visit(IntegerExp t)
2079 this.buf.writeByte('L');
2080 t.type.accept(this);
2081 this.buf.print(t.getInteger());
2082 this.buf.writeByte('E');
2085 override void visit(Nspace t)
2087 if (auto p = getQualifier(t))
2088 p.accept(this);
2090 if (isStd(t))
2091 buf.writestring("St");
2092 else
2094 this.writeIdentifier(t.ident);
2095 this.append(t);
2099 override void visit(Type t)
2101 error(t);
2104 void visit(Tuple t)
2106 assert(0);
2110 /// Helper code to visit `RootObject`, as it doesn't define `accept`,
2111 /// only its direct subtypes do.
2112 private void visitObject(V : Visitor)(RootObject o, V this_)
2114 assert(o !is null);
2115 if (Type ta = isType(o))
2116 ta.accept(this_);
2117 else if (Expression ea = isExpression(o))
2118 ea.accept(this_);
2119 else if (Dsymbol sa = isDsymbol(o))
2120 sa.accept(this_);
2121 else if (TemplateParameter t = isTemplateParameter(o))
2122 t.accept(this_);
2123 else if (Tuple t = isTuple(o))
2124 // `Tuple` inherits `RootObject` and does not define accept
2125 // For this reason, this uses static dispatch on the visitor
2126 this_.visit(t);
2127 else
2128 assert(0, o.toString());
2131 /// Helper function to safely get a type out of a `RootObject`
2132 private Type asType(RootObject o) @safe
2134 if (Type ta = isType(o))
2135 return ta;
2137 // When called with context.res as argument, it can be `FuncDeclaration`
2138 if (auto fd = o.asFuncDecl())
2139 return fd.type;
2140 assert(0);
2143 /// Helper function to safely get a `FuncDeclaration` out of a `RootObject`
2144 private FuncDeclaration asFuncDecl(RootObject o) @safe
2146 Dsymbol d = isDsymbol(o);
2147 assert(d !is null);
2148 auto fd = d.isFuncDeclaration();
2149 assert(fd !is null);
2150 return fd;
2153 /// Helper class to compare entries in components
2154 private extern(C++) final class ComponentVisitor : Visitor
2156 /// Only one of the following is not `null`, it's always
2157 /// the most specialized type, set from the ctor
2158 private Nspace namespace;
2160 /// Ditto
2161 private CPPNamespaceDeclaration namespace2;
2163 /// Ditto
2164 private TypePointer tpointer;
2166 /// Ditto
2167 private TypeReference tref;
2169 /// Ditto
2170 private TypeIdentifier tident;
2172 /// Least specialized type
2173 private RootObject object;
2175 /// Set to the result of the comparison
2176 private bool result;
2178 public this(RootObject base) @safe
2180 switch (base.dyncast())
2182 case DYNCAST.dsymbol:
2183 if (auto ns = (cast(Dsymbol)base).isNspace())
2184 this.namespace = ns;
2185 else if (auto ns = (cast(Dsymbol)base).isCPPNamespaceDeclaration())
2186 this.namespace2 = ns;
2187 else
2188 goto default;
2189 break;
2191 case DYNCAST.type:
2192 auto t = cast(Type)base;
2193 if (auto tp = t.isTypePointer())
2194 this.tpointer = tp;
2195 else if (auto tr = t.isTypeReference())
2196 this.tref = tr;
2197 else if (auto ti = t.isTypeIdentifier())
2198 this.tident = ti;
2199 else
2200 goto default;
2201 break;
2203 // Note: ABI tags are also handled here (they are TupleExp of StringExp)
2204 default:
2205 this.object = base;
2209 /// Introduce base class overloads
2210 alias visit = Visitor.visit;
2212 /// Least specialized overload of each direct child of `RootObject`
2213 public override void visit(Dsymbol o)
2215 this.result = this.object && this.object == o;
2218 /// Ditto
2219 public override void visit(Expression o)
2221 this.result = this.object && this.object == o;
2224 /// Ditto
2225 public void visit(Tuple o)
2227 this.result = this.object && this.object == o;
2230 /// Ditto
2231 public override void visit(Type o)
2233 this.result = this.object && this.object == o;
2236 /// Ditto
2237 public override void visit(TemplateParameter o)
2239 this.result = this.object && this.object == o;
2243 * This overload handles composed types including template parameters
2245 * Components for substitutions include "next" type.
2246 * For example, if `ref T` is present, `ref T` and `T` will be present
2247 * in the substitution array.
2248 * But since we don't have the final/merged type, we cannot rely on
2249 * object comparison, and need to recurse instead.
2251 public override void visit(TypeReference o)
2253 if (!this.tref)
2254 return;
2255 if (this.tref == o)
2256 this.result = true;
2257 else
2259 // It might be a reference to a template parameter that we already
2260 // saw, so we need to recurse
2261 scope v = new ComponentVisitor(this.tref.next);
2262 o.next.visitObject(v);
2263 this.result = v.result;
2267 /// Ditto
2268 public override void visit(TypePointer o)
2270 if (!this.tpointer)
2271 return;
2272 if (this.tpointer == o)
2273 this.result = true;
2274 else
2276 // It might be a pointer to a template parameter that we already
2277 // saw, so we need to recurse
2278 scope v = new ComponentVisitor(this.tpointer.next);
2279 o.next.visitObject(v);
2280 this.result = v.result;
2284 /// Ditto
2285 public override void visit(TypeIdentifier o)
2287 /// Since we know they are at the same level, scope resolution will
2288 /// give us the same symbol, thus we can just compare ident.
2289 this.result = (this.tident && (this.tident.ident == o.ident));
2293 * Overload which accepts a Namespace
2295 * It is very common for large C++ projects to have multiple files sharing
2296 * the same `namespace`. If any D project adopts the same approach
2297 * (e.g. separating data structures from functions), it will lead to two
2298 * `Nspace` objects being instantiated, with different addresses.
2299 * At the same time, we cannot compare just any Dsymbol via identifier,
2300 * because it messes with templates.
2302 * See_Also:
2303 * https://issues.dlang.org/show_bug.cgi?id=18922
2305 * Params:
2306 * ns = C++ namespace to do substitution for
2308 public override void visit(Nspace ns)
2310 this.result = isNamespaceEqual(this.namespace, ns)
2311 || isNamespaceEqual(this.namespace2, ns);
2314 /// Ditto
2315 public override void visit(CPPNamespaceDeclaration ns)
2317 this.result = isNamespaceEqual(this.namespace, ns)
2318 || isNamespaceEqual(this.namespace2, ns);
2322 /// Transitional functions for `CPPNamespaceDeclaration` / `Nspace`
2323 /// Remove when `Nspace` is removed.
2324 private bool isNamespaceEqual (Nspace a, Nspace b)
2326 if (a is null || b is null)
2327 return false;
2328 return a.equals(b);
2331 /// Ditto
2332 private bool isNamespaceEqual (Nspace a, CPPNamespaceDeclaration b)
2334 return isNamespaceEqual(b, a);
2337 /// Ditto
2338 private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = 0)
2340 if ((a is null) != (b is null))
2341 return false;
2342 if (!a.ident.equals(b.ident))
2343 return false;
2345 // We need to see if there's more ident enclosing
2346 if (auto pb = b.toParent().isNspace())
2347 return isNamespaceEqual(a.cppnamespace, pb);
2348 else
2349 return a.cppnamespace is null;
2352 /// Returns:
2353 /// Whether two `CPPNamespaceDeclaration` are equals
2354 private bool isNamespaceEqual (CPPNamespaceDeclaration a, CPPNamespaceDeclaration b) @safe
2356 if (a is null || b is null)
2357 return false;
2359 if ((a.cppnamespace is null) != (b.cppnamespace is null))
2360 return false;
2361 if (a.ident != b.ident)
2362 return false;
2363 return a.cppnamespace is null ? true : isNamespaceEqual(a.cppnamespace, b.cppnamespace);
2367 * A container for ABI tags
2369 * At its hearth, there is a sorted array of ABI tags having been written
2370 * already. ABI tags can be present on parameters, template parameters,
2371 * return value, and varaible. ABI tags for a given type needs to be written
2372 * sorted. When a function returns a type that has ABI tags, only the tags that
2373 * haven't been printed as part of the mangling (e.g. arguments) are written
2374 * directly after the function name.
2376 * This means that:
2377 * ---
2378 * /++ C++ type definitions:
2379 * struct [[gnu::abi_tag("tag1")]] Struct1 {};
2380 * struct [[gnu::abi_tag("tag2")]] Struct2 {};
2381 * // Can also be: "tag2", "tag1", since tags are sorted.
2382 * struct [[gnu::abi_tag("tag1", "tag2")]] Struct3 {};
2383 * +/
2384 * // Functions definitions:
2385 * Struct3 func1 (Struct1);
2386 * Struct3 func2 (Struct2);
2387 * Struct3 func3 (Struct2, Struct1);
2388 * ---
2389 * Will be respectively pseudo-mangled (part of interest between stars) as:
2390 * "_Z4 func1 *B4tag2* ParamsMangling" (ParamsMangling includes tag1),
2391 * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes tag2),
2392 * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes both).
2394 * This is why why need to keep a list of tags that were written,
2395 * and insert the missing one after parameter mangling has been written.
2396 * Since there's a lot of operations that are not easily doable in DMD
2397 * (since we can't use Phobos), this special container is implemented.
2399 private struct ABITagContainer
2401 private Array!StringExp written;
2403 static ArrayLiteralExp forSymbol (Dsymbol s)
2405 if (!s)
2406 return null;
2407 // If this is a template instance, we want the declaration,
2408 // as that's where the UDAs are
2409 if (auto ti = s.isTemplateInstance())
2410 s = ti.tempdecl;
2411 if (!s.userAttribDecl || !s.userAttribDecl.atts)
2412 return null;
2414 foreach (exp; *s.userAttribDecl.atts)
2416 if (UserAttributeDeclaration.isGNUABITag(exp))
2417 return (*exp.isStructLiteralExp().elements)[0]
2418 .isArrayLiteralExp();
2420 return null;
2423 void writeSymbol(Dsymbol s, CppMangleVisitor self)
2425 auto tale = forSymbol(s);
2426 if (!tale) return;
2427 if (self.substitute(tale))
2428 return;
2429 this.write(*self.buf, tale);
2433 * Write an ArrayLiteralExp (expected to be an ABI tag) to the buffer
2435 * Params:
2436 * buf = Buffer to write mangling to
2437 * ale = GNU ABI tag array literal expression, semantically analyzed
2439 void write (ref OutBuffer buf, ArrayLiteralExp ale, bool skipKnown = false)
2441 void writeElem (StringExp exp)
2443 const tag = exp.peekString();
2444 buf.writestring("B");
2445 buf.print(tag.length);
2446 buf.writestring(tag);
2449 bool match;
2450 foreach (exp; *ale.elements)
2452 auto elem = exp.toStringExp();
2453 auto idx = closestIndex(this.written[], elem, match);
2454 if (!match)
2456 writeElem(elem);
2457 this.written.insert(idx, elem);
2459 else if (!skipKnown)
2460 writeElem(elem);
2466 * Returns the closest index to to `exp` in `slice`
2468 * Performs a binary search on `slice` (assumes `slice` is sorted),
2469 * and returns either `exp`'s index in `slice` if `exact` is `true`,
2470 * or the index at which `exp` can be inserted in `slice` if `exact is `false`.
2471 * Inserting `exp` at the return value will keep the array sorted.
2473 * Params:
2474 * slice = The sorted slice to search into
2475 * exp = The string expression to search for
2476 * exact = If `true` on return, `exp` was found in `slice`
2478 * Returns:
2479 * Either the index to insert `exp` at (if `exact == false`),
2480 * or the index of `exp` in `slice`.
2482 private size_t closestIndex (const(StringExp)[] slice, StringExp exp, out bool exact)
2484 if (!slice.length) return 0;
2486 const StringExp* first = slice.ptr;
2487 while (true)
2489 int res = dstrcmp(exp.peekString(), slice[$ / 2].peekString());
2490 if (res == 0)
2492 exact = true;
2493 return (&slice[$/2] - first);
2496 if (slice.length == 1)
2497 return (slice.ptr - first) + (res > 0);
2498 slice = slice[(res > 0 ? $ / 2 : 0) .. (res > 0 ? $ : $ / 2)];
2503 unittest
2505 bool match;
2506 auto s1 = new StringExp(Loc.initial, "Amande");
2507 auto s2 = new StringExp(Loc.initial, "Baguette");
2508 auto s3 = new StringExp(Loc.initial, "Croissant");
2509 auto s4 = new StringExp(Loc.initial, "Framboises");
2510 auto s5 = new StringExp(Loc.initial, "Proscuitto");
2512 // Found, odd size
2513 assert(closestIndex([s1, s2, s3, s4, s5], s1, match) == 0 && match);
2514 assert(closestIndex([s1, s2, s3, s4, s5], s2, match) == 1 && match);
2515 assert(closestIndex([s1, s2, s3, s4, s5], s3, match) == 2 && match);
2516 assert(closestIndex([s1, s2, s3, s4, s5], s4, match) == 3 && match);
2517 assert(closestIndex([s1, s2, s3, s4, s5], s5, match) == 4 && match);
2519 // Not found, even size
2520 assert(closestIndex([s2, s3, s4, s5], s1, match) == 0 && !match);
2521 assert(closestIndex([s1, s3, s4, s5], s2, match) == 1 && !match);
2522 assert(closestIndex([s1, s2, s4, s5], s3, match) == 2 && !match);
2523 assert(closestIndex([s1, s2, s3, s5], s4, match) == 3 && !match);
2524 assert(closestIndex([s1, s2, s3, s4], s5, match) == 4 && !match);
2526 // Found, even size
2527 assert(closestIndex([s1, s2, s3, s4], s1, match) == 0 && match);
2528 assert(closestIndex([s1, s2, s3, s4], s2, match) == 1 && match);
2529 assert(closestIndex([s1, s2, s3, s4], s3, match) == 2 && match);
2530 assert(closestIndex([s1, s2, s3, s4], s4, match) == 3 && match);
2531 assert(closestIndex([s1, s3, s4, s5], s5, match) == 3 && match);
2533 // Not found, odd size
2534 assert(closestIndex([s2, s4, s5], s1, match) == 0 && !match);
2535 assert(closestIndex([s1, s4, s5], s2, match) == 1 && !match);
2536 assert(closestIndex([s1, s2, s4], s3, match) == 2 && !match);
2537 assert(closestIndex([s1, s3, s5], s4, match) == 2 && !match);
2538 assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match);
2541 /***
2542 * Visits the return type of a function and writes leftover ABI tags
2543 * Params:
2544 * tf = Type of the function to mangle the return type of
2545 * previous = already written ones
2546 * toWrite = where to put StringExp's to be written
2548 private
2549 void leftOver(TypeFunction tf, const(Array!StringExp)* previous, Array!StringExp* toWrite)
2551 extern(C++) final class LeftoverVisitor : Visitor
2553 /// List of tags to write
2554 private Array!StringExp* toWrite;
2555 /// List of tags to ignore
2556 private const(Array!StringExp)* ignore;
2559 public this(const(Array!StringExp)* previous, Array!StringExp* toWrite) @safe
2561 this.ignore = previous;
2562 this.toWrite = toWrite;
2565 /// Reintroduce base class overloads
2566 public alias visit = Visitor.visit;
2568 /// Least specialized overload of each direct child of `RootObject`
2569 public override void visit(Dsymbol o)
2571 auto ale = ABITagContainer.forSymbol(o);
2572 if (!ale) return;
2574 bool match;
2575 foreach (elem; *ale.elements)
2577 auto se = elem.toStringExp();
2578 closestIndex((*this.ignore)[], se, match);
2579 if (match) continue;
2580 auto idx = closestIndex((*this.toWrite)[], se, match);
2581 if (!match)
2582 (*this.toWrite).insert(idx, se);
2586 /// Ditto
2587 public override void visit(Type o)
2589 if (auto sym = o.toDsymbol(null))
2590 sym.accept(this);
2593 /// Composite type
2594 public override void visit(TypePointer o)
2596 o.next.accept(this);
2599 public override void visit(TypeReference o)
2601 o.next.accept(this);
2605 scope remainingVisitor = new LeftoverVisitor(previous, toWrite);
2606 tf.next.accept(remainingVisitor);