2 * Does name mangling for `extern(D)` symbols.
4 * Specification: $(LINK2 https://dlang.org/spec/abi.html#name_mangling, Name Mangling)
6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: Walter Bright, http://www.digitalmars.com
8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmangle.d, _dmangle.d)
10 * Documentation: https://dlang.org/phobos/dmd_dmangle.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmangle.d
12 * References: https://dlang.org/blog/2017/12/20/ds-newfangled-name-mangling/
19 /******************************************************************************
20 * Returns exact mangled name of function.
22 extern (C++) const(char)* mangleExact(FuncDeclaration fd)
27 scope Mangler v = new Mangler(&buf);
29 fd.mangleString = buf.extractChars();
31 return fd.mangleString;
34 extern (C++) void mangleToBuffer(Type t, OutBuffer* buf)
37 buf.writestring(t.deco);
40 scope Mangler v = new Mangler(buf, t);
41 v.visitWithMask(t, 0);
45 extern (C++) void mangleToBuffer(Expression e, OutBuffer* buf)
47 scope Mangler v = new Mangler(buf);
51 extern (C++) void mangleToBuffer(Dsymbol s, OutBuffer* buf)
53 scope Mangler v = new Mangler(buf);
57 extern (C++) void mangleToBuffer(TemplateInstance ti, OutBuffer* buf)
59 scope Mangler v = new Mangler(buf);
60 v.mangleTemplateInstance(ti);
63 /// Returns: `true` if the given character is a valid mangled character
64 package bool isValidMangling(dchar c) nothrow
67 c >= 'A' && c <= 'Z' ||
68 c >= 'a' && c <= 'z' ||
69 c >= '0' && c <= '9' ||
70 c != 0 && strchr("$%().:?@[]_", c) ||
74 // valid mangled characters
77 assert('a'.isValidMangling);
78 assert('B'.isValidMangling);
79 assert('2'.isValidMangling);
80 assert('@'.isValidMangling);
81 assert('_'.isValidMangling);
84 // invalid mangled characters
87 assert(!'-'.isValidMangling);
88 assert(!0.isValidMangling);
89 assert(!'/'.isValidMangling);
90 assert(!'\\'.isValidMangling);
93 /**********************************************
94 * Convert a string representing a type (the deco) and
95 * return its equivalent Type.
97 * deco = string containing the deco
99 * null for failed to convert
103 public Type decoToType(const(char)[] deco)
105 //printf("decoToType(): %.*s\n", cast(int)deco.length, deco.ptr);
106 if (auto sv = Type.stringtable.lookup(deco))
110 Type t = cast(Type)sv.value;
119 /***************************************** private ***************************************/
124 import core.stdc.ctype;
125 import core.stdc.stdio;
126 import core.stdc.string;
128 import dmd.aggregate;
129 import dmd.arraytypes;
131 import dmd.declaration;
134 import dmd.dtemplate;
135 import dmd.expression;
139 import dmd.identifier;
141 import dmd.root.ctfloat;
142 import dmd.root.outbuffer;
144 import dmd.root.string;
145 import dmd.root.stringtable;
151 private immutable char[TMAX] mangleChar =
186 Tfunction : 'F', // D function
193 // M // has this, or scope
194 // N // Nh:vector Ng:wild Nn:noreturn
197 // Q // Type/symbol/identifier backward reference
202 // W // Windows function
203 // X // variadic T t...)
204 // Y // variadic T t,...)
205 // Z // not variadic, end of parameters
207 // '@' shouldn't appear anywhere in the deco'd names
219 Tnoreturn : '@', // becomes 'Nn'
224 foreach (i, mangle; mangleChar)
226 if (mangle == char.init)
228 fprintf(stderr, "ty = %u\n", cast(uint)i);
234 /***********************
235 * Mangle basic type ty to buf.
238 private void tyToDecoBuffer(OutBuffer* buf, int ty)
240 const c = mangleChar[ty];
243 buf.writeByte(ty == Tint128 ? 'i' : 'k');
246 /*********************************
249 private void MODtoDecoBuffer(OutBuffer* buf, MOD mod)
255 case MODFlags.const_:
258 case MODFlags.immutable_:
261 case MODFlags.shared_:
264 case MODFlags.shared_ | MODFlags.const_:
265 buf.writestring("Ox");
268 buf.writestring("Ng");
270 case MODFlags.wildconst:
271 buf.writestring("Ngx");
273 case MODFlags.shared_ | MODFlags.wild:
274 buf.writestring("ONg");
276 case MODFlags.shared_ | MODFlags.wildconst:
277 buf.writestring("ONgx");
284 private extern (C++) final class Mangler : Visitor
286 alias visit = Visitor.visit;
288 static assert(Key.sizeof == size_t.sizeof);
289 AssocArray!(Type, size_t) types; // Type => (offset+1) in buf
290 AssocArray!(Identifier, size_t) idents; // Identifier => (offset+1) in buf
294 extern (D) this(OutBuffer* buf, Type rootType = null)
297 this.rootType = rootType;
301 * writes a back reference with the relative position encoded with base 26
302 * using upper case letters for all digits but the last digit which uses
303 * a lower case letter.
304 * The decoder has to look up the referenced position to determine
305 * whether the back reference is an identifier (starts with a digit)
306 * or a type (starts with a letter).
309 * pos = relative position to encode
311 void writeBackRef(size_t pos)
316 while (pos >= mul * base)
320 auto dig = cast(ubyte)(pos / mul);
321 buf.writeByte('A' + dig);
325 buf.writeByte('a' + cast(ubyte)pos);
329 * Back references a non-basic type
331 * The encoded mangling is
332 * 'Q' <relative position of first occurrence of type>
335 * t = the type to encode via back referencing
338 * true if the type was found. A back reference has been encoded.
339 * false if the type was not found. The current position is saved for later back references.
341 bool backrefType(Type t)
347 * https://issues.dlang.org/show_bug.cgi?id=21591
349 * Special case for unmerged TypeFunctions: use the generic merged
350 * function type as backref cache key to avoid missed backrefs.
352 * Merging is based on mangling, so we need to avoid an infinite
353 * recursion by excluding the case where `t` is the root type passed to
354 * `mangleToBuffer()`.
358 if (t.isFunction_Delegate_PtrToFunction())
364 return backrefImpl(types, t);
368 * Back references a single identifier
370 * The encoded mangling is
371 * 'Q' <relative position of first occurrence of type>
374 * id = the identifier to encode via back referencing
377 * true if the identifier was found. A back reference has been encoded.
378 * false if the identifier was not found. The current position is saved for later back references.
380 bool backrefIdentifier(Identifier id)
382 return backrefImpl(idents, id);
385 private extern(D) bool backrefImpl(T)(ref AssocArray!(T, size_t) aa, T key)
387 auto p = aa.getLvalue(key);
390 const offset = *p - 1;
391 writeBackRef(buf.length - offset);
398 void mangleSymbol(Dsymbol s)
403 void mangleType(Type t)
409 void mangleIdentifier(Identifier id, Dsymbol s)
411 if (!backrefIdentifier(id))
412 toBuffer(id.toString(), s);
415 ////////////////////////////////////////////////////////////////////////////
416 /**************************************************
419 void visitWithMask(Type t, ubyte modMask)
421 if (modMask != t.mod)
423 MODtoDecoBuffer(buf, t.mod);
428 override void visit(Type t)
430 tyToDecoBuffer(buf, t.ty);
433 override void visit(TypeNext t)
436 visitWithMask(t.next, t.mod);
439 override void visit(TypeVector t)
441 buf.writestring("Nh");
442 visitWithMask(t.basetype, t.mod);
445 override void visit(TypeSArray t)
449 buf.print(t.dim.toInteger());
451 visitWithMask(t.next, t.mod);
454 override void visit(TypeDArray t)
458 visitWithMask(t.next, t.mod);
461 override void visit(TypeAArray t)
464 visitWithMask(t.index, 0);
465 visitWithMask(t.next, t.mod);
468 override void visit(TypeFunction t)
470 //printf("TypeFunction.toDecoBuffer() t = %p %s\n", t, t.toChars());
471 //static int nest; if (++nest == 50) *(char*)0=0;
472 mangleFuncType(t, t, t.mod, t.next);
475 void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret)
477 //printf("mangleFuncType() %s\n", t.toChars());
480 // printf("TypeFunction.mangleFuncType() t = %s inuse\n", t.toChars());
481 t.inuse = 2; // flag error to caller
485 if (modMask != t.mod)
486 MODtoDecoBuffer(buf, t.mod);
489 final switch (t.linkage)
512 buf.writestring("Na");
514 buf.writestring("Nb");
516 buf.writestring("Nc");
518 buf.writestring("Nd");
520 buf.writestring("Ni");
522 if (ta.isreturn && !ta.isreturninferred)
523 buf.writestring("Nj");
524 else if (ta.isScopeQual && !ta.isscopeinferred)
525 buf.writestring("Nl");
528 buf.writestring("Nm");
533 buf.writestring("Ne");
536 buf.writestring("Nf");
542 // Write argument types
543 foreach (idx, param; t.parameterList)
545 //if (buf.data[buf.length - 1] == '@') assert(0);
546 buf.writeByte('Z' - t.parameterList.varargs); // mark end of arg list
548 visitWithMask(tret, 0);
552 override void visit(TypeIdentifier t)
555 auto name = t.ident.toString();
556 buf.print(cast(int)name.length);
557 buf.writestring(name);
560 override void visit(TypeEnum t)
566 override void visit(TypeStruct t)
568 //printf("TypeStruct.toDecoBuffer('%s') = '%s'\n", t.toChars(), name);
573 override void visit(TypeClass t)
575 //printf("TypeClass.toDecoBuffer('%s' mod=%x) = '%s'\n", t.toChars(), mod, name);
580 override void visit(TypeTuple t)
582 //printf("TypeTuple.toDecoBuffer() t = %p, %s\n", t, t.toChars());
584 Parameter._foreach(t.arguments, (idx, param) {
591 override void visit(TypeNull t)
596 override void visit(TypeNoreturn t)
598 buf.writestring("Nn");
601 ////////////////////////////////////////////////////////////////////////////
602 void mangleDecl(Declaration sthis)
606 mangleIdentifier(sthis.ident, sthis);
607 if (FuncDeclaration fd = sthis.isFuncDeclaration())
609 mangleFunc(fd, false);
613 visitWithMask(sthis.type, 0);
619 void mangleParent(Dsymbol s)
621 //printf("mangleParent() %s %s\n", s.kind(), s.toChars());
623 if (TemplateInstance ti = s.isTemplateInstance())
624 p = ti.isTemplateMixin() ? ti.parent : ti.tempdecl.parent;
629 uint localNum = s.localNum;
631 auto ti = p.isTemplateInstance();
632 if (ti && !ti.isTemplateMixin())
634 localNum = ti.tempdecl.localNum;
635 mangleTemplateInstance(ti);
637 else if (p.getIdent())
639 mangleIdentifier(p.ident, s);
640 if (FuncDeclaration f = p.isFuncDeclaration())
646 /* There can be multiple different declarations in the same
647 * function that have the same mangled name.
648 * This results in localNum having a non-zero number, which
649 * is used to add a fake parent of the form `__Sddd` to make
650 * the mangled names unique.
651 * https://issues.dlang.org/show_bug.cgi?id=20565
662 buf.printf("%u__S%u", ndigits + 3, localNum);
667 void mangleFunc(FuncDeclaration fd, bool inParent)
669 //printf("deco = '%s'\n", fd.type.deco ? fd.type.deco : "null");
670 //printf("fd.type = %s\n", fd.type.toChars());
671 if (fd.needThis() || fd.isNested())
674 if (!fd.type || fd.type.ty == Terror)
676 // never should have gotten here, but could be the result of
677 // failed speculative compilation
678 buf.writestring("9__error__FZ");
680 //printf("[%s] %s no type\n", fd.loc.toChars(), fd.toChars());
681 //assert(0); // don't mangle function until semantic3 done.
685 TypeFunction tf = fd.type.isTypeFunction();
686 TypeFunction tfo = fd.originalType.isTypeFunction();
687 mangleFuncType(tf, tfo, 0, null);
691 visitWithMask(fd.type, 0);
695 /************************************************************
696 * Write length prefixed string to buf.
698 extern (D) void toBuffer(const(char)[] id, Dsymbol s)
700 const len = id.length;
701 if (buf.length + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone
702 s.error("excessive length %llu for symbol, possible recursive expansion?", cast(ulong)(buf.length + len));
710 /************************************************************
711 * Try to obtain an externally mangled identifier from a declaration.
712 * If the declaration is at global scope or mixed in at global scope,
713 * the user might want to call it externally, so an externally mangled
714 * name is returned. Member functions or nested functions can't be called
715 * externally in C, so in that case null is returned. C++ does support
716 * namespaces, so extern(C++) always gives a C++ mangled name.
718 * See also: https://issues.dlang.org/show_bug.cgi?id=20012
721 * d = declaration to mangle
724 * an externally mangled name or null if the declaration cannot be called externally
726 extern (D) static const(char)[] externallyMangledIdentifier(Declaration d)
728 const par = d.toParent(); //toParent() skips over mixin templates
729 if (!par || par.isModule() || d.linkage == LINK.cpp)
731 if (d.linkage != LINK.d && d.localNum)
732 d.error("the same declaration cannot be in multiple scopes with non-D linkage");
733 final switch (d.linkage)
740 return d.ident.toString();
743 const p = target.cpp.toMangle(d);
744 return p.toDString();
748 d.error("forward declaration");
749 return d.ident.toString();
755 override void visit(Declaration d)
757 //printf("Declaration.mangle(this = %p, '%s', parent = '%s', linkage = %d)\n",
758 // d, d.toChars(), d.parent ? d.parent.toChars() : "null", d.linkage);
759 if (const id = externallyMangledIdentifier(d))
764 buf.writestring("_D");
768 const slice = (*buf)[];
769 assert(slice.length);
770 for (size_t pos; pos < slice.length; )
774 const s = utf_decodeChar(slice, pos, c);
775 assert(s is null, s);
776 assert(c.isValidMangling, "The mangled name '" ~ slice ~ "' " ~
777 "contains an invalid character: " ~ slice[ppos..pos]);
782 /******************************************************************************
783 * Normally FuncDeclaration and FuncAliasDeclaration have overloads.
784 * If and only if there is no overloads, mangle() could return
785 * exact mangled name.
788 * void foo(long) {} // _D4test3fooFlZv
789 * void foo(string) {} // _D4test3fooFAyaZv
791 * // from FuncDeclaration.mangle().
792 * pragma(msg, foo.mangleof); // prints unexact mangled name "4test3foo"
793 * // by calling Dsymbol.mangle()
795 * // from FuncAliasDeclaration.mangle()
796 * pragma(msg, __traits(getOverloads, test, "foo")[0].mangleof); // "_D4test3fooFlZv"
797 * pragma(msg, __traits(getOverloads, test, "foo")[1].mangleof); // "_D4test3fooFAyaZv"
799 * If a function has no overloads, .mangleof property still returns exact mangled name.
802 * pragma(msg, bar.mangleof); // still prints "_D4test3barFZv"
803 * // by calling FuncDeclaration.mangleExact().
805 override void visit(FuncDeclaration fd)
810 visit(cast(Dsymbol)fd);
814 override void visit(FuncAliasDeclaration fd)
816 FuncDeclaration f = fd.toAliasFunc();
817 FuncAliasDeclaration fa = f.isFuncAliasDeclaration();
818 if (!fd.hasOverloads && !fa)
828 visit(cast(Dsymbol)fd);
831 override void visit(OverDeclaration od)
835 visit(cast(Dsymbol)od);
838 if (FuncDeclaration fd = od.aliassym.isFuncDeclaration())
846 if (TemplateDeclaration td = od.aliassym.isTemplateDeclaration())
848 if (td.overnext is null)
854 visit(cast(Dsymbol)od);
857 void mangleExact(FuncDeclaration fd)
859 assert(!fd.isFuncAliasDeclaration());
860 if (fd.mangleOverride)
862 buf.writestring(fd.mangleOverride);
867 buf.writestring("_Dmain");
870 if (fd.isWinMain() || fd.isDllMain())
872 buf.writestring(fd.ident.toString());
875 visit(cast(Declaration)fd);
878 override void visit(VarDeclaration vd)
880 if (vd.mangleOverride)
882 buf.writestring(vd.mangleOverride);
885 visit(cast(Declaration)vd);
888 override void visit(AggregateDeclaration ad)
890 ClassDeclaration cd = ad.isClassDeclaration();
891 Dsymbol parentsave = ad.parent;
894 /* These are reserved to the compiler, so keep simple
897 if (cd.ident == Id.Exception && cd.parent.ident == Id.object || cd.ident == Id.TypeInfo || cd.ident == Id.TypeInfo_Struct || cd.ident == Id.TypeInfo_Class || cd.ident == Id.TypeInfo_Tuple || cd == ClassDeclaration.object || cd == Type.typeinfoclass || cd == Module.moduleinfo || strncmp(cd.ident.toChars(), "TypeInfo_", 9) == 0)
899 // Don't mangle parent
903 visit(cast(Dsymbol)ad);
904 ad.parent = parentsave;
907 override void visit(TemplateInstance ti)
911 printf("TemplateInstance.mangle() %p %s", ti, ti.toChars());
913 printf(" parent = %s %s", ti.parent.kind(), ti.parent.toChars());
917 ti.error("is not defined");
921 if (ti.isTemplateMixin() && ti.ident)
922 mangleIdentifier(ti.ident, ti);
924 mangleTemplateInstance(ti);
927 void mangleTemplateInstance(TemplateInstance ti)
929 TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
932 // Use "__U" for the symbols declared inside template constraint.
933 const char T = ti.members ? 'T' : 'U';
934 buf.printf("__%c", T);
935 mangleIdentifier(tempdecl.ident, tempdecl);
937 auto args = ti.tiargs;
938 size_t nparams = tempdecl.parameters.dim - (tempdecl.isVariadic() ? 1 : 0);
939 for (size_t i = 0; i < args.dim; i++)
943 Expression ea = isExpression(o);
944 Dsymbol sa = isDsymbol(o);
945 Tuple va = isTuple(o);
946 //printf("\to [%d] %p ta %p ea %p sa %p va %p\n", i, o, ta, ea, sa, va);
947 if (i < nparams && (*tempdecl.parameters)[i].specialization())
948 buf.writeByte('H'); // https://issues.dlang.org/show_bug.cgi?id=6574
952 visitWithMask(ta, 0);
956 // Don't interpret it yet, it might actually be an alias template parameter.
957 // Only constfold manifest constants, not const/immutable lvalues, see https://issues.dlang.org/show_bug.cgi?id=17339.
958 enum keepLvalue = true;
959 ea = ea.optimize(WANTvalue, keepLvalue);
960 if (auto ev = ea.isVarExp())
966 if (auto et = ea.isThisExp())
972 if (auto ef = ea.isFuncExp())
982 if (ea.op == TOK.tuple)
984 ea.error("tuple is not a valid template value argument");
987 // Now that we know it is not an alias, we MUST obtain a value
988 uint olderr = global.errors;
989 ea = ea.ctfeInterpret();
990 if (ea.op == TOK.error || olderr != global.errors)
993 /* Use type mangling that matches what it would be for a function parameter
995 visitWithMask(ea.type, 0);
1002 if (sa.isDeclaration() && !sa.isOverDeclaration())
1004 Declaration d = sa.isDeclaration();
1006 if (auto fad = d.isFuncAliasDeclaration())
1007 d = fad.toAliasFunc();
1008 if (d.mangleOverride)
1011 toBuffer(d.mangleOverride, d);
1014 if (const id = externallyMangledIdentifier(d))
1020 if (!d.type || !d.type.deco)
1022 ti.error("forward reference of %s `%s`", d.kind(), d.toChars());
1031 assert(i + 1 == args.dim); // must be last one
1041 override void visit(Dsymbol s)
1045 printf("Dsymbol.mangle() '%s'", s.toChars());
1047 printf(" parent = %s %s", s.parent.kind(), s.parent.toChars());
1052 mangleIdentifier(s.ident, s);
1054 toBuffer(s.toString(), s);
1055 //printf("Dsymbol.mangle() %s = %s\n", s.toChars(), id);
1058 ////////////////////////////////////////////////////////////////////////////
1059 override void visit(Expression e)
1061 e.error("expression `%s` is not a valid template value argument", e.toChars());
1064 override void visit(IntegerExp e)
1066 const v = e.toInteger();
1067 if (cast(sinteger_t)v < 0)
1079 override void visit(RealExp e)
1082 realToMangleBuffer(e.value);
1085 void realToMangleBuffer(real_t value)
1087 /* Rely on %A to get portable mangling.
1088 * Must munge result to get only identifier characters.
1090 * Possible values from %A => mangled result
1094 * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79
1097 if (CTFloat.isNaN(value))
1099 buf.writestring("NAN"); // no -NAN bugs
1103 if (value < CTFloat.zero)
1109 if (CTFloat.isInfinity(value))
1111 buf.writestring("INF");
1115 char[36] buffer = void;
1116 // 'A' format yields [-]0xh.hhhhp+-d
1117 const n = CTFloat.sprint(buffer.ptr, 'A', value);
1118 assert(n < buffer.length);
1119 foreach (const c; buffer[2 .. n])
1138 override void visit(ComplexExp e)
1141 realToMangleBuffer(e.toReal());
1142 buf.writeByte('c'); // separate the two
1143 realToMangleBuffer(e.toImaginary());
1146 override void visit(NullExp e)
1151 override void visit(StringExp e)
1156 /* Write string in UTF-8 format
1167 const slice = e.peekWstring();
1168 for (size_t u = 0; u < e.len;)
1171 if (const s = utf_decodeWchar(slice, u, c))
1172 e.error("%.*s", cast(int)s.length, s.ptr);
1182 const slice = e.peekDstring();
1185 if (!utf_isValidDchar(c))
1186 e.error("invalid UCS-32 char \\U%08x", c);
1197 buf.reserve(1 + 11 + 2 * q.length);
1199 buf.print(q.length);
1200 buf.writeByte('_'); // nbytes <= 11
1201 auto slice = buf.allocate(2 * q.length);
1204 char hi = (c >> 4) & 0xF;
1205 slice[i * 2] = cast(char)(hi < 10 ? hi + '0' : hi - 10 + 'a');
1207 slice[i * 2 + 1] = cast(char)(lo < 10 ? lo + '0' : lo - 10 + 'a');
1211 override void visit(ArrayLiteralExp e)
1213 const dim = e.elements ? e.elements.dim : 0;
1216 foreach (i; 0 .. dim)
1222 override void visit(AssocArrayLiteralExp e)
1224 const dim = e.keys.dim;
1227 foreach (i; 0 .. dim)
1229 (*e.keys)[i].accept(this);
1230 (*e.values)[i].accept(this);
1234 override void visit(StructLiteralExp e)
1236 const dim = e.elements ? e.elements.dim : 0;
1239 foreach (i; 0 .. dim)
1241 Expression ex = (*e.elements)[i];
1245 buf.writeByte('v'); // 'v' for void
1249 override void visit(FuncExp e)
1258 ////////////////////////////////////////////////////////////////////////////
1260 override void visit(Parameter p)
1262 if (p.storageClass & STC.scope_ && !(p.storageClass & STC.scopeinferred))
1265 // 'return inout ref' is the same as 'inout ref'
1266 if ((p.storageClass & (STC.return_ | STC.wild)) == STC.return_ &&
1267 !(p.storageClass & STC.returninferred))
1268 buf.writestring("Nk");
1269 switch (p.storageClass & (STC.IOR | STC.lazy_))
1276 case STC.in_ | STC.ref_:
1277 buf.writestring("IK");
1291 printf("storageClass = x%llx\n", p.storageClass & (STC.IOR | STC.lazy_));
1295 visitWithMask(p.type, (p.storageClass & STC.in_) ? MODFlags.const_ : 0);