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-2023 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
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.
20 * https://issues.dlang.org/query.cgi
21 * enter `C++, mangling` as the keywords.
26 import core
.stdc
.stdio
;
28 import dmd
.arraytypes
;
31 import dmd
.declaration
;
35 import dmd
.expression
;
39 import dmd
.identifier
;
43 import dmd
.root
.array
;
44 import dmd
.common
.outbuffer
;
45 import dmd
.root
.rootobject
;
46 import dmd
.root
.string
;
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;
58 operators
= [Id
._cast
, Id
.assign
, Id
.eq
, Id
.index
, Id
.call, Id
.opUnary
, Id
.opBinary
, Id
.opOpAssign
];
59 foreach (i
, op
; operators
)
62 return cast(CppOperator
)i
;
64 return CppOperator
.Unknown
;
68 extern(C
++) const(char)* toCppMangleItanium(Dsymbol s
)
70 //printf("toCppMangleItanium(%s)\n", s.toChars());
72 scope CppMangleVisitor v
= new CppMangleVisitor(&buf
, s
.loc
);
74 return buf
.extractChars();
78 extern(C
++) const(char)* cppTypeInfoMangleItanium(Dsymbol s
)
80 //printf("cppTypeInfoMangle(%s)\n", s.toChars());
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();
89 extern(C
++) const(char)* cppThunkMangleItanium(FuncDeclaration fd
, int offset
)
91 //printf("cppThunkMangleItanium(%s)\n", fd.toChars());
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.
104 * true if sym is an aggregate destructor
106 bool isAggregateDtor(const Dsymbol sym
)
108 const dtor
= sym
.isDtorDeclaration();
111 const ad
= dtor
.isMember();
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
121 /// Function declaration we're mangling
123 /// Current type / expression being processed (semantically analyzed)
126 @disable ref Context
opAssign(ref Context other
);
127 @disable ref Context
opAssign(Context other
);
130 * Helper function to track `res`
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.
138 * The previous state of this `Context` object
140 private Context
push(lazy RootObject next
) @safe
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
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
171 * buf = `OutBuffer` to write the mangling to
172 * loc = `Loc` of the symbol being mangled
174 this(OutBuffer
* buf
, Loc loc
) scope
181 * Entry point. Append mangling to buf[]
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())
202 * Mangle the return type of a function
204 * This is called on a templated function type.
205 * Context is set to the `FuncDeclaration`.
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
220 rt
= rt
.referenceTo();
221 auto prev
= this.context
.push(tf
.nextOf());
222 scope (exit
) this.context
.pop(prev
);
227 * Write a seq-id from an index number, excluding the terminating '_'
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.
235 * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id
237 private void writeSequenceFromIndex(size_t idx
) @safe
241 void write_seq_id(size_t i
)
245 write_seq_id(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...
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.
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);
280 //printf("\tmatch\n");
281 /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ...
286 writeSequenceFromIndex(i
);
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.
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
)
310 component
.visitObject(v
);
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");
327 * Write an identifier preceded by its length
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.
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
);
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.
362 b2
.print(tag
.length
);
364 this.buf
.insert(off
, b2
[]);
369 /************************
370 * Determine if symbol is indeed the global ::std namespace.
372 * s = symbol to check
374 * true if it is ::std
376 static bool isStd(Dsymbol s
)
381 if (auto cnd
= s
.isCPPNamespaceDeclaration())
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
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.
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())
416 // Fundamental arithmetic types:
417 // 1. integral types: bool, char, int, ...
418 // 2. floating point types: float, double, real
420 // 4. null pointer: std::nullptr_t (since C++11)
421 if (t
.ty
== Tvoid || t
.ty
== Tbool
)
423 else if (t
.ty
== Tnull
&& global
.params
.cplusplus
>= CppStdRevision
.cpp11
)
426 return t
.isTypeBasic() && (t
.isintegral() || t
.isreal());
429 /******************************
430 * Write the mangled representation of a template argument.
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();
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();
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
];
458 scope (exit
) this.context
.pop(prev
);
460 if (tp
.isTemplateTypeParameter())
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
);
474 tv
.valType
.accept(this);
475 auto val
= e
.toUInteger();
476 if (!tv
.valType
.isunsigned() && cast(sinteger_t
)val
< 0)
486 .error(ti
.loc
, "%s `%s` internal compiler error: C++ `%s` template value parameter is not supported", ti
.kind
, ti
.toPrettyChars
, tv
.valType
.toChars());
490 else if (tp
.isTemplateAliasParameter())
492 // Passing a function as alias parameter is the same as passing
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();
509 mangle_variable(vd
, true);
512 else if (d
&& d
.isTemplateDeclaration() && d
.isTemplateDeclaration().onemember
)
516 cpp_mangle_name(d
, false);
521 .error(ti
.loc
, "%s `%s` internal compiler error: C++ `%s` template alias parameter is not supported", ti
.kind
, ti
.toPrettyChars
, o
.toChars());
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());
536 /******************************
537 * Write the mangled representation of the template arguments.
539 * ti = the template instance
540 * firstArg = index of the first template argument to mangle
541 * (used for operator overloading)
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
552 foreach (i
; firstArg
.. ti
.tiargs
.length
)
554 TemplateDeclaration td
= ti
.tempdecl
.isTemplateDeclaration();
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
]);
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());
593 * Write the symbol `p` if not null, then execute the delegate
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);
606 buf
.writestring("E");
613 * Write the name of `s` to the buffer
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)
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();
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);
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.
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
))
660 if (!haveNE
&& isNested
)
664 else if (this.writeStdSubstitution(ti
, needsTa
))
666 this.abiTags
.writeSymbol(ti
, this);
672 auto ag
= ti
.aliasdecl ? ti
.aliasdecl
.isAggregateDeclaration() : null;
673 if (ag
&& ag
.pMangleOverride
)
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();
682 this.abiTags
.writeSymbol(to
.tempdecl
, this);
691 this.writeIdentifier(ti
.tempdecl
.toAlias().ident
);
693 this.abiTags
.writeSymbol(ti
.tempdecl
, this);
700 * See if s is actually an instance of a template
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();
711 if (TemplateInstance ti
= p
.isTemplateInstance())
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
;
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.
732 * s = symbol that may have a qualifier
733 * s is rewritten to be TemplateInstance if s is one
735 * qualifier, null if none
737 static Dsymbol
getQualifier(Dsymbol s
)
739 Dsymbol p
= s
.toParent();
740 return (p
&& !p
.isModule()) ? p
: null;
744 static bool isChar(RootObject 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
)
766 if (!t ||
!t
.isTypeStruct())
768 Dsymbol s
= t
.toDsymbol(null);
769 if (s
.ident
!= ident
)
771 Dsymbol p
= s
.toParent();
774 TemplateInstance ti
= p
.isTemplateInstance();
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]);
783 * Detect template args <char, ::std::char_traits<char>>
784 * and write st 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
);
801 void prefix_name(Dsymbol s
)
803 //printf("prefix_name(%s)\n", s.toChars());
807 return buf
.writestring("St");
809 auto si
= getInstance(s
);
810 Dsymbol p
= getQualifier(si
);
816 auto ti
= si
.isTemplateInstance();
817 if (this.writeStdSubstitution(ti
, needsTa
))
819 this.abiTags
.writeSymbol(ti
, this);
827 buf
.writestring("St");
832 source_name(si
, true);
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
842 * Write common substitution for standard types, such as std::allocator
844 * This function assumes that the symbol `ti` is in the namespace `std`.
847 * ti = Template instance to consider
848 * needsTa = If this function returns `true`, this value indicates
849 * if additional template argument mangling is needed
852 * `true` if a special std symbol was found
854 bool writeStdSubstitution(TemplateInstance ti
, out bool needsTa
)
858 if (!isStd(this.getTiNamespace(ti
)) && !isStd(getQualifier(ti
)))
861 if (ti
.name
== Id
.allocator
)
863 buf
.writestring("Sa");
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");
879 buf
.writestring("Sb"); // ::std::basic_string
884 // ::std::basic_istream<char, ::std::char_traits<char>>
885 if (ti
.name
== Id
.basic_istream
&&
886 char_std_char_traits_char(ti
, "Si"))
889 // ::std::basic_ostream<char, ::std::char_traits<char>>
890 if (ti
.name
== Id
.basic_ostream
&&
891 char_std_char_traits_char(ti
, "So"))
894 // ::std::basic_iostream<char, ::std::char_traits<char>>
895 if (ti
.name
== Id
.basic_iostream
&&
896 char_std_char_traits_char(ti
, "Sd"))
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();
907 bool write_prefix
= true;
908 if (p
&& p
.isTemplateInstance())
911 if (find(p
.isTemplateInstance().tempdecl
) >= 0)
912 write_prefix
= false;
915 if (!p || p
.isModule())
917 source_name(se
, false);
922 if (!isStd(p
) || qualified
)
928 buf
.writestring("St");
932 source_name(se
, true);
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
943 TemplateInstance ti
= se
.isTemplateInstance();
944 if (s
.ident
== Id
.allocator
)
946 buf
.writestring("Sa"); // "Sa" is short for ::std::allocator
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");
960 buf
.writestring("Sb"); // ::std::basic_string
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"))
971 else if (s
.ident
== Id
.basic_ostream
)
973 if (char_std_char_traits_char(ti
, "So"))
976 else if (s
.ident
== Id
.basic_iostream
)
978 if (char_std_char_traits_char(ti
, "Sd"))
981 buf
.writestring("St");
982 source_name(se
, true);
988 * Write CV-qualifiers to the buffer
990 * CV-qualifiers are 'r': restrict (unused in D), 'V': volatile, 'K': const
993 * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.CV-qualifiers
995 void CV_qualifiers(const Type t
)
1002 * Mangles a variable
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
);
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");
1022 source_name(d
, true);
1027 buf
.writestring("_Z");
1028 source_name(d
, false);
1032 if (auto varTags
= ABITagContainer
.forSymbol(d
))
1034 buf
.writestring("_Z");
1035 source_name(d
, false);
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
);
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>
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();
1076 this.mangleTemplatedFunction(d
, tf
, ftd
, ti
);
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");
1098 source_name(d
, true);
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
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)
1137 if (!substitute(ns
))
1138 buf
.writestring("St");
1141 else if (dg
!is null)
1144 buf
.writestring("N");
1145 if (!substitute(ns
))
1147 this.writeNamespace(ns
.cppnamespace
, null);
1148 this.writeIdentifier(ns
.ident
);
1153 buf
.writestring("E");
1155 else if (!substitute(ns
))
1157 this.writeNamespace(ns
.cppnamespace
, null);
1158 this.writeIdentifier(ns
.ident
);
1164 * Mangles a function template to C++
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));
1187 source_name(ti
, false);
1188 this.mangleReturnType(preSemantic
);
1189 this.mangleFunctionParameters(ParameterList(preSemantic
.parameterList
.parameters
, tf
.parameterList
.varargs
));
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
);
1202 else if (d
.isAggregateDtor())
1204 buf
.writestring("D1");
1205 mangleFunctionParameters(tf
.parameterList
);
1209 int firstTemplateArg
= 0;
1210 bool appendReturnType
= true;
1211 bool isConvertFunc
= false;
1214 // test for special symbols
1215 CppOperator whichOp
= isCppOperator(ti
.name
);
1216 final switch (whichOp
)
1218 case CppOperator
.Unknown
:
1220 case CppOperator
.Cast
:
1222 firstTemplateArg
= 1;
1223 isConvertFunc
= true;
1224 appendReturnType
= false;
1226 case CppOperator
.Assign
:
1229 case CppOperator
.Eq
:
1232 case CppOperator
.Index
:
1235 case CppOperator
.Call
:
1238 case CppOperator
.Unary
:
1239 case CppOperator
.Binary
:
1240 case CppOperator
.OpAssign
:
1241 TemplateDeclaration td
= ti
.tempdecl
.isTemplateDeclaration();
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();
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
;
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
;
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
;
1299 firstTemplateArg
= 1;
1304 if (symName
.length
== 0)
1305 source_name(ti
, true);
1308 buf
.writestring(symName
);
1310 template_arg(ti
, 0);
1311 appendReturnType
= template_args(ti
, firstTemplateArg
) && appendReturnType
;
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`
1325 * parameters = Array of `Parameter` to mangle
1326 * varargs = if != 0, this function has varargs parameters
1328 void mangleFunctionParameters(ParameterList parameterList
)
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
);
1344 else if (Type cpptype
= target
.cpp
.parameterType(t
))
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",
1353 auto prev
= this.context
.push({
1355 if (isDsymbol(this.context
.res
))
1356 tf
= this.context
.res
.asFuncDecl().type
.isTypeFunction();
1358 tf
= this.context
.res
.asType().isTypeFunction();
1360 return (*tf
.parameterList
.parameters
)[n
].type
;
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
);
1371 if (parameterList
.varargs
== VarArg
.variadic
)
1373 else if (!numparams
)
1374 buf
.writeByte('v'); // encode (void) parameters
1377 /****** The rest is type mangling ************/
1382 if (t
.isImmutable())
1384 else if (t
.isShared())
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 /****************************
1394 * treating it as a Head followed by a Tail.
1396 * t = Head of a type
1398 void headOfType(Type t
)
1400 if (auto tc
= t
.isTypeClass())
1402 mangleTypeClass(tc
, true);
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);
1414 * Write out 1 or 2 character basic type mangling.
1415 * Handle const and substitutions.
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())
1439 * Write structs and enums.
1441 * t = TypeStruct or TypeEnum
1443 void doSymbol(Type t
)
1449 // Handle any target-specific struct types.
1450 if (auto tm
= target
.cpp
.typeMangle(t
))
1452 buf
.writestring(tm
);
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
1467 cpp_mangle_name(s
, false);
1475 /************************
1476 * Mangle a class type.
1477 * If it's the head, treat the initial pointer as a value type.
1480 * head = true for head of a type
1482 void mangleTypeClass(TypeClass t
, bool head
)
1484 if (t
.isImmutable() || t
.isShared())
1487 /* Mangle as a <pointer to><struct>
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
1510 if (!substitute(t
.sym
))
1512 cpp_mangle_name(t
.sym
, false);
1515 append(null); // C++ would have an extra type here
1520 * Mangle the prefix of a nested (e.g. member) function
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
1534 /* <prefix> ::= <prefix> <unqualified-name>
1535 * ::= <template-prefix> <template-args>
1536 * ::= <template-param>
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...).
1548 * t = Parameter type
1549 * params = Template parameters of the function
1551 private void handleParamPack(Type t
, TemplateParameters
* params
)
1553 if (t
.isTypeReference())
1555 auto ti
= t
.isTypeIdentifier();
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.
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())
1577 writeSequenceFromIndex(idx
);
1579 if (param
.isTemplateValueParameter())
1584 * Given an array of template parameters and an identifier,
1585 * returns the index of the identifier in that array.
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)
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
)
1603 return params
.length
;
1607 * Given a template instance `t`, write its qualified name
1608 * without the template parameter list
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
);
1621 this.writeIdentifier(t
.name
);
1624 auto sym1
= type
.toDsymbol(null);
1627 this.writeIdentifier(t
.name
);
1630 // Get the template instance
1631 auto sym
= getQualifier(sym1
);
1632 auto sym2
= getQualifier(sym
);
1633 if (sym2
&& isStd(sym2
)) // Nspace path
1636 assert(sym
.isTemplateInstance());
1637 if (this.writeStdSubstitution(sym
.isTemplateInstance(), unused
))
1639 // std names don't require `N..E`
1640 buf
.writestring("St");
1641 this.writeIdentifier(t
.name
);
1647 buf
.writestring("N");
1648 if (!this.substitute(sym2
))
1651 this.writeNamespace(
1652 sym1
.cppnamespace
, () {
1653 this.writeIdentifier(t
.name
);
1658 buf
.writestring("E");
1663 alias visit
= Visitor
.visit
;
1665 override void visit(TypeNull t
)
1667 if (t
.isImmutable() || t
.isShared())
1670 writeBasicType(t
, 'D', 'n');
1673 override void visit(TypeNoreturn t
)
1675 if (t
.isImmutable() || t
.isShared())
1678 writeBasicType(t
, 0, 'v'); // mangle like `void`
1681 override void visit(TypeBasic t
)
1683 if (t
.isImmutable() || t
.isShared())
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())
1698 buf
.writestring(tm
);
1715 * x long long, __int64
1716 * y unsigned long long, __int64
1718 * o unsigned __int128
1721 * e long double, __float80
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
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.
1743 if (t
.isimaginary())
1744 buf
.writeByte('G'); // 'G' means imaginary
1746 buf
.writeByte('C'); // 'C' means complex
1752 return Type
.tfloat32
.accept(this);
1755 return Type
.tfloat64
.accept(this);
1758 return Type
.tfloat80
.accept(this);
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;
1777 c
= target
.c
.longsize
== 8 ?
'l' : 'x';
1780 c
= target
.c
.longsize
== 8 ?
'm' : 'y';
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
1794 writeBasicType(t
, p
, c
);
1797 override void visit(TypeVector t
)
1799 if (t
.isImmutable() || t
.isShared())
1807 // Handle any target-specific vector types.
1808 if (auto tm
= target
.cpp
.typeMangle(t
))
1810 buf
.writestring(tm
);
1814 assert(t
.basetype
&& t
.basetype
.ty
== Tsarray
);
1815 auto tsa
= t
.basetype
.isTypeSArray();
1817 buf
.writestring("Dv"); // -- Gnu ABI v.4
1818 buf
.print(tsa
.dim
.toInteger());
1820 t
.basetype
.nextOf().accept(this);
1824 override void visit(TypeSArray t
)
1826 if (t
.isImmutable() || t
.isShared())
1833 buf
.print(t
.dim ? t
.dim
.toInteger() : 0);
1835 t
.next
.accept(this);
1838 override void visit(TypePointer t
)
1840 if (t
.isImmutable() || t
.isShared())
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
1851 auto prev
= this.context
.push(this.context
.res
.asType().nextOf());
1852 scope (exit
) this.context
.pop(prev
);
1853 t
.next
.accept(this);
1857 override void visit(TypeReference t
)
1862 CV_qualifiers(t
.nextOf());
1863 headOfType(t
.nextOf());
1864 if (t
.nextOf().isConst())
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
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
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.
1895 if (t
.linkage
== LINK
.c
)
1899 tn
= tn
.referenceTo();
1901 mangleFunctionParameters(t
.parameterList
);
1906 override void visit(TypeStruct t
)
1908 if (t
.isImmutable() || t
.isShared())
1910 //printf("TypeStruct %s\n", t.toChars());
1914 override void visit(TypeEnum t
)
1916 if (t
.isImmutable() || t
.isShared())
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);
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
))
1983 // If substitution failed, write `TX_` where `X` is the index
1984 this.writeTemplateArgIndex(idx
, param
);
1986 // Write the ABI tags, if any
1987 if (auto sym
= this.context
.res
.isDsymbol())
1988 this.abiTags
.writeSymbol(sym
, this);
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
2014 // When visiting the arguments, the context will be set to the
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);
2042 assert(0, arg
.toString());
2048 // `name` is used, not `ident`
2049 assert(t
.name
!is null);
2050 assert(t
.tiargs
!is null);
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
))
2063 this.writeTemplateArgIndex(idx
, param
);
2067 else if (this.writeStdSubstitution(t
, needsTa
))
2072 else if (!this.substitute(t
))
2073 this.writeQualified(t
, &writeArgs
);
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
))
2091 buf
.writestring("St");
2094 this.writeIdentifier(t
.ident
);
2099 override void visit(Type t
)
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_
)
2115 if (Type ta
= isType(o
))
2117 else if (Expression ea
= isExpression(o
))
2119 else if (Dsymbol sa
= isDsymbol(o
))
2121 else if (TemplateParameter t
= isTemplateParameter(o
))
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
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
))
2137 // When called with context.res as argument, it can be `FuncDeclaration`
2138 if (auto fd
= o
.asFuncDecl())
2143 /// Helper function to safely get a `FuncDeclaration` out of a `RootObject`
2144 private FuncDeclaration
asFuncDecl(RootObject o
) @safe
2146 Dsymbol d
= isDsymbol(o
);
2148 auto fd
= d
.isFuncDeclaration();
2149 assert(fd
!is null);
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
;
2161 private CPPNamespaceDeclaration namespace2
;
2164 private TypePointer tpointer
;
2167 private TypeReference tref
;
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
;
2192 auto t
= cast(Type
)base
;
2193 if (auto tp
= t
.isTypePointer())
2195 else if (auto tr
= t
.isTypeReference())
2197 else if (auto ti
= t
.isTypeIdentifier())
2203 // Note: ABI tags are also handled here (they are TupleExp of StringExp)
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
;
2219 public override void visit(Expression o
)
2221 this.result
= this.object
&& this.object
== o
;
2225 public void visit(Tuple o
)
2227 this.result
= this.object
&& this.object
== o
;
2231 public override void visit(Type o
)
2233 this.result
= this.object
&& this.object
== o
;
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
)
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
;
2268 public override void visit(TypePointer o
)
2272 if (this.tpointer
== o
)
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
;
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.
2303 * https://issues.dlang.org/show_bug.cgi?id=18922
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
);
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)
2332 private bool isNamespaceEqual (Nspace a
, CPPNamespaceDeclaration b
)
2334 return isNamespaceEqual(b
, a
);
2338 private bool isNamespaceEqual (CPPNamespaceDeclaration a
, Nspace b
, size_t idx
= 0)
2340 if ((a
is null) != (b
is null))
2342 if (!a
.ident
.equals(b
.ident
))
2345 // We need to see if there's more ident enclosing
2346 if (auto pb
= b
.toParent().isNspace())
2347 return isNamespaceEqual(a
.cppnamespace
, pb
);
2349 return a
.cppnamespace
is null;
2353 /// Whether two `CPPNamespaceDeclaration` are equals
2354 private bool isNamespaceEqual (CPPNamespaceDeclaration a
, CPPNamespaceDeclaration b
) @safe
2356 if (a
is null || b
is null)
2359 if ((a
.cppnamespace
is null) != (b
.cppnamespace
is null))
2361 if (a
.ident
!= b
.ident
)
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.
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 {};
2384 * // Functions definitions:
2385 * Struct3 func1 (Struct1);
2386 * Struct3 func2 (Struct2);
2387 * Struct3 func3 (Struct2, Struct1);
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
)
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())
2411 if (!s
.userAttribDecl ||
!s
.userAttribDecl
.atts
)
2414 foreach (exp
; *s
.userAttribDecl
.atts
)
2416 if (UserAttributeDeclaration
.isGNUABITag(exp
))
2417 return (*exp
.isStructLiteralExp().elements
)[0]
2418 .isArrayLiteralExp();
2423 void writeSymbol(Dsymbol s
, CppMangleVisitor self
)
2425 auto tale
= forSymbol(s
);
2427 if (self
.substitute(tale
))
2429 this.write(*self
.buf
, tale
);
2433 * Write an ArrayLiteralExp (expected to be an ABI tag) to the buffer
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
);
2450 foreach (exp
; *ale
.elements
)
2452 auto elem
= exp
.toStringExp();
2453 auto idx
= closestIndex(this.written
[], elem
, match
);
2457 this.written
.insert(idx
, elem
);
2459 else if (!skipKnown
)
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.
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`
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
;
2489 int res
= dstrcmp(exp
.peekString(), slice
[$ / 2].peekString());
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)];
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");
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
);
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
);
2542 * Visits the return type of a function and writes leftover ABI tags
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
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
);
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
);
2582 (*this.toWrite
).insert(idx
, se
);
2587 public override void visit(Type o
)
2589 if (auto sym
= o
.toDsymbol(null))
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
);