| /** | |
| * Do mangling for C++ linkage. | |
| * | |
| * This is the POSIX side of the implementation. | |
| * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`. | |
| * | |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * Authors: Walter Bright, https://www.digitalmars.com | |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d, _cppmangle.d) | |
| * Documentation: https://dlang.org/phobos/dmd_cppmangle.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmangle.d | |
| * | |
| * References: | |
| * Follows Itanium C++ ABI 1.86 section 5.1 | |
| * http://refspecs.linux-foundation.org/cxxabi-1.86.html#mangling | |
| * which is where the grammar comments come from. | |
| * | |
| * Bugs: | |
| * https://issues.dlang.org/query.cgi | |
| * enter `C++, mangling` as the keywords. | |
| */ | |
| module dmd.cppmangle; | |
| import core.stdc.string; | |
| import core.stdc.stdio; | |
| import dmd.arraytypes; | |
| import dmd.astenums; | |
| import dmd.attrib; | |
| import dmd.declaration; | |
| import dmd.dsymbol; | |
| import dmd.dtemplate; | |
| import dmd.errors; | |
| import dmd.expression; | |
| import dmd.func; | |
| import dmd.globals; | |
| import dmd.id; | |
| import dmd.identifier; | |
| import dmd.mtype; | |
| import dmd.nspace; | |
| import dmd.root.array; | |
| import dmd.common.outbuffer; | |
| import dmd.root.rootobject; | |
| import dmd.root.string; | |
| import dmd.target; | |
| import dmd.tokens; | |
| import dmd.typesem; | |
| import dmd.visitor; | |
| // helper to check if an identifier is a C++ operator | |
| enum CppOperator { Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign, Unknown } | |
| package CppOperator isCppOperator(Identifier id) | |
| { | |
| __gshared const(Identifier)[] operators = null; | |
| if (!operators) | |
| operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign]; | |
| foreach (i, op; operators) | |
| { | |
| if (op == id) | |
| return cast(CppOperator)i; | |
| } | |
| return CppOperator.Unknown; | |
| } | |
| /// | |
| extern(C++) const(char)* toCppMangleItanium(Dsymbol s) | |
| { | |
| //printf("toCppMangleItanium(%s)\n", s.toChars()); | |
| OutBuffer buf; | |
| scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc); | |
| v.mangleOf(s); | |
| return buf.extractChars(); | |
| } | |
| /// | |
| extern(C++) const(char)* cppTypeInfoMangleItanium(Dsymbol s) | |
| { | |
| //printf("cppTypeInfoMangle(%s)\n", s.toChars()); | |
| OutBuffer buf; | |
| buf.writestring("_ZTI"); // "TI" means typeinfo structure | |
| scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc); | |
| v.cpp_mangle_name(s, false); | |
| return buf.extractChars(); | |
| } | |
| /// | |
| extern(C++) const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset) | |
| { | |
| //printf("cppThunkMangleItanium(%s)\n", fd.toChars()); | |
| OutBuffer buf; | |
| buf.printf("_ZThn%u_", offset); // "Th" means thunk, "n%u" is the call offset | |
| scope CppMangleVisitor v = new CppMangleVisitor(&buf, fd.loc); | |
| v.mangle_function_encoding(fd); | |
| return buf.extractChars(); | |
| } | |
| /****************************** | |
| * Determine if sym is a full aggregate destructor. | |
| * Params: | |
| * sym = Dsymbol | |
| * Returns: | |
| * true if sym is an aggregate destructor | |
| */ | |
| bool isAggregateDtor(const Dsymbol sym) | |
| { | |
| const dtor = sym.isDtorDeclaration(); | |
| if (!dtor) | |
| return false; | |
| const ad = dtor.isMember(); | |
| assert(ad); | |
| return dtor == ad.aggrDtor; | |
| } | |
| /// Context used when processing pre-semantic AST | |
| private struct Context | |
| { | |
| /// Template instance of the function being mangled | |
| TemplateInstance ti; | |
| /// Function declaration we're mangling | |
| FuncDeclaration fd; | |
| /// Current type / expression being processed (semantically analyzed) | |
| RootObject res; | |
| @disable ref Context opAssign(ref Context other); | |
| @disable ref Context opAssign(Context other); | |
| /** | |
| * Helper function to track `res` | |
| * | |
| * Params: | |
| * next = Value to set `this.res` to. | |
| * If `this.res` is `null`, the expression is not evalutated. | |
| * This allow this code to be used even when no context is needed. | |
| * | |
| * Returns: | |
| * The previous state of this `Context` object | |
| */ | |
| private Context push(lazy RootObject next) | |
| { | |
| auto r = this.res; | |
| if (r !is null) | |
| this.res = next; | |
| return Context(this.ti, this.fd, r); | |
| } | |
| /** | |
| * Reset the context to a previous one, making any adjustment necessary | |
| */ | |
| private void pop(ref Context prev) | |
| { | |
| this.res = prev.res; | |
| } | |
| } | |
| private final class CppMangleVisitor : Visitor | |
| { | |
| /// Context used when processing pre-semantic AST | |
| private Context context; | |
| ABITagContainer abiTags; /// Container for already-written ABI tags | |
| Objects components; /// array of components available for substitution | |
| OutBuffer* buf; /// append the mangling to buf[] | |
| Loc loc; /// location for use in error messages | |
| /** | |
| * Constructor | |
| * | |
| * Params: | |
| * buf = `OutBuffer` to write the mangling to | |
| * loc = `Loc` of the symbol being mangled | |
| */ | |
| this(OutBuffer* buf, Loc loc) | |
| { | |
| this.buf = buf; | |
| this.loc = loc; | |
| } | |
| /***** | |
| * Entry point. Append mangling to buf[] | |
| * Params: | |
| * s = symbol to mangle | |
| */ | |
| void mangleOf(Dsymbol s) | |
| { | |
| if (VarDeclaration vd = s.isVarDeclaration()) | |
| { | |
| mangle_variable(vd, vd.cppnamespace !is null); | |
| } | |
| else if (FuncDeclaration fd = s.isFuncDeclaration()) | |
| { | |
| mangle_function(fd); | |
| } | |
| else | |
| { | |
| assert(0); | |
| } | |
| } | |
| /** | |
| * Mangle the return type of a function | |
| * | |
| * This is called on a templated function type. | |
| * Context is set to the `FuncDeclaration`. | |
| * | |
| * Params: | |
| * preSemantic = the `FuncDeclaration`'s `originalType` | |
| */ | |
| void mangleReturnType(TypeFunction preSemantic) | |
| { | |
| auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type; | |
| Type rt = preSemantic.nextOf(); | |
| if (tf.isref) | |
| rt = rt.referenceTo(); | |
| auto prev = this.context.push(tf.nextOf()); | |
| scope (exit) this.context.pop(prev); | |
| this.headOfType(rt); | |
| } | |
| /** | |
| * Write a seq-id from an index number, excluding the terminating '_' | |
| * | |
| * Params: | |
| * idx = the index in a substitution list. | |
| * Note that index 0 has no value, and `S0_` would be the | |
| * substitution at index 1 in the list. | |
| * | |
| * See-Also: | |
| * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id | |
| */ | |
| private void writeSequenceFromIndex(size_t idx) | |
| { | |
| if (idx) | |
| { | |
| void write_seq_id(size_t i) | |
| { | |
| if (i >= 36) | |
| { | |
| write_seq_id(i / 36); | |
| i %= 36; | |
| } | |
| i += (i < 10) ? '0' : 'A' - 10; | |
| buf.writeByte(cast(char)i); | |
| } | |
| write_seq_id(idx - 1); | |
| } | |
| } | |
| /** | |
| * Attempt to perform substitution on `p` | |
| * | |
| * If `p` already appeared in the mangling, it is stored as | |
| * a 'part', and short references in the form of `SX_` can be used. | |
| * Note that `p` can be anything: template declaration, struct declaration, | |
| * class declaration, namespace... | |
| * | |
| * Params: | |
| * p = The object to attempt to substitute | |
| * nested = Whether or not `p` is to be considered nested. | |
| * When `true`, `N` will be prepended before the substitution. | |
| * | |
| * Returns: | |
| * Whether `p` already appeared in the mangling, | |
| * and substitution has been written to `this.buf`. | |
| */ | |
| bool substitute(RootObject p, bool nested = false) | |
| { | |
| //printf("substitute %s\n", p ? p.toChars() : null); | |
| auto i = find(p); | |
| if (i < 0) | |
| return false; | |
| //printf("\tmatch\n"); | |
| /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ... | |
| */ | |
| if (nested) | |
| buf.writeByte('N'); | |
| buf.writeByte('S'); | |
| writeSequenceFromIndex(i); | |
| buf.writeByte('_'); | |
| return true; | |
| } | |
| /****** | |
| * See if `p` exists in components[] | |
| * | |
| * Note that components can contain `null` entries, | |
| * as the index used in mangling is based on the index in the array. | |
| * | |
| * If called with an object whose dynamic type is `Nspace`, | |
| * calls the `find(Nspace)` overload. | |
| * | |
| * Returns: | |
| * index if found, -1 if not | |
| */ | |
| int find(RootObject p) | |
| { | |
| //printf("find %p %d %s\n", p, p.dyncast(), p ? p.toChars() : null); | |
| scope v = new ComponentVisitor(p); | |
| foreach (i, component; components) | |
| { | |
| if (component) | |
| component.visitObject(v); | |
| if (v.result) | |
| return cast(int)i; | |
| } | |
| return -1; | |
| } | |
| /********************* | |
| * Append p to components[] | |
| */ | |
| void append(RootObject p) | |
| { | |
| //printf("append %p %d %s\n", p, p.dyncast(), p ? p.toChars() : "null"); | |
| components.push(p); | |
| } | |
| /** | |
| * Write an identifier preceded by its length | |
| * | |
| * Params: | |
| * ident = `Identifier` to write to `this.buf` | |
| */ | |
| void writeIdentifier(const ref Identifier ident) | |
| { | |
| const name = ident.toString(); | |
| this.buf.print(name.length); | |
| this.buf.writestring(name); | |
| } | |
| /** | |
| * Insert the leftover ABI tags to the buffer | |
| * | |
| * This inset ABI tags that hasn't already been written | |
| * after the mangled name of the function. | |
| * For more details, see the `abiTags` variable. | |
| * | |
| * Params: | |
| * off = Offset to insert at | |
| * fd = Type of the function to mangle the return type of | |
| */ | |
| void writeRemainingTags(size_t off, TypeFunction tf) | |
| { | |
| scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written); | |
| tf.next.accept(remainingVisitor); | |
| OutBuffer b2; | |
| foreach (se; remainingVisitor.toWrite) | |
| { | |
| auto tag = se.peekString(); | |
| // We can only insert a slice, and each insert is a memmove, | |
| // so use a temporary buffer to keep it efficient. | |
| b2.reset(); | |
| b2.writestring("B"); | |
| b2.print(tag.length); | |
| b2.writestring(tag); | |
| this.buf.insert(off, b2[]); | |
| off += b2.length; | |
| } | |
| } | |
| /************************ | |
| * Determine if symbol is indeed the global ::std namespace. | |
| * Params: | |
| * s = symbol to check | |
| * Returns: | |
| * true if it is ::std | |
| */ | |
| static bool isStd(Dsymbol s) | |
| { | |
| if (!s) | |
| return false; | |
| if (auto cnd = s.isCPPNamespaceDeclaration()) | |
| return isStd(cnd); | |
| return (s.ident == Id.std && // the right name | |
| s.isNspace() && // g++ disallows global "std" for other than a namespace | |
| !getQualifier(s)); // at global level | |
| } | |
| /// Ditto | |
| static bool isStd(CPPNamespaceDeclaration s) | |
| { | |
| return s && s.cppnamespace is null && s.ident == Id.std; | |
| } | |
| /************************ | |
| * Determine if type is a C++ fundamental type. | |
| * Params: | |
| * t = type to check | |
| * Returns: | |
| * true if it is a fundamental type | |
| */ | |
| static bool isFundamentalType(Type t) | |
| { | |
| // First check the target whether some specific ABI is being followed. | |
| bool isFundamental = void; | |
| if (target.cpp.fundamentalType(t, isFundamental)) | |
| return isFundamental; | |
| if (auto te = t.isTypeEnum()) | |
| { | |
| // Peel off enum type from special types. | |
| if (te.sym.isSpecial()) | |
| t = te.memType(); | |
| } | |
| // Fundamental arithmetic types: | |
| // 1. integral types: bool, char, int, ... | |
| // 2. floating point types: float, double, real | |
| // 3. void | |
| // 4. null pointer: std::nullptr_t (since C++11) | |
| if (t.ty == Tvoid || t.ty == Tbool) | |
| return true; | |
| else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11) | |
| return true; | |
| else | |
| return t.isTypeBasic() && (t.isintegral() || t.isreal()); | |
| } | |
| /****************************** | |
| * Write the mangled representation of a template argument. | |
| * Params: | |
| * ti = the template instance | |
| * arg = the template argument index | |
| */ | |
| void template_arg(TemplateInstance ti, size_t arg) | |
| { | |
| TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); | |
| assert(td); | |
| TemplateParameter tp = (*td.parameters)[arg]; | |
| RootObject o = (*ti.tiargs)[arg]; | |
| auto prev = this.context.push({ | |
| TemplateInstance parentti; | |
| if (this.context.res.dyncast() == DYNCAST.dsymbol) | |
| parentti = this.context.res.asFuncDecl().parent.isTemplateInstance(); | |
| else | |
| parentti = this.context.res.asType().toDsymbol(null).parent.isTemplateInstance(); | |
| return (*parentti.tiargs)[arg]; | |
| }()); | |
| scope (exit) this.context.pop(prev); | |
| if (tp.isTemplateTypeParameter()) | |
| { | |
| Type t = isType(o); | |
| assert(t); | |
| t.accept(this); | |
| } | |
| else if (TemplateValueParameter tv = tp.isTemplateValueParameter()) | |
| { | |
| // <expr-primary> ::= L <type> <value number> E # integer literal | |
| if (tv.valType.isintegral()) | |
| { | |
| Expression e = isExpression(o); | |
| assert(e); | |
| buf.writeByte('L'); | |
| tv.valType.accept(this); | |
| auto val = e.toUInteger(); | |
| if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0) | |
| { | |
| val = -val; | |
| buf.writeByte('n'); | |
| } | |
| buf.print(val); | |
| buf.writeByte('E'); | |
| } | |
| else | |
| { | |
| ti.error("Internal Compiler Error: C++ `%s` template value parameter is not supported", tv.valType.toChars()); | |
| fatal(); | |
| } | |
| } | |
| else if (tp.isTemplateAliasParameter()) | |
| { | |
| // Passing a function as alias parameter is the same as passing | |
| // `&function` | |
| Dsymbol d = isDsymbol(o); | |
| Expression e = isExpression(o); | |
| if (d && d.isFuncDeclaration()) | |
| { | |
| // X .. E => template parameter is an expression | |
| // 'ad' => unary operator ('&') | |
| // L .. E => is a <expr-primary> | |
| buf.writestring("XadL"); | |
| mangle_function(d.isFuncDeclaration()); | |
| buf.writestring("EE"); | |
| } | |
| else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration()) | |
| { | |
| VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration(); | |
| buf.writeByte('L'); | |
| mangle_variable(vd, true); | |
| buf.writeByte('E'); | |
| } | |
| else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember) | |
| { | |
| if (!substitute(d)) | |
| { | |
| cpp_mangle_name(d, false); | |
| } | |
| } | |
| else | |
| { | |
| ti.error("Internal Compiler Error: C++ `%s` template alias parameter is not supported", o.toChars()); | |
| fatal(); | |
| } | |
| } | |
| else if (tp.isTemplateThisParameter()) | |
| { | |
| ti.error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o.toChars()); | |
| fatal(); | |
| } | |
| else | |
| { | |
| assert(0); | |
| } | |
| } | |
| /****************************** | |
| * Write the mangled representation of the template arguments. | |
| * Params: | |
| * ti = the template instance | |
| * firstArg = index of the first template argument to mangle | |
| * (used for operator overloading) | |
| * Returns: | |
| * true if any arguments were written | |
| */ | |
| bool template_args(TemplateInstance ti, int firstArg = 0) | |
| { | |
| /* <template-args> ::= I <template-arg>+ E | |
| */ | |
| if (!ti || ti.tiargs.dim <= firstArg) // could happen if std::basic_string is not a template | |
| return false; | |
| buf.writeByte('I'); | |
| foreach (i; firstArg .. ti.tiargs.dim) | |
| { | |
| TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); | |
| assert(td); | |
| TemplateParameter tp = (*td.parameters)[i]; | |
| /* | |
| * <template-arg> ::= <type> # type or template | |
| * ::= X <expression> E # expression | |
| * ::= <expr-primary> # simple expressions | |
| * ::= J <template-arg>* E # argument pack | |
| * | |
| * Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.template-arg | |
| */ | |
| if (TemplateTupleParameter tt = tp.isTemplateTupleParameter()) | |
| { | |
| buf.writeByte('J'); // argument pack | |
| // mangle the rest of the arguments as types | |
| foreach (j; i .. (*ti.tiargs).dim) | |
| { | |
| Type t = isType((*ti.tiargs)[j]); | |
| assert(t); | |
| t.accept(this); | |
| } | |
| buf.writeByte('E'); | |
| break; | |
| } | |
| template_arg(ti, i); | |
| } | |
| buf.writeByte('E'); | |
| return true; | |
| } | |
| /** | |
| * Write the symbol `p` if not null, then execute the delegate | |
| * | |
| * Params: | |
| * p = Symbol to write | |
| * dg = Delegate to execute | |
| */ | |
| void writeChained(Dsymbol p, scope void delegate() dg) | |
| { | |
| if (p && !p.isModule()) | |
| { | |
| buf.writestring("N"); | |
| source_name(p, true); | |
| dg(); | |
| buf.writestring("E"); | |
| } | |
| else | |
| dg(); | |
| } | |
| /** | |
| * Write the name of `s` to the buffer | |
| * | |
| * Params: | |
| * s = Symbol to write the name of | |
| * haveNE = Whether `N..E` is already part of the mangling | |
| * Because `Nspace` and `CPPNamespaceAttribute` can be | |
| * mixed, this is a mandatory hack. | |
| */ | |
| void source_name(Dsymbol s, bool haveNE = false) | |
| { | |
| version (none) | |
| { | |
| printf("source_name(%s)\n", s.toChars()); | |
| auto sl = this.buf.peekSlice(); | |
| assert(sl.length == 0 || haveNE || s.cppnamespace is null || sl != "_ZN"); | |
| } | |
| auto ti = s.isTemplateInstance(); | |
| if (!ti) | |
| { | |
| auto ag = s.isAggregateDeclaration(); | |
| const ident = (ag && ag.mangleOverride) ? ag.mangleOverride.id : s.ident; | |
| this.writeNamespace(s.cppnamespace, () { | |
| this.writeIdentifier(ident); | |
| this.abiTags.writeSymbol(s, this); | |
| }, | |
| haveNE); | |
| return; | |
| } | |
| bool needsTa = false; | |
| // https://issues.dlang.org/show_bug.cgi?id=20413 | |
| // N..E is not needed when substituting members of the std namespace. | |
| // This is observed in the GCC and Clang implementations. | |
| // The Itanium specification is not clear enough on this specific case. | |
| // References: | |
| // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.name | |
| // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression | |
| Dsymbol q = getQualifier(ti.tempdecl); | |
| Dsymbol ns = ti.tempdecl.cppnamespace; | |
| const inStd = ns && isStd(ns) || q && isStd(q); | |
| const isNested = !inStd && (ns || q); | |
| if (substitute(ti.tempdecl, !haveNE && isNested)) | |
| { | |
| template_args(ti); | |
| if (!haveNE && isNested) | |
| buf.writeByte('E'); | |
| return; | |
| } | |
| else if (this.writeStdSubstitution(ti, needsTa)) | |
| { | |
| this.abiTags.writeSymbol(ti, this); | |
| if (needsTa) | |
| template_args(ti); | |
| return; | |
| } | |
| auto ag = ti.aliasdecl ? ti.aliasdecl.isAggregateDeclaration() : null; | |
| if (ag && ag.mangleOverride) | |
| { | |
| this.writeNamespace( | |
| ti.toAlias().cppnamespace, () { | |
| this.writeIdentifier(ag.mangleOverride.id); | |
| if (ag.mangleOverride.agg && ag.mangleOverride.agg.isInstantiated()) | |
| { | |
| auto to = ag.mangleOverride.agg.isInstantiated(); | |
| append(to); | |
| this.abiTags.writeSymbol(to.tempdecl, this); | |
| template_args(to); | |
| } | |
| }, haveNE); | |
| } | |
| else | |
| { | |
| this.writeNamespace( | |
| s.cppnamespace, () { | |
| this.writeIdentifier(ti.tempdecl.toAlias().ident); | |
| append(ti.tempdecl); | |
| this.abiTags.writeSymbol(ti.tempdecl, this); | |
| template_args(ti); | |
| }, haveNE); | |
| } | |
| } | |
| /******** | |
| * See if s is actually an instance of a template | |
| * Params: | |
| * s = symbol | |
| * Returns: | |
| * if s is instance of a template, return the instance, otherwise return s | |
| */ | |
| static Dsymbol getInstance(Dsymbol s) | |
| { | |
| Dsymbol p = s.toParent(); | |
| if (p) | |
| { | |
| if (TemplateInstance ti = p.isTemplateInstance()) | |
| return ti; | |
| } | |
| return s; | |
| } | |
| /// Get the namespace of a template instance | |
| CPPNamespaceDeclaration getTiNamespace(TemplateInstance ti) | |
| { | |
| // If we receive a pre-semantic `TemplateInstance`, | |
| // `cppnamespace` is always `null` | |
| return ti.tempdecl ? ti.cppnamespace | |
| : this.context.res.asType().toDsymbol(null).cppnamespace; | |
| } | |
| /******** | |
| * Get qualifier for `s`, meaning the symbol | |
| * that s is in the symbol table of. | |
| * The module does not count as a qualifier, because C++ | |
| * does not have modules. | |
| * Params: | |
| * s = symbol that may have a qualifier | |
| * s is rewritten to be TemplateInstance if s is one | |
| * Returns: | |
| * qualifier, null if none | |
| */ | |
| static Dsymbol getQualifier(Dsymbol s) | |
| { | |
| Dsymbol p = s.toParent(); | |
| return (p && !p.isModule()) ? p : null; | |
| } | |
| // Detect type char | |
| static bool isChar(RootObject o) | |
| { | |
| Type t = isType(o); | |
| return (t && t.equals(Type.tchar)); | |
| } | |
| // Detect type ::std::char_traits<char> | |
| bool isChar_traits_char(RootObject o) | |
| { | |
| return isIdent_char(Id.char_traits, o); | |
| } | |
| // Detect type ::std::allocator<char> | |
| bool isAllocator_char(RootObject o) | |
| { | |
| return isIdent_char(Id.allocator, o); | |
| } | |
| // Detect type ::std::ident<char> | |
| bool isIdent_char(Identifier ident, RootObject o) | |
| { | |
| Type t = isType(o); | |
| if (!t || t.ty != Tstruct) | |
| return false; | |
| Dsymbol s = (cast(TypeStruct)t).toDsymbol(null); | |
| if (s.ident != ident) | |
| return false; | |
| Dsymbol p = s.toParent(); | |
| if (!p) | |
| return false; | |
| TemplateInstance ti = p.isTemplateInstance(); | |
| if (!ti) | |
| return false; | |
| Dsymbol q = getQualifier(ti); | |
| const bool inStd = isStd(q) || isStd(this.getTiNamespace(ti)); | |
| return inStd && ti.tiargs.dim == 1 && isChar((*ti.tiargs)[0]); | |
| } | |
| /*** | |
| * Detect template args <char, ::std::char_traits<char>> | |
| * and write st if found. | |
| * Returns: | |
| * true if found | |
| */ | |
| bool char_std_char_traits_char(TemplateInstance ti, string st) | |
| { | |
| if (ti.tiargs.dim == 2 && | |
| isChar((*ti.tiargs)[0]) && | |
| isChar_traits_char((*ti.tiargs)[1])) | |
| { | |
| buf.writestring(st.ptr); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void prefix_name(Dsymbol s) | |
| { | |
| //printf("prefix_name(%s)\n", s.toChars()); | |
| if (substitute(s)) | |
| return; | |
| if (isStd(s)) | |
| return buf.writestring("St"); | |
| auto si = getInstance(s); | |
| Dsymbol p = getQualifier(si); | |
| if (p) | |
| { | |
| if (isStd(p)) | |
| { | |
| bool needsTa; | |
| auto ti = si.isTemplateInstance(); | |
| if (this.writeStdSubstitution(ti, needsTa)) | |
| { | |
| this.abiTags.writeSymbol(ti, this); | |
| if (needsTa) | |
| { | |
| template_args(ti); | |
| append(ti); | |
| } | |
| return; | |
| } | |
| buf.writestring("St"); | |
| } | |
| else | |
| prefix_name(p); | |
| } | |
| source_name(si, true); | |
| if (!isStd(si)) | |
| /* Do this after the source_name() call to keep components[] | |
| * in the right order. | |
| * https://issues.dlang.org/show_bug.cgi?id=17947 | |
| */ | |
| append(si); | |
| } | |
| /** | |
| * Write common substitution for standard types, such as std::allocator | |
| * | |
| * This function assumes that the symbol `ti` is in the namespace `std`. | |
| * | |
| * Params: | |
| * ti = Template instance to consider | |
| * needsTa = If this function returns `true`, this value indicates | |
| * if additional template argument mangling is needed | |
| * | |
| * Returns: | |
| * `true` if a special std symbol was found | |
| */ | |
| bool writeStdSubstitution(TemplateInstance ti, out bool needsTa) | |
| { | |
| if (!ti) | |
| return false; | |
| if (!isStd(this.getTiNamespace(ti)) && !isStd(getQualifier(ti))) | |
| return false; | |
| if (ti.name == Id.allocator) | |
| { | |
| buf.writestring("Sa"); | |
| needsTa = true; | |
| return true; | |
| } | |
| if (ti.name == Id.basic_string) | |
| { | |
| // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>> | |
| if (ti.tiargs.dim == 3 && | |
| isChar((*ti.tiargs)[0]) && | |
| isChar_traits_char((*ti.tiargs)[1]) && | |
| isAllocator_char((*ti.tiargs)[2])) | |
| { | |
| buf.writestring("Ss"); | |
| return true; | |
| } | |
| buf.writestring("Sb"); // ::std::basic_string | |
| needsTa = true; | |
| return true; | |
| } | |
| // ::std::basic_istream<char, ::std::char_traits<char>> | |
| if (ti.name == Id.basic_istream && | |
| char_std_char_traits_char(ti, "Si")) | |
| return true; | |
| // ::std::basic_ostream<char, ::std::char_traits<char>> | |
| if (ti.name == Id.basic_ostream && | |
| char_std_char_traits_char(ti, "So")) | |
| return true; | |
| // ::std::basic_iostream<char, ::std::char_traits<char>> | |
| if (ti.name == Id.basic_iostream && | |
| char_std_char_traits_char(ti, "Sd")) | |
| return true; | |
| return false; | |
| } | |
| void cpp_mangle_name(Dsymbol s, bool qualified) | |
| { | |
| //printf("cpp_mangle_name(%s, %d)\n", s.toChars(), qualified); | |
| Dsymbol p = s.toParent(); | |
| Dsymbol se = s; | |
| bool write_prefix = true; | |
| if (p && p.isTemplateInstance()) | |
| { | |
| se = p; | |
| if (find(p.isTemplateInstance().tempdecl) >= 0) | |
| write_prefix = false; | |
| p = p.toParent(); | |
| } | |
| if (!p || p.isModule()) | |
| { | |
| source_name(se, false); | |
| append(s); | |
| return; | |
| } | |
| if (!isStd(p) || qualified) | |
| { | |
| buf.writeByte('N'); | |
| if (write_prefix) | |
| { | |
| if (isStd(p)) | |
| buf.writestring("St"); | |
| else | |
| prefix_name(p); | |
| } | |
| source_name(se, true); | |
| buf.writeByte('E'); | |
| append(s); | |
| return; | |
| } | |
| /* The N..E is not required if: | |
| * 1. the parent is 'std' | |
| * 2. 'std' is the initial qualifier | |
| * 3. there is no CV-qualifier or a ref-qualifier for a member function | |
| * ABI 5.1.8 | |
| */ | |
| TemplateInstance ti = se.isTemplateInstance(); | |
| if (s.ident == Id.allocator) | |
| { | |
| buf.writestring("Sa"); // "Sa" is short for ::std::allocator | |
| template_args(ti); | |
| } | |
| else if (s.ident == Id.basic_string) | |
| { | |
| // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>> | |
| if (ti.tiargs.dim == 3 && | |
| isChar((*ti.tiargs)[0]) && | |
| isChar_traits_char((*ti.tiargs)[1]) && | |
| isAllocator_char((*ti.tiargs)[2])) | |
| { | |
| buf.writestring("Ss"); | |
| return; | |
| } | |
| buf.writestring("Sb"); // ::std::basic_string | |
| template_args(ti); | |
| } | |
| else | |
| { | |
| // ::std::basic_istream<char, ::std::char_traits<char>> | |
| if (s.ident == Id.basic_istream) | |
| { | |
| if (char_std_char_traits_char(ti, "Si")) | |
| return; | |
| } | |
| else if (s.ident == Id.basic_ostream) | |
| { | |
| if (char_std_char_traits_char(ti, "So")) | |
| return; | |
| } | |
| else if (s.ident == Id.basic_iostream) | |
| { | |
| if (char_std_char_traits_char(ti, "Sd")) | |
| return; | |
| } | |
| buf.writestring("St"); | |
| source_name(se, true); | |
| } | |
| append(s); | |
| } | |
| /** | |
| * Write CV-qualifiers to the buffer | |
| * | |
| * CV-qualifiers are 'r': restrict (unused in D), 'V': volatile, 'K': const | |
| * | |
| * See_Also: | |
| * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.CV-qualifiers | |
| */ | |
| void CV_qualifiers(const Type t) | |
| { | |
| if (t.isConst()) | |
| buf.writeByte('K'); | |
| } | |
| /** | |
| * Mangles a variable | |
| * | |
| * Params: | |
| * d = Variable declaration to mangle | |
| * isNested = Whether this variable is nested, e.g. a template parameter | |
| * or within a namespace | |
| */ | |
| void mangle_variable(VarDeclaration d, bool isNested) | |
| { | |
| // fake mangling for fields to fix https://issues.dlang.org/show_bug.cgi?id=16525 | |
| if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared))) | |
| { | |
| d.error("Internal Compiler Error: C++ static non-`__gshared` non-`extern` variables not supported"); | |
| fatal(); | |
| } | |
| Dsymbol p = d.toParent(); | |
| if (p && !p.isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE" | |
| { | |
| buf.writestring("_ZN"); | |
| prefix_name(p); | |
| source_name(d, true); | |
| buf.writeByte('E'); | |
| } | |
| else if (isNested) | |
| { | |
| buf.writestring("_Z"); | |
| source_name(d, false); | |
| } | |
| else | |
| { | |
| if (auto varTags = ABITagContainer.forSymbol(d)) | |
| { | |
| buf.writestring("_Z"); | |
| source_name(d, false); | |
| return; | |
| } | |
| if (auto typeTags = ABITagContainer.forSymbol(d.type.toDsymbol(null))) | |
| { | |
| buf.writestring("_Z"); | |
| source_name(d, false); | |
| this.abiTags.write(*this.buf, typeTags); | |
| return; | |
| } | |
| //char beta[6] should mangle as "beta" | |
| buf.writestring(d.ident.toString()); | |
| } | |
| } | |
| void mangle_function(FuncDeclaration d) | |
| { | |
| //printf("mangle_function(%s)\n", d.toChars()); | |
| /* | |
| * <mangled-name> ::= _Z <encoding> | |
| */ | |
| buf.writestring("_Z"); | |
| this.mangle_function_encoding(d); | |
| } | |
| void mangle_function_encoding(FuncDeclaration d) | |
| { | |
| //printf("mangle_function_encoding(%s)\n", d.toChars()); | |
| /* | |
| * <encoding> ::= <function name> <bare-function-type> | |
| * ::= <data name> | |
| * ::= <special-name> | |
| */ | |
| TypeFunction tf = cast(TypeFunction)d.type; | |
| if (TemplateDeclaration ftd = getFuncTemplateDecl(d)) | |
| { | |
| /* It's an instance of a function template | |
| */ | |
| TemplateInstance ti = d.parent.isTemplateInstance(); | |
| assert(ti); | |
| this.mangleTemplatedFunction(d, tf, ftd, ti); | |
| return; | |
| } | |
| Dsymbol p = d.toParent(); | |
| if (p && !p.isModule() && tf.linkage == LINK.cpp) | |
| { | |
| this.mangleNestedFuncPrefix(tf, p); | |
| if (auto ctor = d.isCtorDeclaration()) | |
| buf.writestring(ctor.isCpCtor ? "C2" : "C1"); | |
| else if (d.isAggregateDtor()) | |
| buf.writestring("D1"); | |
| else if (d.ident && d.ident == Id.assign) | |
| buf.writestring("aS"); | |
| else if (d.ident && d.ident == Id.eq) | |
| buf.writestring("eq"); | |
| else if (d.ident && d.ident == Id.index) | |
| buf.writestring("ix"); | |
| else if (d.ident && d.ident == Id.call) | |
| buf.writestring("cl"); | |
| else | |
| source_name(d, true); | |
| buf.writeByte('E'); | |
| } | |
| else | |
| { | |
| source_name(d, false); | |
| } | |
| // Save offset for potentially writing tags | |
| const size_t off = this.buf.length(); | |
| // Template args accept extern "C" symbols with special mangling | |
| if (tf.linkage == LINK.cpp) | |
| mangleFunctionParameters(tf.parameterList); | |
| if (!tf.next.isTypeBasic()) | |
| this.writeRemainingTags(off, tf); | |
| } | |
| /** | |
| * Recursively mangles a non-scoped namespace | |
| * | |
| * Parameters: | |
| * ns = Namespace to mangle | |
| * dg = A delegate to write the identifier in this namespace | |
| * haveNE = When `false` (the default), surround the namespace / dg | |
| * call with nested name qualifier (`N..E`). | |
| * Otherwise, they are already present (e.g. `Nspace` was used). | |
| */ | |
| void writeNamespace(CPPNamespaceDeclaration ns, scope void delegate() dg, | |
| bool haveNE = false) | |
| { | |
| void runDg () { if (dg !is null) dg(); } | |
| if (ns is null || ns.ident is null) | |
| return runDg(); | |
| if (isStd(ns)) | |
| { | |
| if (!substitute(ns)) | |
| buf.writestring("St"); | |
| runDg(); | |
| } | |
| else if (dg !is null) | |
| { | |
| if (!haveNE) | |
| buf.writestring("N"); | |
| if (!substitute(ns)) | |
| { | |
| this.writeNamespace(ns.cppnamespace, null); | |
| this.writeIdentifier(ns.ident); | |
| append(ns); | |
| } | |
| dg(); | |
| if (!haveNE) | |
| buf.writestring("E"); | |
| } | |
| else if (!substitute(ns)) | |
| { | |
| this.writeNamespace(ns.cppnamespace, null); | |
| this.writeIdentifier(ns.ident); | |
| append(ns); | |
| } | |
| } | |
| /** | |
| * Mangles a function template to C++ | |
| * | |
| * Params: | |
| * d = Function declaration | |
| * tf = Function type (casted d.type) | |
| * ftd = Template declaration (ti.templdecl) | |
| * ti = Template instance (d.parent) | |
| */ | |
| void mangleTemplatedFunction(FuncDeclaration d, TypeFunction tf, | |
| TemplateDeclaration ftd, TemplateInstance ti) | |
| { | |
| Dsymbol p = ti.toParent(); | |
| // Check if this function is *not* nested | |
| if (!p || p.isModule() || tf.linkage != LINK.cpp) | |
| { | |
| this.context.ti = ti; | |
| this.context.fd = d; | |
| this.context.res = d; | |
| TypeFunction preSemantic = cast(TypeFunction)d.originalType; | |
| auto nspace = ti.toParent(); | |
| if (nspace && nspace.isNspace()) | |
| this.writeChained(ti.toParent(), () => source_name(ti, true)); | |
| else | |
| source_name(ti, false); | |
| this.mangleReturnType(preSemantic); | |
| this.mangleFunctionParameters(ParameterList(preSemantic.parameterList.parameters, tf.parameterList.varargs)); | |
| return; | |
| } | |
| // It's a nested function (e.g. a member of an aggregate) | |
| this.mangleNestedFuncPrefix(tf, p); | |
| if (d.isCtorDeclaration()) | |
| { | |
| buf.writestring("C1"); | |
| mangleFunctionParameters(tf.parameterList); | |
| return; | |
| } | |
| else if (d.isAggregateDtor()) | |
| { | |
| buf.writestring("D1"); | |
| mangleFunctionParameters(tf.parameterList); | |
| return; | |
| } | |
| int firstTemplateArg = 0; | |
| bool appendReturnType = true; | |
| bool isConvertFunc = false; | |
| string symName; | |
| // test for special symbols | |
| CppOperator whichOp = isCppOperator(ti.name); | |
| final switch (whichOp) | |
| { | |
| case CppOperator.Unknown: | |
| break; | |
| case CppOperator.Cast: | |
| symName = "cv"; | |
| firstTemplateArg = 1; | |
| isConvertFunc = true; | |
| appendReturnType = false; | |
| break; | |
| case CppOperator.Assign: | |
| symName = "aS"; | |
| break; | |
| case CppOperator.Eq: | |
| symName = "eq"; | |
| break; | |
| case CppOperator.Index: | |
| symName = "ix"; | |
| break; | |
| case CppOperator.Call: | |
| symName = "cl"; | |
| break; | |
| case CppOperator.Unary: | |
| case CppOperator.Binary: | |
| case CppOperator.OpAssign: | |
| TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); | |
| assert(td); | |
| assert(ti.tiargs.dim >= 1); | |
| TemplateParameter tp = (*td.parameters)[0]; | |
| TemplateValueParameter tv = tp.isTemplateValueParameter(); | |
| if (!tv || !tv.valType.isString()) | |
| break; // expecting a string argument to operators! | |
| Expression exp = (*ti.tiargs)[0].isExpression(); | |
| StringExp str = exp.toStringExp(); | |
| switch (whichOp) | |
| { | |
| case CppOperator.Unary: | |
| switch (str.peekString()) | |
| { | |
| case "*": symName = "de"; goto continue_template; | |
| case "++": symName = "pp"; goto continue_template; | |
| case "--": symName = "mm"; goto continue_template; | |
| case "-": symName = "ng"; goto continue_template; | |
| case "+": symName = "ps"; goto continue_template; | |
| case "~": symName = "co"; goto continue_template; | |
| default: break; | |
| } | |
| break; | |
| case CppOperator.Binary: | |
| switch (str.peekString()) | |
| { | |
| case ">>": symName = "rs"; goto continue_template; | |
| case "<<": symName = "ls"; goto continue_template; | |
| case "*": symName = "ml"; goto continue_template; | |
| case "-": symName = "mi"; goto continue_template; | |
| case "+": symName = "pl"; goto continue_template; | |
| case "&": symName = "an"; goto continue_template; | |
| case "/": symName = "dv"; goto continue_template; | |
| case "%": symName = "rm"; goto continue_template; | |
| case "^": symName = "eo"; goto continue_template; | |
| case "|": symName = "or"; goto continue_template; | |
| default: break; | |
| } | |
| break; | |
| case CppOperator.OpAssign: | |
| switch (str.peekString()) | |
| { | |
| case "*": symName = "mL"; goto continue_template; | |
| case "+": symName = "pL"; goto continue_template; | |
| case "-": symName = "mI"; goto continue_template; | |
| case "/": symName = "dV"; goto continue_template; | |
| case "%": symName = "rM"; goto continue_template; | |
| case ">>": symName = "rS"; goto continue_template; | |
| case "<<": symName = "lS"; goto continue_template; | |
| case "&": symName = "aN"; goto continue_template; | |
| case "|": symName = "oR"; goto continue_template; | |
| case "^": symName = "eO"; goto continue_template; | |
| default: break; | |
| } | |
| break; | |
| default: | |
| assert(0); | |
| continue_template: | |
| firstTemplateArg = 1; | |
| break; | |
| } | |
| break; | |
| } | |
| if (symName.length == 0) | |
| source_name(ti, true); | |
| else | |
| { | |
| buf.writestring(symName); | |
| if (isConvertFunc) | |
| template_arg(ti, 0); | |
| appendReturnType = template_args(ti, firstTemplateArg) && appendReturnType; | |
| } | |
| buf.writeByte('E'); | |
| if (appendReturnType) | |
| headOfType(tf.nextOf()); // mangle return type | |
| mangleFunctionParameters(tf.parameterList); | |
| } | |
| /** | |
| * Mangle the parameters of a function | |
| * | |
| * For templated functions, `context.res` is set to the `FuncDeclaration` | |
| * | |
| * Params: | |
| * parameters = Array of `Parameter` to mangle | |
| * varargs = if != 0, this function has varargs parameters | |
| */ | |
| void mangleFunctionParameters(ParameterList parameterList) | |
| { | |
| int numparams = 0; | |
| foreach (n, fparam; parameterList) | |
| { | |
| Type t = fparam.type.merge2(); | |
| if (fparam.isReference()) | |
| t = t.referenceTo(); | |
| else if (fparam.storageClass & STC.lazy_) | |
| { | |
| // Mangle as delegate | |
| auto tf = new TypeFunction(ParameterList(), t, LINK.d); | |
| auto td = new TypeDelegate(tf); | |
| t = td.merge(); | |
| } | |
| else if (Type cpptype = target.cpp.parameterType(t)) | |
| t = cpptype; | |
| if (t.ty == Tsarray) | |
| { | |
| // Static arrays in D are passed by value; no counterpart in C++ | |
| .error(loc, "Internal Compiler Error: unable to pass static array `%s` to extern(C++) function, use pointer instead", | |
| t.toChars()); | |
| fatal(); | |
| } | |
| auto prev = this.context.push({ | |
| TypeFunction tf; | |
| if (isDsymbol(this.context.res)) | |
| tf = cast(TypeFunction)this.context.res.asFuncDecl().type; | |
| else | |
| tf = this.context.res.asType().isTypeFunction(); | |
| assert(tf); | |
| return (*tf.parameterList.parameters)[n].type; | |
| }()); | |
| scope (exit) this.context.pop(prev); | |
| if (this.context.ti && global.params.cplusplus >= CppStdRevision.cpp11) | |
| handleParamPack(t, this.context.ti.tempdecl.isTemplateDeclaration().parameters); | |
| headOfType(t); | |
| ++numparams; | |
| } | |
| if (parameterList.varargs == VarArg.variadic) | |
| buf.writeByte('z'); | |
| else if (!numparams) | |
| buf.writeByte('v'); // encode (void) parameters | |
| } | |
| /****** The rest is type mangling ************/ | |
| void error(Type t) | |
| { | |
| const(char)* p; | |
| if (t.isImmutable()) | |
| p = "`immutable` "; | |
| else if (t.isShared()) | |
| p = "`shared` "; | |
| else | |
| p = ""; | |
| .error(loc, "Internal Compiler Error: %stype `%s` cannot be mapped to C++\n", p, t.toChars()); | |
| fatal(); //Fatal, because this error should be handled in frontend | |
| } | |
| /**************************** | |
| * Mangle a type, | |
| * treating it as a Head followed by a Tail. | |
| * Params: | |
| * t = Head of a type | |
| */ | |
| void headOfType(Type t) | |
| { | |
| if (t.ty == Tclass) | |
| { | |
| mangleTypeClass(cast(TypeClass)t, true); | |
| } | |
| else | |
| { | |
| // For value types, strip const/immutable/shared from the head of the type | |
| auto prev = this.context.push(this.context.res.asType().mutableOf().unSharedOf()); | |
| scope (exit) this.context.pop(prev); | |
| t.mutableOf().unSharedOf().accept(this); | |
| } | |
| } | |
| /****** | |
| * Write out 1 or 2 character basic type mangling. | |
| * Handle const and substitutions. | |
| * Params: | |
| * t = type to mangle | |
| * p = if not 0, then character prefix | |
| * c = mangling character | |
| */ | |
| void writeBasicType(Type t, char p, char c) | |
| { | |
| // Only do substitutions for non-fundamental types. | |
| if (!isFundamentalType(t) || t.isConst()) | |
| { | |
| if (substitute(t)) | |
| return; | |
| else | |
| append(t); | |
| } | |
| CV_qualifiers(t); | |
| if (p) | |
| buf.writeByte(p); | |
| buf.writeByte(c); | |
| } | |
| /**************** | |
| * Write structs and enums. | |
| * Params: | |
| * t = TypeStruct or TypeEnum | |
| */ | |
| void doSymbol(Type t) | |
| { | |
| if (substitute(t)) | |
| return; | |
| CV_qualifiers(t); | |
| // Handle any target-specific struct types. | |
| if (auto tm = target.cpp.typeMangle(t)) | |
| { | |
| buf.writestring(tm); | |
| } | |
| else | |
| { | |
| Dsymbol s = t.toDsymbol(null); | |
| Dsymbol p = s.toParent(); | |
| if (p && p.isTemplateInstance()) | |
| { | |
| /* https://issues.dlang.org/show_bug.cgi?id=17947 | |
| * Substitute the template instance symbol, not the struct/enum symbol | |
| */ | |
| if (substitute(p)) | |
| return; | |
| } | |
| if (!substitute(s)) | |
| cpp_mangle_name(s, false); | |
| } | |
| if (t.isConst()) | |
| append(t); | |
| } | |
| /************************ | |
| * Mangle a class type. | |
| * If it's the head, treat the initial pointer as a value type. | |
| * Params: | |
| * t = class type | |
| * head = true for head of a type | |
| */ | |
| void mangleTypeClass(TypeClass t, bool head) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| /* Mangle as a <pointer to><struct> | |
| */ | |
| if (substitute(t)) | |
| return; | |
| if (!head) | |
| CV_qualifiers(t); | |
| buf.writeByte('P'); | |
| CV_qualifiers(t); | |
| { | |
| Dsymbol s = t.toDsymbol(null); | |
| Dsymbol p = s.toParent(); | |
| if (p && p.isTemplateInstance()) | |
| { | |
| /* https://issues.dlang.org/show_bug.cgi?id=17947 | |
| * Substitute the template instance symbol, not the class symbol | |
| */ | |
| if (substitute(p)) | |
| return; | |
| } | |
| } | |
| if (!substitute(t.sym)) | |
| { | |
| cpp_mangle_name(t.sym, false); | |
| } | |
| if (t.isConst()) | |
| append(null); // C++ would have an extra type here | |
| append(t); | |
| } | |
| /** | |
| * Mangle the prefix of a nested (e.g. member) function | |
| * | |
| * Params: | |
| * tf = Type of the nested function | |
| * parent = Parent in which the function is nested | |
| */ | |
| void mangleNestedFuncPrefix(TypeFunction tf, Dsymbol parent) | |
| { | |
| /* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E | |
| * ::= N [<CV-qualifiers>] <template-prefix> <template-args> E | |
| */ | |
| buf.writeByte('N'); | |
| CV_qualifiers(tf); | |
| /* <prefix> ::= <prefix> <unqualified-name> | |
| * ::= <template-prefix> <template-args> | |
| * ::= <template-param> | |
| * ::= # empty | |
| * ::= <substitution> | |
| * ::= <prefix> <data-member-prefix> | |
| */ | |
| prefix_name(parent); | |
| } | |
| /** | |
| * Write `Dp` (C++11 function parameter pack prefix) if 't' is a TemplateSequenceParameter (T...). | |
| * | |
| * Params: | |
| * t = Parameter type | |
| * params = Template parameters of the function | |
| */ | |
| private void handleParamPack(Type t, TemplateParameters* params) | |
| { | |
| if (t.isTypeReference()) | |
| t = t.nextOf(); | |
| auto ti = t.isTypeIdentifier(); | |
| if (!ti) | |
| return; | |
| auto idx = templateParamIndex(ti.ident, params); | |
| if (idx < params.length && (*params)[idx].isTemplateTupleParameter()) | |
| buf.writestring("Dp"); | |
| } | |
| /** | |
| * Helper function to write a `T..._` template index. | |
| * | |
| * Params: | |
| * idx = Index of `param` in the template argument list | |
| * param = Template parameter to mangle | |
| */ | |
| private void writeTemplateArgIndex(size_t idx, TemplateParameter param) | |
| { | |
| // expressions are mangled in <X..E> | |
| if (param.isTemplateValueParameter()) | |
| buf.writeByte('X'); | |
| buf.writeByte('T'); | |
| writeSequenceFromIndex(idx); | |
| buf.writeByte('_'); | |
| if (param.isTemplateValueParameter()) | |
| buf.writeByte('E'); | |
| } | |
| /** | |
| * Given an array of template parameters and an identifier, | |
| * returns the index of the identifier in that array. | |
| * | |
| * Params: | |
| * ident = Identifier for which substitution is attempted | |
| * (e.g. `void func(T)(T param)` => `T` from `T param`) | |
| * params = `TemplateParameters` of the enclosing symbol | |
| * (in the previous example, `func`'s template parameters) | |
| * | |
| * Returns: | |
| * The index of the identifier match in `params`, | |
| * or `params.length` if there wasn't any match. | |
| */ | |
| private static size_t templateParamIndex( | |
| const ref Identifier ident, TemplateParameters* params) | |
| { | |
| foreach (idx, param; *params) | |
| if (param.ident == ident) | |
| return idx; | |
| return params.length; | |
| } | |
| /** | |
| * Given a template instance `t`, write its qualified name | |
| * without the template parameter list | |
| * | |
| * Params: | |
| * t = Post-parsing `TemplateInstance` pointing to the symbol | |
| * to mangle (one level deep) | |
| * dg = Delegate to execute after writing the qualified symbol | |
| * | |
| */ | |
| private void writeQualified(TemplateInstance t, scope void delegate() dg) | |
| { | |
| auto type = isType(this.context.res); | |
| if (!type) | |
| { | |
| this.writeIdentifier(t.name); | |
| return dg(); | |
| } | |
| auto sym1 = type.toDsymbol(null); | |
| if (!sym1) | |
| { | |
| this.writeIdentifier(t.name); | |
| return dg(); | |
| } | |
| // Get the template instance | |
| auto sym = getQualifier(sym1); | |
| auto sym2 = getQualifier(sym); | |
| if (sym2 && isStd(sym2)) // Nspace path | |
| { | |
| bool unused; | |
| assert(sym.isTemplateInstance()); | |
| if (this.writeStdSubstitution(sym.isTemplateInstance(), unused)) | |
| return dg(); | |
| // std names don't require `N..E` | |
| buf.writestring("St"); | |
| this.writeIdentifier(t.name); | |
| this.append(t); | |
| return dg(); | |
| } | |
| else if (sym2) | |
| { | |
| buf.writestring("N"); | |
| if (!this.substitute(sym2)) | |
| sym2.accept(this); | |
| } | |
| this.writeNamespace( | |
| sym1.cppnamespace, () { | |
| this.writeIdentifier(t.name); | |
| this.append(t); | |
| dg(); | |
| }); | |
| if (sym2) | |
| buf.writestring("E"); | |
| } | |
| extern(C++): | |
| alias visit = Visitor.visit; | |
| override void visit(TypeNull t) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| writeBasicType(t, 'D', 'n'); | |
| } | |
| override void visit(TypeNoreturn t) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| writeBasicType(t, 0, 'v'); // mangle like `void` | |
| } | |
| override void visit(TypeBasic t) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| // Handle any target-specific basic types. | |
| if (auto tm = target.cpp.typeMangle(t)) | |
| { | |
| // Only do substitutions for non-fundamental types. | |
| if (!isFundamentalType(t) || t.isConst()) | |
| { | |
| if (substitute(t)) | |
| return; | |
| else | |
| append(t); | |
| } | |
| CV_qualifiers(t); | |
| buf.writestring(tm); | |
| return; | |
| } | |
| /* <builtin-type>: | |
| * v void | |
| * w wchar_t | |
| * b bool | |
| * c char | |
| * a signed char | |
| * h unsigned char | |
| * s short | |
| * t unsigned short | |
| * i int | |
| * j unsigned int | |
| * l long | |
| * m unsigned long | |
| * x long long, __int64 | |
| * y unsigned long long, __int64 | |
| * n __int128 | |
| * o unsigned __int128 | |
| * f float | |
| * d double | |
| * e long double, __float80 | |
| * g __float128 | |
| * z ellipsis | |
| * Dd 64 bit IEEE 754r decimal floating point | |
| * De 128 bit IEEE 754r decimal floating point | |
| * Df 32 bit IEEE 754r decimal floating point | |
| * Dh 16 bit IEEE 754r half-precision floating point | |
| * Di char32_t | |
| * Ds char16_t | |
| * u <source-name> # vendor extended type | |
| */ | |
| if (t.isimaginary() || t.iscomplex()) | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=22806 | |
| // Complex and imaginary types are represented in the same way as | |
| // arrays or vectors in C++. First substitute the outer type, then | |
| // write out the mangle string of the underlying type. | |
| if (substitute(t)) | |
| return; | |
| append(t); | |
| CV_qualifiers(t); | |
| if (t.isimaginary()) | |
| buf.writeByte('G'); // 'G' means imaginary | |
| else | |
| buf.writeByte('C'); // 'C' means complex | |
| switch (t.ty) | |
| { | |
| case Timaginary32: | |
| case Tcomplex32: | |
| return Type.tfloat32.accept(this); | |
| case Timaginary64: | |
| case Tcomplex64: | |
| return Type.tfloat64.accept(this); | |
| case Timaginary80: | |
| case Tcomplex80: | |
| return Type.tfloat80.accept(this); | |
| default: | |
| assert(0); | |
| } | |
| } | |
| char c; | |
| char p = 0; | |
| switch (t.ty) | |
| { | |
| case Tvoid: c = 'v'; break; | |
| case Tint8: c = 'a'; break; | |
| case Tuns8: c = 'h'; break; | |
| case Tint16: c = 's'; break; | |
| case Tuns16: c = 't'; break; | |
| case Tint32: c = 'i'; break; | |
| case Tuns32: c = 'j'; break; | |
| case Tfloat32: c = 'f'; break; | |
| case Tint64: | |
| c = target.c.longsize == 8 ? 'l' : 'x'; | |
| break; | |
| case Tuns64: | |
| c = target.c.longsize == 8 ? 'm' : 'y'; | |
| break; | |
| case Tint128: c = 'n'; break; | |
| case Tuns128: c = 'o'; break; | |
| case Tfloat64: c = 'd'; break; | |
| case Tfloat80: c = 'e'; break; | |
| case Tbool: c = 'b'; break; | |
| case Tchar: c = 'c'; break; | |
| case Twchar: p = 'D'; c = 's'; break; // since C++11 | |
| case Tdchar: p = 'D'; c = 'i'; break; // since C++11 | |
| default: | |
| return error(t); | |
| } | |
| writeBasicType(t, p, c); | |
| } | |
| override void visit(TypeVector t) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| if (substitute(t)) | |
| return; | |
| append(t); | |
| CV_qualifiers(t); | |
| // Handle any target-specific vector types. | |
| if (auto tm = target.cpp.typeMangle(t)) | |
| { | |
| buf.writestring(tm); | |
| } | |
| else | |
| { | |
| assert(t.basetype && t.basetype.ty == Tsarray); | |
| auto tsa = t.basetype.isTypeSArray(); | |
| assert(tsa.dim); | |
| buf.writestring("Dv"); // -- Gnu ABI v.4 | |
| buf.print(tsa.dim.toInteger()); | |
| buf.writeByte('_'); | |
| t.basetype.nextOf().accept(this); | |
| } | |
| } | |
| override void visit(TypeSArray t) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| if (!substitute(t)) | |
| append(t); | |
| CV_qualifiers(t); | |
| buf.writeByte('A'); | |
| buf.print(t.dim ? t.dim.toInteger() : 0); | |
| buf.writeByte('_'); | |
| t.next.accept(this); | |
| } | |
| override void visit(TypePointer t) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| // Check for const - Since we cannot represent C++'s `char* const`, | |
| // and `const char* const` (a.k.a `const(char*)` in D) is mangled | |
| // the same as `const char*` (`const(char)*` in D), we need to add | |
| // an extra `K` if `nextOf()` is `const`, before substitution | |
| CV_qualifiers(t); | |
| if (substitute(t)) | |
| return; | |
| buf.writeByte('P'); | |
| auto prev = this.context.push(this.context.res.asType().nextOf()); | |
| scope (exit) this.context.pop(prev); | |
| t.next.accept(this); | |
| append(t); | |
| } | |
| override void visit(TypeReference t) | |
| { | |
| if (substitute(t)) | |
| return; | |
| buf.writeByte('R'); | |
| CV_qualifiers(t.nextOf()); | |
| headOfType(t.nextOf()); | |
| if (t.nextOf().isConst()) | |
| append(t.nextOf()); | |
| append(t); | |
| } | |
| override void visit(TypeFunction t) | |
| { | |
| /* | |
| * <function-type> ::= F [Y] <bare-function-type> E | |
| * <bare-function-type> ::= <signature type>+ | |
| * # types are possible return type, then parameter types | |
| */ | |
| /* ABI says: | |
| "The type of a non-static member function is considered to be different, | |
| for the purposes of substitution, from the type of a namespace-scope or | |
| static member function whose type appears similar. The types of two | |
| non-static member functions are considered to be different, for the | |
| purposes of substitution, if the functions are members of different | |
| classes. In other words, for the purposes of substitution, the class of | |
| which the function is a member is considered part of the type of | |
| function." | |
| BUG: Right now, types of functions are never merged, so our simplistic | |
| component matcher always finds them to be different. | |
| We should use Type.equals on these, and use different | |
| TypeFunctions for non-static member functions, and non-static | |
| member functions of different classes. | |
| */ | |
| if (substitute(t)) | |
| return; | |
| buf.writeByte('F'); | |
| if (t.linkage == LINK.c) | |
| buf.writeByte('Y'); | |
| Type tn = t.next; | |
| if (t.isref) | |
| tn = tn.referenceTo(); | |
| tn.accept(this); | |
| mangleFunctionParameters(t.parameterList); | |
| buf.writeByte('E'); | |
| append(t); | |
| } | |
| override void visit(TypeStruct t) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| //printf("TypeStruct %s\n", t.toChars()); | |
| doSymbol(t); | |
| } | |
| override void visit(TypeEnum t) | |
| { | |
| if (t.isImmutable() || t.isShared()) | |
| return error(t); | |
| /* __c_(u)long(long) and others get special mangling | |
| */ | |
| const id = t.sym.ident; | |
| //printf("enum id = '%s'\n", id.toChars()); | |
| if (id == Id.__c_long) | |
| return writeBasicType(t, 0, 'l'); | |
| else if (id == Id.__c_ulong) | |
| return writeBasicType(t, 0, 'm'); | |
| else if (id == Id.__c_char) | |
| return writeBasicType(t, 0, 'c'); | |
| else if (id == Id.__c_wchar_t) | |
| return writeBasicType(t, 0, 'w'); | |
| else if (id == Id.__c_longlong) | |
| return writeBasicType(t, 0, 'x'); | |
| else if (id == Id.__c_ulonglong) | |
| return writeBasicType(t, 0, 'y'); | |
| else if (id == Id.__c_complex_float) | |
| return Type.tcomplex32.accept(this); | |
| else if (id == Id.__c_complex_double) | |
| return Type.tcomplex64.accept(this); | |
| else if (id == Id.__c_complex_real) | |
| return Type.tcomplex80.accept(this); | |
| doSymbol(t); | |
| } | |
| override void visit(TypeClass t) | |
| { | |
| mangleTypeClass(t, false); | |
| } | |
| /** | |
| * Performs template parameter substitution | |
| * | |
| * Mangling is performed on a copy of the post-parsing AST before | |
| * any semantic pass is run. | |
| * There is no easy way to link a type to the template parameters | |
| * once semantic has run, because: | |
| * - the `TemplateInstance` installs aliases in its scope to its params | |
| * - `AliasDeclaration`s are resolved in many places | |
| * - semantic passes are destructive, so the `TypeIdentifier` gets lost | |
| * | |
| * As a result, the best approach with the current architecture is to: | |
| * - Run the visitor on the `originalType` of the function, | |
| * looking up any `TypeIdentifier` at the template scope when found. | |
| * - Fallback to the post-semantic `TypeFunction` when the identifier is | |
| * not a template parameter. | |
| */ | |
| override void visit(TypeIdentifier t) | |
| { | |
| auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; | |
| assert(decl.parameters !is null); | |
| auto idx = templateParamIndex(t.ident, decl.parameters); | |
| // If not found, default to the post-semantic type | |
| if (idx >= decl.parameters.length) | |
| return this.context.res.visitObject(this); | |
| auto param = (*decl.parameters)[idx]; | |
| if (auto type = this.context.res.isType()) | |
| CV_qualifiers(type); | |
| // Otherwise, attempt substitution (`S_` takes precedence on `T_`) | |
| if (this.substitute(param)) | |
| return; | |
| // If substitution failed, write `TX_` where `X` is the index | |
| this.writeTemplateArgIndex(idx, param); | |
| this.append(param); | |
| // Write the ABI tags, if any | |
| if (auto sym = this.context.res.isDsymbol()) | |
| this.abiTags.writeSymbol(sym, this); | |
| } | |
| /// Ditto | |
| override void visit(TypeInstance t) | |
| { | |
| assert(t.tempinst !is null); | |
| t.tempinst.accept(this); | |
| } | |
| /** | |
| * Mangles a `TemplateInstance` | |
| * | |
| * A `TemplateInstance` can be found either in the parameter, | |
| * or the return value. | |
| * Arguments to the template instance needs to be mangled but the template | |
| * can be partially substituted, so for example the following: | |
| * `Container!(T, Val) func16479_12 (alias Container, T, int Val) ()` | |
| * will mangle the return value part to "T_IT0_XT1_EE" | |
| */ | |
| override void visit(TemplateInstance t) | |
| { | |
| // Template names are substituted, but args still need to be written | |
| void writeArgs () | |
| { | |
| buf.writeByte('I'); | |
| // When visiting the arguments, the context will be set to the | |
| // resolved type | |
| auto analyzed_ti = this.context.res.asType().toDsymbol(null).isInstantiated(); | |
| auto prev = this.context; | |
| scope (exit) this.context.pop(prev); | |
| foreach (idx, RootObject o; *t.tiargs) | |
| { | |
| this.context.res = (*analyzed_ti.tiargs)[idx]; | |
| o.visitObject(this); | |
| } | |
| if (analyzed_ti.tiargs.dim > t.tiargs.dim) | |
| { | |
| // If the resolved AST has more args than the parse one, | |
| // we have default arguments | |
| auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters; | |
| foreach (idx, arg; (*oparams)[t.tiargs.dim .. $]) | |
| { | |
| this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.dim]; | |
| if (auto ttp = arg.isTemplateTypeParameter()) | |
| ttp.defaultType.accept(this); | |
| else if (auto tvp = arg.isTemplateValueParameter()) | |
| tvp.defaultValue.accept(this); | |
| else if (auto tvp = arg.isTemplateThisParameter()) | |
| tvp.defaultType.accept(this); | |
| else if (auto tvp = arg.isTemplateAliasParameter()) | |
| tvp.defaultAlias.visitObject(this); | |
| else | |
| assert(0, arg.toString()); | |
| } | |
| } | |
| buf.writeByte('E'); | |
| } | |
| // `name` is used, not `ident` | |
| assert(t.name !is null); | |
| assert(t.tiargs !is null); | |
| bool needsTa; | |
| auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; | |
| // Attempt to substitute the template itself | |
| auto idx = templateParamIndex(t.name, decl.parameters); | |
| if (idx < decl.parameters.length) | |
| { | |
| auto param = (*decl.parameters)[idx]; | |
| if (auto type = t.getType()) | |
| CV_qualifiers(type); | |
| if (this.substitute(param)) | |
| return; | |
| this.writeTemplateArgIndex(idx, param); | |
| this.append(param); | |
| writeArgs(); | |
| } | |
| else if (this.writeStdSubstitution(t, needsTa)) | |
| { | |
| if (needsTa) | |
| writeArgs(); | |
| } | |
| else if (!this.substitute(t)) | |
| this.writeQualified(t, &writeArgs); | |
| } | |
| /// Ditto | |
| override void visit(IntegerExp t) | |
| { | |
| this.buf.writeByte('L'); | |
| t.type.accept(this); | |
| this.buf.print(t.getInteger()); | |
| this.buf.writeByte('E'); | |
| } | |
| override void visit(Nspace t) | |
| { | |
| if (auto p = getQualifier(t)) | |
| p.accept(this); | |
| if (isStd(t)) | |
| buf.writestring("St"); | |
| else | |
| { | |
| this.writeIdentifier(t.ident); | |
| this.append(t); | |
| } | |
| } | |
| override void visit(Type t) | |
| { | |
| error(t); | |
| } | |
| void visit(Tuple t) | |
| { | |
| assert(0); | |
| } | |
| } | |
| /// Helper code to visit `RootObject`, as it doesn't define `accept`, | |
| /// only its direct subtypes do. | |
| private void visitObject(V : Visitor)(RootObject o, V this_) | |
| { | |
| assert(o !is null); | |
| if (Type ta = isType(o)) | |
| ta.accept(this_); | |
| else if (Expression ea = isExpression(o)) | |
| ea.accept(this_); | |
| else if (Dsymbol sa = isDsymbol(o)) | |
| sa.accept(this_); | |
| else if (TemplateParameter t = isTemplateParameter(o)) | |
| t.accept(this_); | |
| else if (Tuple t = isTuple(o)) | |
| // `Tuple` inherits `RootObject` and does not define accept | |
| // For this reason, this uses static dispatch on the visitor | |
| this_.visit(t); | |
| else | |
| assert(0, o.toString()); | |
| } | |
| /// Helper function to safely get a type out of a `RootObject` | |
| private Type asType(RootObject o) | |
| { | |
| Type ta = isType(o); | |
| // When called with context.res as argument, it can be `FuncDeclaration` | |
| if (!ta && o.asFuncDecl()) | |
| ta = (cast(FuncDeclaration)o).type; | |
| assert(ta !is null, o.toString()); | |
| return ta; | |
| } | |
| /// Helper function to safely get a `FuncDeclaration` out of a `RootObject` | |
| private FuncDeclaration asFuncDecl(RootObject o) | |
| { | |
| Dsymbol d = isDsymbol(o); | |
| assert(d !is null); | |
| auto fd = d.isFuncDeclaration(); | |
| assert(fd !is null); | |
| return fd; | |
| } | |
| /// Helper class to compare entries in components | |
| private extern(C++) final class ComponentVisitor : Visitor | |
| { | |
| /// Only one of the following is not `null`, it's always | |
| /// the most specialized type, set from the ctor | |
| private Nspace namespace; | |
| /// Ditto | |
| private CPPNamespaceDeclaration namespace2; | |
| /// Ditto | |
| private TypePointer tpointer; | |
| /// Ditto | |
| private TypeReference tref; | |
| /// Ditto | |
| private TypeIdentifier tident; | |
| /// Least specialized type | |
| private RootObject object; | |
| /// Set to the result of the comparison | |
| private bool result; | |
| public this(RootObject base) | |
| { | |
| switch (base.dyncast()) | |
| { | |
| case DYNCAST.dsymbol: | |
| if (auto ns = (cast(Dsymbol)base).isNspace()) | |
| this.namespace = ns; | |
| else if (auto ns = (cast(Dsymbol)base).isCPPNamespaceDeclaration()) | |
| this.namespace2 = ns; | |
| else | |
| goto default; | |
| break; | |
| case DYNCAST.type: | |
| auto t = cast(Type)base; | |
| if (t.ty == Tpointer) | |
| this.tpointer = cast(TypePointer)t; | |
| else if (t.ty == Treference) | |
| this.tref = cast(TypeReference)t; | |
| else if (t.ty == Tident) | |
| this.tident = cast(TypeIdentifier)t; | |
| else | |
| goto default; | |
| break; | |
| // Note: ABI tags are also handled here (they are TupleExp of StringExp) | |
| default: | |
| this.object = base; | |
| } | |
| } | |
| /// Introduce base class overloads | |
| alias visit = Visitor.visit; | |
| /// Least specialized overload of each direct child of `RootObject` | |
| public override void visit(Dsymbol o) | |
| { | |
| this.result = this.object && this.object == o; | |
| } | |
| /// Ditto | |
| public override void visit(Expression o) | |
| { | |
| this.result = this.object && this.object == o; | |
| } | |
| /// Ditto | |
| public void visit(Tuple o) | |
| { | |
| this.result = this.object && this.object == o; | |
| } | |
| /// Ditto | |
| public override void visit(Type o) | |
| { | |
| this.result = this.object && this.object == o; | |
| } | |
| /// Ditto | |
| public override void visit(TemplateParameter o) | |
| { | |
| this.result = this.object && this.object == o; | |
| } | |
| /** | |
| * This overload handles composed types including template parameters | |
| * | |
| * Components for substitutions include "next" type. | |
| * For example, if `ref T` is present, `ref T` and `T` will be present | |
| * in the substitution array. | |
| * But since we don't have the final/merged type, we cannot rely on | |
| * object comparison, and need to recurse instead. | |
| */ | |
| public override void visit(TypeReference o) | |
| { | |
| if (!this.tref) | |
| return; | |
| if (this.tref == o) | |
| this.result = true; | |
| else | |
| { | |
| // It might be a reference to a template parameter that we already | |
| // saw, so we need to recurse | |
| scope v = new ComponentVisitor(this.tref.next); | |
| o.next.visitObject(v); | |
| this.result = v.result; | |
| } | |
| } | |
| /// Ditto | |
| public override void visit(TypePointer o) | |
| { | |
| if (!this.tpointer) | |
| return; | |
| if (this.tpointer == o) | |
| this.result = true; | |
| else | |
| { | |
| // It might be a pointer to a template parameter that we already | |
| // saw, so we need to recurse | |
| scope v = new ComponentVisitor(this.tpointer.next); | |
| o.next.visitObject(v); | |
| this.result = v.result; | |
| } | |
| } | |
| /// Ditto | |
| public override void visit(TypeIdentifier o) | |
| { | |
| /// Since we know they are at the same level, scope resolution will | |
| /// give us the same symbol, thus we can just compare ident. | |
| this.result = (this.tident && (this.tident.ident == o.ident)); | |
| } | |
| /** | |
| * Overload which accepts a Namespace | |
| * | |
| * It is very common for large C++ projects to have multiple files sharing | |
| * the same `namespace`. If any D project adopts the same approach | |
| * (e.g. separating data structures from functions), it will lead to two | |
| * `Nspace` objects being instantiated, with different addresses. | |
| * At the same time, we cannot compare just any Dsymbol via identifier, | |
| * because it messes with templates. | |
| * | |
| * See_Also: | |
| * https://issues.dlang.org/show_bug.cgi?id=18922 | |
| * | |
| * Params: | |
| * ns = C++ namespace to do substitution for | |
| */ | |
| public override void visit(Nspace ns) | |
| { | |
| this.result = isNamespaceEqual(this.namespace, ns) | |
| || isNamespaceEqual(this.namespace2, ns); | |
| } | |
| /// Ditto | |
| public override void visit(CPPNamespaceDeclaration ns) | |
| { | |
| this.result = isNamespaceEqual(this.namespace, ns) | |
| || isNamespaceEqual(this.namespace2, ns); | |
| } | |
| } | |
| /// Transitional functions for `CPPNamespaceDeclaration` / `Nspace` | |
| /// Remove when `Nspace` is removed. | |
| private bool isNamespaceEqual (Nspace a, Nspace b) | |
| { | |
| if (a is null || b is null) | |
| return false; | |
| return a.equals(b); | |
| } | |
| /// Ditto | |
| private bool isNamespaceEqual (Nspace a, CPPNamespaceDeclaration b) | |
| { | |
| return isNamespaceEqual(b, a); | |
| } | |
| /// Ditto | |
| private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = 0) | |
| { | |
| if ((a is null) != (b is null)) | |
| return false; | |
| if (!a.ident.equals(b.ident)) | |
| return false; | |
| // We need to see if there's more ident enclosing | |
| if (auto pb = b.toParent().isNspace()) | |
| return isNamespaceEqual(a.cppnamespace, pb); | |
| else | |
| return a.cppnamespace is null; | |
| } | |
| /// Returns: | |
| /// Whether two `CPPNamespaceDeclaration` are equals | |
| private bool isNamespaceEqual (CPPNamespaceDeclaration a, CPPNamespaceDeclaration b) | |
| { | |
| if (a is null || b is null) | |
| return false; | |
| if ((a.cppnamespace is null) != (b.cppnamespace is null)) | |
| return false; | |
| if (a.ident != b.ident) | |
| return false; | |
| return a.cppnamespace is null ? true : isNamespaceEqual(a.cppnamespace, b.cppnamespace); | |
| } | |
| /** | |
| * A container for ABI tags | |
| * | |
| * At its hearth, there is a sorted array of ABI tags having been written | |
| * already. ABI tags can be present on parameters, template parameters, | |
| * return value, and varaible. ABI tags for a given type needs to be written | |
| * sorted. When a function returns a type that has ABI tags, only the tags that | |
| * haven't been printed as part of the mangling (e.g. arguments) are written | |
| * directly after the function name. | |
| * | |
| * This means that: | |
| * --- | |
| * /++ C++ type definitions: | |
| * struct [[gnu::abi_tag("tag1")]] Struct1 {}; | |
| * struct [[gnu::abi_tag("tag2")]] Struct2 {}; | |
| * // Can also be: "tag2", "tag1", since tags are sorted. | |
| * struct [[gnu::abi_tag("tag1", "tag2")]] Struct3 {}; | |
| * +/ | |
| * // Functions definitions: | |
| * Struct3 func1 (Struct1); | |
| * Struct3 func2 (Struct2); | |
| * Struct3 func3 (Struct2, Struct1); | |
| * --- | |
| * Will be respectively pseudo-mangled (part of interest between stars) as: | |
| * "_Z4 func1 *B4tag2* ParamsMangling" (ParamsMangling includes tag1), | |
| * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes tag2), | |
| * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes both). | |
| * | |
| * This is why why need to keep a list of tags that were written, | |
| * and insert the missing one after parameter mangling has been written. | |
| * Since there's a lot of operations that are not easily doable in DMD | |
| * (since we can't use Phobos), this special container is implemented. | |
| */ | |
| private struct ABITagContainer | |
| { | |
| private Array!StringExp written; | |
| static ArrayLiteralExp forSymbol (Dsymbol s) | |
| { | |
| if (!s) | |
| return null; | |
| // If this is a template instance, we want the declaration, | |
| // as that's where the UDAs are | |
| if (auto ti = s.isTemplateInstance()) | |
| s = ti.tempdecl; | |
| if (!s.userAttribDecl || !s.userAttribDecl.atts) | |
| return null; | |
| foreach (exp; *s.userAttribDecl.atts) | |
| { | |
| if (UserAttributeDeclaration.isGNUABITag(exp)) | |
| return (*exp.isStructLiteralExp().elements)[0] | |
| .isArrayLiteralExp(); | |
| } | |
| return null; | |
| } | |
| void writeSymbol(Dsymbol s, CppMangleVisitor self) | |
| { | |
| auto tale = forSymbol(s); | |
| if (!tale) return; | |
| if (self.substitute(tale)) | |
| return; | |
| this.write(*self.buf, tale); | |
| } | |
| /** | |
| * Write an ArrayLiteralExp (expected to be an ABI tag) to the buffer | |
| * | |
| * Params: | |
| * buf = Buffer to write mangling to | |
| * ale = GNU ABI tag array literal expression, semantically analyzed | |
| */ | |
| void write (ref OutBuffer buf, ArrayLiteralExp ale, bool skipKnown = false) | |
| { | |
| void writeElem (StringExp exp) | |
| { | |
| const tag = exp.peekString(); | |
| buf.writestring("B"); | |
| buf.print(tag.length); | |
| buf.writestring(tag); | |
| } | |
| bool match; | |
| foreach (exp; *ale.elements) | |
| { | |
| auto elem = exp.toStringExp(); | |
| auto idx = closestIndex(this.written[], elem, match); | |
| if (!match) | |
| { | |
| writeElem(elem); | |
| this.written.insert(idx, elem); | |
| } | |
| else if (!skipKnown) | |
| writeElem(elem); | |
| } | |
| } | |
| } | |
| /** | |
| * Returns the closest index to to `exp` in `slice` | |
| * | |
| * Performs a binary search on `slice` (assumes `slice` is sorted), | |
| * and returns either `exp`'s index in `slice` if `exact` is `true`, | |
| * or the index at which `exp` can be inserted in `slice` if `exact is `false`. | |
| * Inserting `exp` at the return value will keep the array sorted. | |
| * | |
| * Params: | |
| * slice = The sorted slice to search into | |
| * exp = The string expression to search for | |
| * exact = If `true` on return, `exp` was found in `slice` | |
| * | |
| * Returns: | |
| * Either the index to insert `exp` at (if `exact == false`), | |
| * or the index of `exp` in `slice`. | |
| */ | |
| private size_t closestIndex (const(StringExp)[] slice, StringExp exp, out bool exact) | |
| { | |
| if (!slice.length) return 0; | |
| const StringExp* first = slice.ptr; | |
| while (true) | |
| { | |
| int res = dstrcmp(exp.peekString(), slice[$ / 2].peekString()); | |
| if (res == 0) | |
| { | |
| exact = true; | |
| return (&slice[$/2] - first); | |
| } | |
| if (slice.length == 1) | |
| return (slice.ptr - first) + (res > 0); | |
| slice = slice[(res > 0 ? $ / 2 : 0) .. (res > 0 ? $ : $ / 2)]; | |
| } | |
| } | |
| // | |
| unittest | |
| { | |
| bool match; | |
| auto s1 = new StringExp(Loc.initial, "Amande"); | |
| auto s2 = new StringExp(Loc.initial, "Baguette"); | |
| auto s3 = new StringExp(Loc.initial, "Croissant"); | |
| auto s4 = new StringExp(Loc.initial, "Framboises"); | |
| auto s5 = new StringExp(Loc.initial, "Proscuitto"); | |
| // Found, odd size | |
| assert(closestIndex([s1, s2, s3, s4, s5], s1, match) == 0 && match); | |
| assert(closestIndex([s1, s2, s3, s4, s5], s2, match) == 1 && match); | |
| assert(closestIndex([s1, s2, s3, s4, s5], s3, match) == 2 && match); | |
| assert(closestIndex([s1, s2, s3, s4, s5], s4, match) == 3 && match); | |
| assert(closestIndex([s1, s2, s3, s4, s5], s5, match) == 4 && match); | |
| // Not found, even size | |
| assert(closestIndex([s2, s3, s4, s5], s1, match) == 0 && !match); | |
| assert(closestIndex([s1, s3, s4, s5], s2, match) == 1 && !match); | |
| assert(closestIndex([s1, s2, s4, s5], s3, match) == 2 && !match); | |
| assert(closestIndex([s1, s2, s3, s5], s4, match) == 3 && !match); | |
| assert(closestIndex([s1, s2, s3, s4], s5, match) == 4 && !match); | |
| // Found, even size | |
| assert(closestIndex([s1, s2, s3, s4], s1, match) == 0 && match); | |
| assert(closestIndex([s1, s2, s3, s4], s2, match) == 1 && match); | |
| assert(closestIndex([s1, s2, s3, s4], s3, match) == 2 && match); | |
| assert(closestIndex([s1, s2, s3, s4], s4, match) == 3 && match); | |
| assert(closestIndex([s1, s3, s4, s5], s5, match) == 3 && match); | |
| // Not found, odd size | |
| assert(closestIndex([s2, s4, s5], s1, match) == 0 && !match); | |
| assert(closestIndex([s1, s4, s5], s2, match) == 1 && !match); | |
| assert(closestIndex([s1, s2, s4], s3, match) == 2 && !match); | |
| assert(closestIndex([s1, s3, s5], s4, match) == 2 && !match); | |
| assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match); | |
| } | |
| /** | |
| * Visits the return type of a function and writes leftover ABI tags | |
| */ | |
| extern(C++) private final class LeftoverVisitor : Visitor | |
| { | |
| /// List of tags to write | |
| private Array!StringExp toWrite; | |
| /// List of tags to ignore | |
| private const(Array!StringExp)* ignore; | |
| /// | |
| public this(const(Array!StringExp)* previous) | |
| { | |
| this.ignore = previous; | |
| } | |
| /// Reintroduce base class overloads | |
| public alias visit = Visitor.visit; | |
| /// Least specialized overload of each direct child of `RootObject` | |
| public override void visit(Dsymbol o) | |
| { | |
| auto ale = ABITagContainer.forSymbol(o); | |
| if (!ale) return; | |
| bool match; | |
| foreach (elem; *ale.elements) | |
| { | |
| auto se = elem.toStringExp(); | |
| closestIndex((*this.ignore)[], se, match); | |
| if (match) continue; | |
| auto idx = closestIndex(this.toWrite[], se, match); | |
| if (!match) | |
| this.toWrite.insert(idx, se); | |
| } | |
| } | |
| /// Ditto | |
| public override void visit(Type o) | |
| { | |
| if (auto sym = o.toDsymbol(null)) | |
| sym.accept(this); | |
| } | |
| /// Composite type | |
| public override void visit(TypePointer o) | |
| { | |
| o.next.accept(this); | |
| } | |
| public override void visit(TypeReference o) | |
| { | |
| o.next.accept(this); | |
| } | |
| } |