1
/**
2
 * Do mangling for C++ linkage.
3
 *
4
 * This is the POSIX side of the implementation.
5
 * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`.
6
 *
7
 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
8
 * Authors: Walter Bright, http://www.digitalmars.com
9
 * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10
 * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d, _cppmangle.d)
11
 * Documentation:  https://dlang.org/phobos/dmd_cppmangle.html
12
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmangle.d
13
 *
14
 * References:
15
 *  Follows Itanium C++ ABI 1.86 section 5.1
16
 *  http://refspecs.linux-foundation.org/cxxabi-1.86.html#mangling
17
 *  which is where the grammar comments come from.
18
 *
19
 * Bugs:
20
 *  https://issues.dlang.org/query.cgi
21
 *  enter `C++, mangling` as the keywords.
22
 */
23

24
module dmd.cppmangle;
25

26
import core.stdc.string;
27
import core.stdc.stdio;
28

29
import dmd.arraytypes;
30
import dmd.attrib;
31
import dmd.declaration;
32
import dmd.dsymbol;
33
import dmd.dtemplate;
34
import dmd.errors;
35
import dmd.expression;
36
import dmd.func;
37
import dmd.globals;
38
import dmd.id;
39
import dmd.identifier;
40
import dmd.mtype;
41
import dmd.nspace;
42
import dmd.root.array;
43
import dmd.root.outbuffer;
44
import dmd.root.rootobject;
45
import dmd.root.string;
46
import dmd.target;
47
import dmd.tokens;
48
import dmd.typesem;
49
import dmd.visitor;
50

51

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)
55
{
56 1
    __gshared const(Identifier)[] operators = null;
57 1
    if (!operators)
58 1
        operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign];
59 1
    foreach (i, op; operators)
60
    {
61 1
        if (op == id)
62 1
            return cast(CppOperator)i;
63
    }
64 1
    return CppOperator.Unknown;
65
}
66

67
///
68
extern(C++) const(char)* toCppMangleItanium(Dsymbol s)
69
{
70
    //printf("toCppMangleItanium(%s)\n", s.toChars());
71 1
    OutBuffer buf;
72 1
    scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc);
73 1
    v.mangleOf(s);
74 1
    return buf.extractChars();
75
}
76

77
///
78
extern(C++) const(char)* cppTypeInfoMangleItanium(Dsymbol s)
79
{
80
    //printf("cppTypeInfoMangle(%s)\n", s.toChars());
81 1
    OutBuffer buf;
82 1
    buf.writestring("_ZTI");    // "TI" means typeinfo structure
83 1
    scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc);
84 1
    v.cpp_mangle_name(s, false);
85 1
    return buf.extractChars();
86
}
87

88
/******************************
89
 * Determine if sym is the 'primary' destructor, that is,
90
 * the most-aggregate destructor (the one that is defined as __xdtor)
91
 * Params:
92
 *      sym = Dsymbol
93
 * Returns:
94
 *      true if sym is the primary destructor for an aggregate
95
 */
96
bool isPrimaryDtor(const Dsymbol sym)
97
{
98 1
    const dtor = sym.isDtorDeclaration();
99 1
    if (!dtor)
100 1
        return false;
101 1
    const ad = dtor.isMember();
102 1
    assert(ad);
103 1
    return dtor == ad.primaryDtor;
104
}
105

106
/// Context used when processing pre-semantic AST
107
private struct Context
108
{
109
    /// Template instance of the function being mangled
110
    TemplateInstance ti;
111
    /// Function declaration we're mangling
112
    FuncDeclaration fd;
113
    /// Current type / expression being processed (semantically analyzed)
114
    RootObject res;
115

116
    @disable ref Context opAssign(ref Context other);
117
    @disable ref Context opAssign(Context other);
118

119
    /**
120
     * Helper function to track `res`
121
     *
122
     * Params:
123
     *   next = Value to set `this.res` to.
124
     *          If `this.res` is `null`, the expression is not evalutated.
125
     *          This allow this code to be used even when no context is needed.
126
     *
127
     * Returns:
128
     *   The previous state of this `Context` object
129
     */
130
    private Context push(lazy RootObject next)
131
    {
132 1
        auto r = this.res;
133 1
        if (r !is null)
134 1
            this.res = next;
135 1
        return Context(this.ti, this.fd, r);
136
    }
137

138
    /**
139
     * Reset the context to a previous one, making any adjustment necessary
140
     */
141
    private void pop(ref Context prev)
142
    {
143 1
        this.res = prev.res;
144
    }
145
}
146

147
private final class CppMangleVisitor : Visitor
148
{
149
    /// Context used when processing pre-semantic AST
150
    private Context context;
151

152
    ABITagContainer abiTags;    /// Container for already-written ABI tags
153
    Objects components;         /// array of components available for substitution
154
    OutBuffer* buf;             /// append the mangling to buf[]
155
    Loc loc;                    /// location for use in error messages
156

157
    /**
158
     * Constructor
159
     *
160
     * Params:
161
     *   buf = `OutBuffer` to write the mangling to
162
     *   loc = `Loc` of the symbol being mangled
163
     */
164 1
    this(OutBuffer* buf, Loc loc)
165
    {
166 1
        this.buf = buf;
167 1
        this.loc = loc;
168
    }
169

170
    /*****
171
     * Entry point. Append mangling to buf[]
172
     * Params:
173
     *  s = symbol to mangle
174
     */
175
    void mangleOf(Dsymbol s)
176
    {
177 1
        if (VarDeclaration vd = s.isVarDeclaration())
178
        {
179 1
            mangle_variable(vd, vd.cppnamespace !is null);
180
        }
181 1
        else if (FuncDeclaration fd = s.isFuncDeclaration())
182
        {
183 1
            mangle_function(fd);
184
        }
185
        else
186
        {
187 0
            assert(0);
188
        }
189
    }
190

191
    /**
192
     * Mangle the return type of a function
193
     *
194
     * This is called on a templated function type.
195
     * Context is set to the `FuncDeclaration`.
196
     *
197
     * Params:
198
     *   preSemantic = the `FuncDeclaration`'s `originalType`
199
     */
200
    void mangleReturnType(TypeFunction preSemantic)
201
    {
202 1
        auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
203 1
        Type rt = preSemantic.nextOf();
204 1
        if (tf.isref)
205 1
            rt = rt.referenceTo();
206 1
        auto prev = this.context.push(tf.nextOf());
207 1
        scope (exit) this.context.pop(prev);
208 1
        this.headOfType(rt);
209
    }
210

211
    /**
212
     * Write a seq-id from an index number, excluding the terminating '_'
213
     *
214
     * Params:
215
     *   idx = the index in a substitution list.
216
     *         Note that index 0 has no value, and `S0_` would be the
217
     *         substitution at index 1 in the list.
218
     *
219
     * See-Also:
220
     *  https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id
221
     */
222
    private void writeSequenceFromIndex(size_t idx)
223
    {
224 1
        if (idx)
225
        {
226
            void write_seq_id(size_t i)
227
            {
228 1
                if (i >= 36)
229
                {
230 1
                    write_seq_id(i / 36);
231 1
                    i %= 36;
232
                }
233 1
                i += (i < 10) ? '0' : 'A' - 10;
234 1
                buf.writeByte(cast(char)i);
235
            }
236

237 1
            write_seq_id(idx - 1);
238
        }
239
    }
240

241
    /**
242
     * Attempt to perform substitution on `p`
243
     *
244
     * If `p` already appeared in the mangling, it is stored as
245
     * a 'part', and short references in the form of `SX_` can be used.
246
     * Note that `p` can be anything: template declaration, struct declaration,
247
     * class declaration, namespace...
248
     *
249
     * Params:
250
     *   p = The object to attempt to substitute
251
     *   nested = Whether or not `p` is to be considered nested.
252
     *            When `true`, `N` will be prepended before the substitution.
253
     *
254
     * Returns:
255
     *   Whether `p` already appeared in the mangling,
256
     *   and substitution has been written to `this.buf`.
257
     */
258
    bool substitute(RootObject p, bool nested = false)
259
    {
260
        //printf("substitute %s\n", p ? p.toChars() : null);
261 1
        auto i = find(p);
262 1
        if (i >= 0)
263
        {
264
            //printf("\tmatch\n");
265
            /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ...
266
             */
267 1
            if (nested)
268 1
                buf.writeByte('N');
269 1
            buf.writeByte('S');
270 1
            writeSequenceFromIndex(i);
271 1
            buf.writeByte('_');
272 1
            return true;
273
        }
274 1
        return false;
275
    }
276

277
    /******
278
     * See if `p` exists in components[]
279
     *
280
     * Note that components can contain `null` entries,
281
     * as the index used in mangling is based on the index in the array.
282
     *
283
     * If called with an object whose dynamic type is `Nspace`,
284
     * calls the `find(Nspace)` overload.
285
     *
286
     * Returns:
287
     *  index if found, -1 if not
288
     */
289
    int find(RootObject p)
290
    {
291
        //printf("find %p %d %s\n", p, p.dyncast(), p ? p.toChars() : null);
292 1
        scope v = new ComponentVisitor(p);
293 1
        foreach (i, component; components)
294
        {
295 1
            if (component)
296 1
                component.visitObject(v);
297 1
            if (v.result)
298 1
                return cast(int)i;
299
        }
300 1
        return -1;
301
    }
302

303
    /*********************
304
     * Append p to components[]
305
     */
306
    void append(RootObject p)
307
    {
308
        //printf("append %p %d %s\n", p, p.dyncast(), p ? p.toChars() : "null");
309 1
        components.push(p);
310
    }
311

312
    /**
313
     * Write an identifier preceded by its length
314
     *
315
     * Params:
316
     *   ident = `Identifier` to write to `this.buf`
317
     */
318
    void writeIdentifier(const ref Identifier ident)
319
    {
320 1
        const name = ident.toString();
321 1
        this.buf.print(name.length);
322 1
        this.buf.writestring(name);
323
    }
324

325
    /**
326
     * Insert the leftover ABI tags to the buffer
327
     *
328
     * This inset ABI tags that hasn't already been written
329
     * after the mangled name of the function.
330
     * For more details, see the `abiTags` variable.
331
     *
332
     * Params:
333
     *   off  = Offset to insert at
334
     *   fd   = Type of the function to mangle the return type of
335
     */
336
    void writeRemainingTags(size_t off, TypeFunction tf)
337
    {
338 1
        scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written);
339 1
        tf.next.accept(remainingVisitor);
340 1
        OutBuffer b2;
341 1
        foreach (se; remainingVisitor.toWrite)
342
        {
343 1
            auto tag = se.peekString();
344
            // We can only insert a slice, and each insert is a memmove,
345
            // so use a temporary buffer to keep it efficient.
346 1
            b2.reset();
347 1
            b2.writestring("B");
348 1
            b2.print(tag.length);
349 1
            b2.writestring(tag);
350 1
            this.buf.insert(off, b2[]);
351 1
            off += b2.length;
352
        }
353
    }
354

355
    /************************
356
     * Determine if symbol is indeed the global ::std namespace.
357
     * Params:
358
     *  s = symbol to check
359
     * Returns:
360
     *  true if it is ::std
361
     */
362
    static bool isStd(Dsymbol s)
363
    {
364 1
        if (!s)
365 1
            return false;
366

367 1
        if (auto cnd = s.isCPPNamespaceDeclaration())
368 1
            return isStd(cnd);
369

370 1
        return (s.ident == Id.std &&    // the right name
371 1
                s.isNspace() &&         // g++ disallows global "std" for other than a namespace
372 1
                !getQualifier(s));      // at global level
373
    }
374

375
    /// Ditto
376
    static bool isStd(CPPNamespaceDeclaration s)
377
    {
378 1
        return s && s.cppnamespace is null && s.ident == Id.std;
379
    }
380

381
    /************************
382
     * Determine if type is a C++ fundamental type.
383
     * Params:
384
     *  t = type to check
385
     * Returns:
386
     *  true if it is a fundamental type
387
     */
388
    static bool isFundamentalType(Type t)
389
    {
390
        // First check the target whether some specific ABI is being followed.
391 1
        bool isFundamental = void;
392 1
        if (target.cpp.fundamentalType(t, isFundamental))
393 0
            return isFundamental;
394

395 1
        if (auto te = t.isTypeEnum())
396
        {
397
            // Peel off enum type from special types.
398 1
            if (te.sym.isSpecial())
399 1
                t = te.memType();
400
        }
401

402
        // Fundamental arithmetic types:
403
        // 1. integral types: bool, char, int, ...
404
        // 2. floating point types: float, double, real
405
        // 3. void
406
        // 4. null pointer: std::nullptr_t (since C++11)
407 1
        if (t.ty == Tvoid || t.ty == Tbool)
408 1
            return true;
409 1
        else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11)
410 0
            return true;
411
        else
412 1
            return t.isTypeBasic() && (t.isintegral() || t.isreal());
413
    }
414

415
    /******************************
416
     * Write the mangled representation of a template argument.
417
     * Params:
418
     *  ti  = the template instance
419
     *  arg = the template argument index
420
     */
421
    void template_arg(TemplateInstance ti, size_t arg)
422
    {
423 1
        TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
424 1
        assert(td);
425 1
        TemplateParameter tp = (*td.parameters)[arg];
426 1
        RootObject o = (*ti.tiargs)[arg];
427

428 1
        auto prev = this.context.push({
429 1
                TemplateInstance parentti;
430 1
                if (this.context.res.dyncast() == DYNCAST.dsymbol)
431 1
                    parentti = this.context.res.asFuncDecl().parent.isTemplateInstance();
432
                else
433 1
                    parentti = this.context.res.asType().toDsymbol(null).parent.isTemplateInstance();
434 1
                return (*parentti.tiargs)[arg];
435 1
            }());
436 1
        scope (exit) this.context.pop(prev);
437

438 1
        if (tp.isTemplateTypeParameter())
439
        {
440 1
            Type t = isType(o);
441 1
            assert(t);
442 1
            t.accept(this);
443
        }
444 1
        else if (TemplateValueParameter tv = tp.isTemplateValueParameter())
445
        {
446
            // <expr-primary> ::= L <type> <value number> E  # integer literal
447 1
            if (tv.valType.isintegral())
448
            {
449 1
                Expression e = isExpression(o);
450 1
                assert(e);
451 1
                buf.writeByte('L');
452 1
                tv.valType.accept(this);
453 1
                auto val = e.toUInteger();
454 1
                if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0)
455
                {
456 1
                    val = -val;
457 1
                    buf.writeByte('n');
458
                }
459 1
                buf.print(val);
460 1
                buf.writeByte('E');
461
            }
462
            else
463
            {
464 0
                ti.error("Internal Compiler Error: C++ `%s` template value parameter is not supported", tv.valType.toChars());
465 0
                fatal();
466
            }
467
        }
468 1
        else if (tp.isTemplateAliasParameter())
469
        {
470
            // Passing a function as alias parameter is the same as passing
471
            // `&function`
472 1
            Dsymbol d = isDsymbol(o);
473 1
            Expression e = isExpression(o);
474 1
            if (d && d.isFuncDeclaration())
475
            {
476
                // X .. E => template parameter is an expression
477
                // 'ad'   => unary operator ('&')
478
                // L .. E => is a <expr-primary>
479 1
                buf.writestring("XadL");
480 1
                mangle_function(d.isFuncDeclaration());
481 1
                buf.writestring("EE");
482
            }
483 1
            else if (e && e.op == TOK.variable && (cast(VarExp)e).var.isVarDeclaration())
484
            {
485 0
                VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration();
486 0
                buf.writeByte('L');
487 0
                mangle_variable(vd, true);
488 0
                buf.writeByte('E');
489
            }
490 1
            else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember)
491
            {
492 1
                if (!substitute(d))
493
                {
494 1
                    cpp_mangle_name(d, false);
495
                }
496
            }
497
            else
498
            {
499 0
                ti.error("Internal Compiler Error: C++ `%s` template alias parameter is not supported", o.toChars());
500 0
                fatal();
501
            }
502
        }
503 0
        else if (tp.isTemplateThisParameter())
504
        {
505 0
            ti.error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o.toChars());
506 0
            fatal();
507
        }
508
        else
509
        {
510 0
            assert(0);
511
        }
512
    }
513

514
    /******************************
515
     * Write the mangled representation of the template arguments.
516
     * Params:
517
     *  ti = the template instance
518
     *  firstArg = index of the first template argument to mangle
519
     *             (used for operator overloading)
520
     * Returns:
521
     *  true if any arguments were written
522
     */
523
    bool template_args(TemplateInstance ti, int firstArg = 0)
524
    {
525
        /* <template-args> ::= I <template-arg>+ E
526
         */
527 1
        if (!ti || ti.tiargs.dim <= firstArg)   // could happen if std::basic_string is not a template
528 1
            return false;
529 1
        buf.writeByte('I');
530 1
        foreach (i; firstArg .. ti.tiargs.dim)
531
        {
532 1
            TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
533 1
            assert(td);
534 1
            TemplateParameter tp = (*td.parameters)[i];
535

536
            /*
537
             * <template-arg> ::= <type>               # type or template
538
             *                ::= X <expression> E     # expression
539
             *                ::= <expr-primary>       # simple expressions
540
             *                ::= J <template-arg>* E  # argument pack
541
             *
542
             * Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.template-arg
543
             */
544 1
            if (TemplateTupleParameter tt = tp.isTemplateTupleParameter())
545
            {
546 1
                buf.writeByte('J');     // argument pack
547

548
                // mangle the rest of the arguments as types
549 1
                foreach (j; i .. (*ti.tiargs).dim)
550
                {
551 1
                    Type t = isType((*ti.tiargs)[j]);
552 1
                    assert(t);
553 1
                    t.accept(this);
554
                }
555

556 1
                buf.writeByte('E');
557 1
                break;
558
            }
559

560 1
            template_arg(ti, i);
561
        }
562 1
        buf.writeByte('E');
563 1
        return true;
564
    }
565

566
    /**
567
     * Write the symbol `p` if not null, then execute the delegate
568
     *
569
     * Params:
570
     *   p = Symbol to write
571
     *   dg = Delegate to execute
572
     */
573
    void writeChained(Dsymbol p, scope void delegate() dg)
574
    {
575 0
        if (p && !p.isModule())
576
        {
577 0
            buf.writestring("N");
578 0
            source_name(p, true);
579 0
            dg();
580 0
            buf.writestring("E");
581
        }
582
        else
583 0
            dg();
584
    }
585

586
    /**
587
     * Write the name of `s` to the buffer
588
     *
589
     * Params:
590
     *   s = Symbol to write the name of
591
     *   haveNE = Whether `N..E` is already part of the mangling
592
     *            Because `Nspace` and `CPPNamespaceAttribute` can be
593
     *            mixed, this is a mandatory hack.
594
     */
595
    void source_name(Dsymbol s, bool haveNE = false)
596
    {
597
        version (none)
598
        {
599
            printf("source_name(%s)\n", s.toChars());
600
            auto sl = this.buf.peekSlice();
601
            assert(sl.length == 0 || haveNE || s.cppnamespace is null || sl != "_ZN");
602
        }
603 1
        if (TemplateInstance ti = s.isTemplateInstance())
604
        {
605 1
            bool needsTa = false;
606

607
            // https://issues.dlang.org/show_bug.cgi?id=20413
608
            // N..E is not needed when substituting members of the std namespace.
609
            // This is observed in the GCC and Clang implementations.
610
            // The Itanium specification is not clear enough on this specific case.
611
            // References:
612
            //   https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.name
613
            //   https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression
614 1
            Dsymbol q = getQualifier(ti.tempdecl);
615 1
            Dsymbol ns = ti.tempdecl.cppnamespace;
616 1
            const inStd = ns && isStd(ns) || q && isStd(q);
617 1
            const isNested = !inStd && (ns || q);
618

619 1
            if (substitute(ti.tempdecl, !haveNE && isNested))
620
            {
621 1
                template_args(ti);
622 1
                if (!haveNE && isNested)
623 1
                    buf.writeByte('E');
624
            }
625 1
            else if (this.writeStdSubstitution(ti, needsTa))
626
            {
627 1
                this.abiTags.writeSymbol(ti, this);
628 1
                if (needsTa)
629 1
                    template_args(ti);
630
            }
631
            else
632
            {
633 1
                this.writeNamespace(
634
                    s.cppnamespace, () {
635 1
                        this.writeIdentifier(ti.tempdecl.toAlias().ident);
636 1
                        append(ti.tempdecl);
637 1
                        this.abiTags.writeSymbol(ti.tempdecl, this);
638 1
                        template_args(ti);
639
                    }, haveNE);
640
            }
641
        }
642
        else
643 1
            this.writeNamespace(s.cppnamespace, () {
644 1
                    this.writeIdentifier(s.ident);
645 1
                    this.abiTags.writeSymbol(s, this);
646
                },
647
                haveNE);
648
    }
649

650
    /********
651
     * See if s is actually an instance of a template
652
     * Params:
653
     *  s = symbol
654
     * Returns:
655
     *  if s is instance of a template, return the instance, otherwise return s
656
     */
657
    static Dsymbol getInstance(Dsymbol s)
658
    {
659 1
        Dsymbol p = s.toParent();
660 1
        if (p)
661
        {
662 1
            if (TemplateInstance ti = p.isTemplateInstance())
663 1
                return ti;
664
        }
665 1
        return s;
666
    }
667

668
    /// Get the namespace of a template instance
669
    CPPNamespaceDeclaration getTiNamespace(TemplateInstance ti)
670
    {
671
        // If we receive a pre-semantic `TemplateInstance`,
672
        // `cppnamespace` is always `null`
673 1
        return ti.tempdecl ? ti.cppnamespace
674 1
            : this.context.res.asType().toDsymbol(null).cppnamespace;
675
    }
676

677
    /********
678
     * Get qualifier for `s`, meaning the symbol
679
     * that s is in the symbol table of.
680
     * The module does not count as a qualifier, because C++
681
     * does not have modules.
682
     * Params:
683
     *  s = symbol that may have a qualifier
684
     *      s is rewritten to be TemplateInstance if s is one
685
     * Returns:
686
     *  qualifier, null if none
687
     */
688
    static Dsymbol getQualifier(Dsymbol s)
689
    {
690 1
        Dsymbol p = s.toParent();
691 1
        return (p && !p.isModule()) ? p : null;
692
    }
693

694
    // Detect type char
695
    static bool isChar(RootObject o)
696
    {
697 1
        Type t = isType(o);
698 1
        return (t && t.equals(Type.tchar));
699
    }
700

701
    // Detect type ::std::char_traits<char>
702
    bool isChar_traits_char(RootObject o)
703
    {
704 1
        return isIdent_char(Id.char_traits, o);
705
    }
706

707
    // Detect type ::std::allocator<char>
708
    bool isAllocator_char(RootObject o)
709
    {
710 1
        return isIdent_char(Id.allocator, o);
711
    }
712

713
    // Detect type ::std::ident<char>
714
    bool isIdent_char(Identifier ident, RootObject o)
715
    {
716 1
        Type t = isType(o);
717 1
        if (!t || t.ty != Tstruct)
718 1
            return false;
719 1
        Dsymbol s = (cast(TypeStruct)t).toDsymbol(null);
720 1
        if (s.ident != ident)
721 0
            return false;
722 1
        Dsymbol p = s.toParent();
723 1
        if (!p)
724 0
            return false;
725 1
        TemplateInstance ti = p.isTemplateInstance();
726 1
        if (!ti)
727 0
            return false;
728 1
        Dsymbol q = getQualifier(ti);
729 1
        const bool inStd = isStd(q) || isStd(this.getTiNamespace(ti));
730 1
        return inStd && ti.tiargs.dim == 1 && isChar((*ti.tiargs)[0]);
731
    }
732

733
    /***
734
     * Detect template args <char, ::std::char_traits<char>>
735
     * and write st if found.
736
     * Returns:
737
     *  true if found
738
     */
739
    bool char_std_char_traits_char(TemplateInstance ti, string st)
740
    {
741 1
        if (ti.tiargs.dim == 2 &&
742 1
            isChar((*ti.tiargs)[0]) &&
743 1
            isChar_traits_char((*ti.tiargs)[1]))
744
        {
745 1
            buf.writestring(st.ptr);
746 1
            return true;
747
        }
748 0
        return false;
749
    }
750

751

752
    void prefix_name(Dsymbol s)
753
    {
754
        //printf("prefix_name(%s)\n", s.toChars());
755 1
        if (substitute(s))
756 1
            return;
757 1
        if (isStd(s))
758 0
            return buf.writestring("St");
759

760 1
        auto si = getInstance(s);
761 1
        Dsymbol p = getQualifier(si);
762 1
        if (p)
763
        {
764 1
            if (isStd(p))
765
            {
766 1
                bool needsTa;
767 1
                auto ti = si.isTemplateInstance();
768 1
                if (this.writeStdSubstitution(ti, needsTa))
769
                {
770 1
                    this.abiTags.writeSymbol(ti, this);
771 1
                    if (needsTa)
772
                    {
773 1
                        template_args(ti);
774 1
                        append(ti);
775
                    }
776 1
                    return;
777
                }
778 1
                buf.writestring("St");
779
            }
780
            else
781 1
                prefix_name(p);
782
        }
783 1
        source_name(si, true);
784 1
        if (!isStd(si))
785
            /* Do this after the source_name() call to keep components[]
786
             * in the right order.
787
             * https://issues.dlang.org/show_bug.cgi?id=17947
788
             */
789 1
            append(si);
790
    }
791

792
    /**
793
     * Write common substitution for standard types, such as std::allocator
794
     *
795
     * This function assumes that the symbol `ti` is in the namespace `std`.
796
     *
797
     * Params:
798
     *   ti = Template instance to consider
799
     *   needsTa = If this function returns `true`, this value indicates
800
     *             if additional template argument mangling is needed
801
     *
802
     * Returns:
803
     *   `true` if a special std symbol was found
804
     */
805
    bool writeStdSubstitution(TemplateInstance ti, out bool needsTa)
806
    {
807 1
        if (!ti)
808 1
            return false;
809 1
        if (!isStd(this.getTiNamespace(ti)) && !isStd(getQualifier(ti)))
810 1
            return false;
811

812 1
        if (ti.name == Id.allocator)
813
        {
814 1
            buf.writestring("Sa");
815 1
            needsTa = true;
816 1
            return true;
817
        }
818 1
        if (ti.name == Id.basic_string)
819
        {
820
            // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
821 1
            if (ti.tiargs.dim == 3 &&
822 1
                isChar((*ti.tiargs)[0]) &&
823 1
                isChar_traits_char((*ti.tiargs)[1]) &&
824 1
                isAllocator_char((*ti.tiargs)[2]))
825

826
            {
827 1
                buf.writestring("Ss");
828 1
                return true;
829
            }
830 1
            buf.writestring("Sb");      // ::std::basic_string
831 1
            needsTa = true;
832 1
            return true;
833
        }
834

835
        // ::std::basic_istream<char, ::std::char_traits<char>>
836 1
        if (ti.name == Id.basic_istream &&
837 1
            char_std_char_traits_char(ti, "Si"))
838 1
            return true;
839

840
        // ::std::basic_ostream<char, ::std::char_traits<char>>
841 1
        if (ti.name == Id.basic_ostream &&
842 1
            char_std_char_traits_char(ti, "So"))
843 1
            return true;
844

845
        // ::std::basic_iostream<char, ::std::char_traits<char>>
846 1
        if (ti.name == Id.basic_iostream &&
847 1
            char_std_char_traits_char(ti, "Sd"))
848 1
            return true;
849

850 1
        return false;
851
    }
852

853

854
    void cpp_mangle_name(Dsymbol s, bool qualified)
855
    {
856
        //printf("cpp_mangle_name(%s, %d)\n", s.toChars(), qualified);
857 1
        Dsymbol p = s.toParent();
858 1
        Dsymbol se = s;
859 1
        bool write_prefix = true;
860 1
        if (p && p.isTemplateInstance())
861
        {
862 1
            se = p;
863 1
            if (find(p.isTemplateInstance().tempdecl) >= 0)
864 1
                write_prefix = false;
865 1
            p = p.toParent();
866
        }
867 1
        if (p && !p.isModule())
868
        {
869
            /* The N..E is not required if:
870
             * 1. the parent is 'std'
871
             * 2. 'std' is the initial qualifier
872
             * 3. there is no CV-qualifier or a ref-qualifier for a member function
873
             * ABI 5.1.8
874
             */
875 1
            if (isStd(p) && !qualified)
876
            {
877 1
                TemplateInstance ti = se.isTemplateInstance();
878 1
                if (s.ident == Id.allocator)
879
                {
880 1
                    buf.writestring("Sa"); // "Sa" is short for ::std::allocator
881 1
                    template_args(ti);
882
                }
883 1
                else if (s.ident == Id.basic_string)
884
                {
885
                    // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
886 1
                    if (ti.tiargs.dim == 3 &&
887 1
                        isChar((*ti.tiargs)[0]) &&
888 1
                        isChar_traits_char((*ti.tiargs)[1]) &&
889 1
                        isAllocator_char((*ti.tiargs)[2]))
890
                    {
891 1
                        buf.writestring("Ss");
892 1
                        return;
893
                    }
894 1
                    buf.writestring("Sb");      // ::std::basic_string
895 1
                    template_args(ti);
896
                }
897
                else
898
                {
899
                    // ::std::basic_istream<char, ::std::char_traits<char>>
900 1
                    if (s.ident == Id.basic_istream)
901
                    {
902 1
                        if (char_std_char_traits_char(ti, "Si"))
903 1
                            return;
904
                    }
905 1
                    else if (s.ident == Id.basic_ostream)
906
                    {
907 1
                        if (char_std_char_traits_char(ti, "So"))
908 1
                            return;
909
                    }
910 1
                    else if (s.ident == Id.basic_iostream)
911
                    {
912 1
                        if (char_std_char_traits_char(ti, "Sd"))
913 1
                            return;
914
                    }
915 1
                    buf.writestring("St");
916 1
                    source_name(se, true);
917
                }
918
            }
919
            else
920
            {
921 1
                buf.writeByte('N');
922 1
                if (write_prefix)
923
                {
924 1
                    if (isStd(p))
925 0
                        buf.writestring("St");
926
                    else
927 1
                        prefix_name(p);
928
                }
929 1
                source_name(se, true);
930 1
                buf.writeByte('E');
931
            }
932
        }
933
        else
934 1
            source_name(se, false);
935 1
        append(s);
936
    }
937

938
    /**
939
     * Write CV-qualifiers to the buffer
940
     *
941
     * CV-qualifiers are 'r': restrict (unused in D), 'V': volatile, 'K': const
942
     *
943
     * See_Also:
944
     *   https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.CV-qualifiers
945
     */
946
    void CV_qualifiers(const Type t)
947
    {
948 1
        if (t.isConst())
949 1
            buf.writeByte('K');
950
    }
951

952
    /**
953
     * Mangles a variable
954
     *
955
     * Params:
956
     *   d = Variable declaration to mangle
957
     *   isNested = Whether this variable is nested, e.g. a template parameter
958
     *              or within a namespace
959
     */
960
    void mangle_variable(VarDeclaration d, bool isNested)
961
    {
962
        // fake mangling for fields to fix https://issues.dlang.org/show_bug.cgi?id=16525
963 1
        if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared)))
964
        {
965 0
            d.error("Internal Compiler Error: C++ static non-`__gshared` non-`extern` variables not supported");
966 0
            fatal();
967
        }
968 1
        Dsymbol p = d.toParent();
969 1
        if (p && !p.isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE"
970
        {
971 1
            buf.writestring("_ZN");
972 1
            prefix_name(p);
973 1
            source_name(d, true);
974 1
            buf.writeByte('E');
975
        }
976 1
        else if (isNested)
977
        {
978 1
            buf.writestring("_Z");
979 1
            source_name(d, false);
980
        }
981
        else
982
        {
983 1
            if (auto varTags = ABITagContainer.forSymbol(d))
984
            {
985 1
                buf.writestring("_Z");
986 1
                source_name(d, false);
987 1
                return;
988
            }
989 1
            if (auto typeTags = ABITagContainer.forSymbol(d.type.toDsymbol(null)))
990
            {
991 1
                buf.writestring("_Z");
992 1
                source_name(d, false);
993 1
                this.abiTags.write(*this.buf, typeTags);
994 1
                return;
995
            }
996
            //char beta[6] should mangle as "beta"
997 1
            buf.writestring(d.ident.toString());
998
        }
999
    }
1000

1001
    void mangle_function(FuncDeclaration d)
1002
    {
1003
        //printf("mangle_function(%s)\n", d.toChars());
1004
        /*
1005
         * <mangled-name> ::= _Z <encoding>
1006
         * <encoding> ::= <function name> <bare-function-type>
1007
         *            ::= <data name>
1008
         *            ::= <special-name>
1009
         */
1010 1
        TypeFunction tf = cast(TypeFunction)d.type;
1011 1
        buf.writestring("_Z");
1012

1013 1
        if (TemplateDeclaration ftd = getFuncTemplateDecl(d))
1014
        {
1015
            /* It's an instance of a function template
1016
             */
1017 1
            TemplateInstance ti = d.parent.isTemplateInstance();
1018 1
            assert(ti);
1019 1
            this.mangleTemplatedFunction(d, tf, ftd, ti);
1020
        }
1021
        else
1022
        {
1023 1
            Dsymbol p = d.toParent();
1024 1
            if (p && !p.isModule() && tf.linkage == LINK.cpp)
1025
            {
1026 1
                this.mangleNestedFuncPrefix(tf, p);
1027

1028 1
                if (auto ctor = d.isCtorDeclaration())
1029 1
                    buf.writestring(ctor.isCpCtor ? "C2" : "C1");
1030 1
                else if (d.isPrimaryDtor())
1031 1
                    buf.writestring("D1");
1032 1
                else if (d.ident && d.ident == Id.assign)
1033 1
                    buf.writestring("aS");
1034 1
                else if (d.ident && d.ident == Id.eq)
1035 1
                    buf.writestring("eq");
1036 1
                else if (d.ident && d.ident == Id.index)
1037 1
                    buf.writestring("ix");
1038 1
                else if (d.ident && d.ident == Id.call)
1039 1
                    buf.writestring("cl");
1040
                else
1041 1
                    source_name(d, true);
1042 1
                buf.writeByte('E');
1043
            }
1044
            else
1045
            {
1046 1
                source_name(d, false);
1047
            }
1048

1049
            // Save offset for potentially writing tags
1050 1
            const size_t off = this.buf.length();
1051

1052
            // Template args accept extern "C" symbols with special mangling
1053 1
            if (tf.linkage == LINK.cpp)
1054 1
                mangleFunctionParameters(tf.parameterList);
1055

1056 1
            if (!tf.next.isTypeBasic())
1057 1
                this.writeRemainingTags(off, tf);
1058
        }
1059
    }
1060

1061
    /**
1062
     * Recursively mangles a non-scoped namespace
1063
     *
1064
     * Parameters:
1065
     *   ns = Namespace to mangle
1066
     *   dg = A delegate to write the identifier in this namespace
1067
     *   haveNE = When `false` (the default), surround the namespace / dg
1068
     *            call with nested name qualifier (`N..E`).
1069
     *            Otherwise, they are already present (e.g. `Nspace` was used).
1070
     */
1071
    void writeNamespace(CPPNamespaceDeclaration ns, scope void delegate() dg,
1072
                        bool haveNE = false)
1073
    {
1074 1
        void runDg () { if (dg !is null) dg(); }
1075

1076 1
        if (ns is null)
1077 1
            return runDg();
1078

1079 1
        if (isStd(ns))
1080
        {
1081 1
            if (!substitute(ns))
1082 1
                buf.writestring("St");
1083 1
            runDg();
1084
        }
1085 1
        else if (dg !is null)
1086
        {
1087 1
            if (!haveNE)
1088 1
                buf.writestring("N");
1089 1
            if (!substitute(ns))
1090
            {
1091 1
                this.writeNamespace(ns.cppnamespace, null);
1092 1
                this.writeIdentifier(ns.ident);
1093 1
                append(ns);
1094
            }
1095 1
            dg();
1096 1
            if (!haveNE)
1097 1
                buf.writestring("E");
1098
        }
1099 1
        else if (!substitute(ns))
1100
        {
1101 1
            this.writeNamespace(ns.cppnamespace, null);
1102 1
            this.writeIdentifier(ns.ident);
1103 1
            append(ns);
1104
        }
1105
    }
1106

1107
    /**
1108
     * Mangles a function template to C++
1109
     *
1110
     * Params:
1111
     *   d = Function declaration
1112
     *   tf = Function type (casted d.type)
1113
     *   ftd = Template declaration (ti.templdecl)
1114
     *   ti = Template instance (d.parent)
1115
     */
1116
    void mangleTemplatedFunction(FuncDeclaration d, TypeFunction tf,
1117
                                 TemplateDeclaration ftd, TemplateInstance ti)
1118
    {
1119 1
        Dsymbol p = ti.toParent();
1120
        // Check if this function is *not* nested
1121 1
        if (!p || p.isModule() || tf.linkage != LINK.cpp)
1122
        {
1123 1
            this.context.ti = ti;
1124 1
            this.context.fd = d;
1125 1
            this.context.res = d;
1126 1
            TypeFunction preSemantic = cast(TypeFunction)d.originalType;
1127 1
            auto nspace = ti.toParent();
1128 1
            if (nspace && nspace.isNspace())
1129 0
                this.writeChained(ti.toParent(), () => source_name(ti, true));
1130
            else
1131 1
                source_name(ti, false);
1132 1
            this.mangleReturnType(preSemantic);
1133 1
            this.mangleFunctionParameters(ParameterList(preSemantic.parameterList.parameters, tf.parameterList.varargs));
1134 1
            return;
1135
        }
1136

1137
        // It's a nested function (e.g. a member of an aggregate)
1138 1
        this.mangleNestedFuncPrefix(tf, p);
1139

1140 1
        if (d.isCtorDeclaration())
1141
        {
1142 0
            buf.writestring("C1");
1143
        }
1144 1
        else if (d.isPrimaryDtor())
1145
        {
1146 0
            buf.writestring("D1");
1147
        }
1148
        else
1149
        {
1150 1
            int firstTemplateArg = 0;
1151 1
            bool appendReturnType = true;
1152 1
            bool isConvertFunc = false;
1153 1
            string symName;
1154

1155
            // test for special symbols
1156 1
            CppOperator whichOp = isCppOperator(ti.name);
1157 1
            final switch (whichOp)
1158
            {
1159 1
            case CppOperator.Unknown:
1160 1
                break;
1161 1
            case CppOperator.Cast:
1162 1
                symName = "cv";
1163 1
                firstTemplateArg = 1;
1164 1
                isConvertFunc = true;
1165 1
                appendReturnType = false;
1166 1
                break;
1167 0
            case CppOperator.Assign:
1168 0
                symName = "aS";
1169 0
                break;
1170 0
            case CppOperator.Eq:
1171 0
                symName = "eq";
1172 0
                break;
1173 0
            case CppOperator.Index:
1174 0
                symName = "ix";
1175 0
                break;
1176 0
            case CppOperator.Call:
1177 0
                symName = "cl";
1178 0
                break;
1179 1
            case CppOperator.Unary:
1180 1
            case CppOperator.Binary:
1181 1
            case CppOperator.OpAssign:
1182 1
                TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
1183 1
                assert(td);
1184 1
                assert(ti.tiargs.dim >= 1);
1185 1
                TemplateParameter tp = (*td.parameters)[0];
1186 1
                TemplateValueParameter tv = tp.isTemplateValueParameter();
1187 1
                if (!tv || !tv.valType.isString())
1188 0
                    break; // expecting a string argument to operators!
1189 1
                Expression exp = (*ti.tiargs)[0].isExpression();
1190 1
                StringExp str = exp.toStringExp();
1191 1
                switch (whichOp)
1192
                {
1193 1
                case CppOperator.Unary:
1194 1
                    switch (str.peekString())
1195
                    {
1196 1
                    case "*":   symName = "de"; goto continue_template;
1197 1
                    case "++":  symName = "pp"; goto continue_template;
1198 1
                    case "--":  symName = "mm"; goto continue_template;
1199 1
                    case "-":   symName = "ng"; goto continue_template;
1200 1
                    case "+":   symName = "ps"; goto continue_template;
1201 1
                    case "~":   symName = "co"; goto continue_template;
1202 0
                    default:    break;
1203
                    }
1204 0
                    break;
1205 1
                case CppOperator.Binary:
1206 1
                    switch (str.peekString())
1207
                    {
1208 1
                    case ">>":  symName = "rs"; goto continue_template;
1209 1
                    case "<<":  symName = "ls"; goto continue_template;
1210 1
                    case "*":   symName = "ml"; goto continue_template;
1211 1
                    case "-":   symName = "mi"; goto continue_template;
1212 1
                    case "+":   symName = "pl"; goto continue_template;
1213 1
                    case "&":   symName = "an"; goto continue_template;
1214 1
                    case "/":   symName = "dv"; goto continue_template;
1215 1
                    case "%":   symName = "rm"; goto continue_template;
1216 1
                    case "^":   symName = "eo"; goto continue_template;
1217 1
                    case "|":   symName = "or"; goto continue_template;
1218 0
                    default:    break;
1219
                    }
1220 0
                    break;
1221 1
                case CppOperator.OpAssign:
1222 1
                    switch (str.peekString())
1223
                    {
1224 1
                    case "*":   symName = "mL"; goto continue_template;
1225 1
                    case "+":   symName = "pL"; goto continue_template;
1226 1
                    case "-":   symName = "mI"; goto continue_template;
1227 1
                    case "/":   symName = "dV"; goto continue_template;
1228 1
                    case "%":   symName = "rM"; goto continue_template;
1229 1
                    case ">>":  symName = "rS"; goto continue_template;
1230 1
                    case "<<":  symName = "lS"; goto continue_template;
1231 1
                    case "&":   symName = "aN"; goto continue_template;
1232 1
                    case "|":   symName = "oR"; goto continue_template;
1233 1
                    case "^":   symName = "eO"; goto continue_template;
1234 0
                    default:    break;
1235
                    }
1236 0
                    break;
1237 0
                default:
1238 0
                    assert(0);
1239
                continue_template:
1240 1
                    firstTemplateArg = 1;
1241 1
                    break;
1242
                }
1243 1
                break;
1244
            }
1245 1
            if (symName.length == 0)
1246 1
                source_name(ti, true);
1247
            else
1248
            {
1249 1
                buf.writestring(symName);
1250 1
                if (isConvertFunc)
1251 1
                    template_arg(ti, 0);
1252 1
                appendReturnType = template_args(ti, firstTemplateArg) && appendReturnType;
1253
            }
1254 1
            buf.writeByte('E');
1255 1
            if (appendReturnType)
1256 1
                headOfType(tf.nextOf());  // mangle return type
1257
        }
1258 1
        mangleFunctionParameters(tf.parameterList);
1259
    }
1260

1261
    /**
1262
     * Mangle the parameters of a function
1263
     *
1264
     * For templated functions, `context.res` is set to the `FuncDeclaration`
1265
     *
1266
     * Params:
1267
     *   parameters = Array of `Parameter` to mangle
1268
     *   varargs = if != 0, this function has varargs parameters
1269
     */
1270
    void mangleFunctionParameters(ParameterList parameterList)
1271
    {
1272 1
        int numparams = 0;
1273

1274 1
        foreach (n, fparam; parameterList)
1275
        {
1276 1
            Type t = target.cpp.parameterType(fparam);
1277 1
            if (t.ty == Tsarray)
1278
            {
1279
                // Static arrays in D are passed by value; no counterpart in C++
1280 0
                .error(loc, "Internal Compiler Error: unable to pass static array `%s` to extern(C++) function, use pointer instead",
1281
                    t.toChars());
1282 0
                fatal();
1283
            }
1284 1
            auto prev = this.context.push({
1285 1
                    TypeFunction tf;
1286 1
                    if (isDsymbol(this.context.res))
1287 1
                        tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
1288
                    else
1289 1
                        tf = this.context.res.asType().isTypeFunction();
1290 1
                    assert(tf);
1291 1
                    return (*tf.parameterList.parameters)[n].type;
1292 1
                }());
1293 1
            scope (exit) this.context.pop(prev);
1294 1
            headOfType(t);
1295 1
            ++numparams;
1296
        }
1297

1298 1
        if (parameterList.varargs == VarArg.variadic)
1299 1
            buf.writeByte('z');
1300 1
        else if (!numparams)
1301 1
            buf.writeByte('v'); // encode (void) parameters
1302
    }
1303

1304
    /****** The rest is type mangling ************/
1305

1306
    void error(Type t)
1307
    {
1308 0
        const(char)* p;
1309 0
        if (t.isImmutable())
1310 0
            p = "`immutable` ";
1311 0
        else if (t.isShared())
1312 0
            p = "`shared` ";
1313
        else
1314 0
            p = "";
1315 0
        .error(loc, "Internal Compiler Error: %stype `%s` cannot be mapped to C++\n", p, t.toChars());
1316 0
        fatal(); //Fatal, because this error should be handled in frontend
1317
    }
1318

1319
    /****************************
1320
     * Mangle a type,
1321
     * treating it as a Head followed by a Tail.
1322
     * Params:
1323
     *  t = Head of a type
1324
     */
1325
    void headOfType(Type t)
1326
    {
1327 1
        if (t.ty == Tclass)
1328
        {
1329 1
            mangleTypeClass(cast(TypeClass)t, true);
1330
        }
1331
        else
1332
        {
1333
            // For value types, strip const/immutable/shared from the head of the type
1334 1
            auto prev = this.context.push(this.context.res.asType().mutableOf().unSharedOf());
1335 1
            scope (exit) this.context.pop(prev);
1336 1
            t.mutableOf().unSharedOf().accept(this);
1337
        }
1338
    }
1339

1340
    /******
1341
     * Write out 1 or 2 character basic type mangling.
1342
     * Handle const and substitutions.
1343
     * Params:
1344
     *  t = type to mangle
1345
     *  p = if not 0, then character prefix
1346
     *  c = mangling character
1347
     */
1348
    void writeBasicType(Type t, char p, char c)
1349
    {
1350
        // Only do substitutions for non-fundamental types.
1351 1
        if (!isFundamentalType(t) || t.isConst())
1352
        {
1353 1
            if (substitute(t))
1354 0
                return;
1355
            else
1356 1
                append(t);
1357
        }
1358 1
        CV_qualifiers(t);
1359 1
        if (p)
1360 1
            buf.writeByte(p);
1361 1
        buf.writeByte(c);
1362
    }
1363

1364

1365
    /****************
1366
     * Write structs and enums.
1367
     * Params:
1368
     *  t = TypeStruct or TypeEnum
1369
     */
1370
    void doSymbol(Type t)
1371
    {
1372 1
        if (substitute(t))
1373 0
            return;
1374 1
        CV_qualifiers(t);
1375

1376
        // Handle any target-specific struct types.
1377 1
        if (auto tm = target.cpp.typeMangle(t))
1378
        {
1379 0
            buf.writestring(tm);
1380
        }
1381
        else
1382
        {
1383 1
            Dsymbol s = t.toDsymbol(null);
1384 1
            Dsymbol p = s.toParent();
1385 1
            if (p && p.isTemplateInstance())
1386
            {
1387
                 /* https://issues.dlang.org/show_bug.cgi?id=17947
1388
                  * Substitute the template instance symbol, not the struct/enum symbol
1389
                  */
1390 1
                if (substitute(p))
1391 1
                    return;
1392
            }
1393 1
            if (!substitute(s))
1394 1
                cpp_mangle_name(s, false);
1395
        }
1396 1
        if (t.isConst())
1397 1
            append(t);
1398
    }
1399

1400

1401

1402
    /************************
1403
     * Mangle a class type.
1404
     * If it's the head, treat the initial pointer as a value type.
1405
     * Params:
1406
     *  t = class type
1407
     *  head = true for head of a type
1408
     */
1409
    void mangleTypeClass(TypeClass t, bool head)
1410
    {
1411 1
        if (t.isImmutable() || t.isShared())
1412 0
            return error(t);
1413

1414
        /* Mangle as a <pointer to><struct>
1415
         */
1416 1
        if (substitute(t))
1417 1
            return;
1418 1
        if (!head)
1419 1
            CV_qualifiers(t);
1420 1
        buf.writeByte('P');
1421

1422 1
        CV_qualifiers(t);
1423

1424
        {
1425 1
            Dsymbol s = t.toDsymbol(null);
1426 1
            Dsymbol p = s.toParent();
1427 1
            if (p && p.isTemplateInstance())
1428
            {
1429
                 /* https://issues.dlang.org/show_bug.cgi?id=17947
1430
                  * Substitute the template instance symbol, not the class symbol
1431
                  */
1432 1
                if (substitute(p))
1433 0
                    return;
1434
            }
1435
        }
1436

1437 1
        if (!substitute(t.sym))
1438
        {
1439 1
            cpp_mangle_name(t.sym, false);
1440
        }
1441 1
        if (t.isConst())
1442 1
            append(null);  // C++ would have an extra type here
1443 1
        append(t);
1444
    }
1445

1446
    /**
1447
     * Mangle the prefix of a nested (e.g. member) function
1448
     *
1449
     * Params:
1450
     *   tf = Type of the nested function
1451
     *   parent = Parent in which the function is nested
1452
     */
1453
    void mangleNestedFuncPrefix(TypeFunction tf, Dsymbol parent)
1454
    {
1455
        /* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
1456
         *               ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
1457
         */
1458 1
        buf.writeByte('N');
1459 1
        CV_qualifiers(tf);
1460

1461
        /* <prefix> ::= <prefix> <unqualified-name>
1462
         *          ::= <template-prefix> <template-args>
1463
         *          ::= <template-param>
1464
         *          ::= # empty
1465
         *          ::= <substitution>
1466
         *          ::= <prefix> <data-member-prefix>
1467
         */
1468 1
        prefix_name(parent);
1469
    }
1470

1471
    /**
1472
     * Helper function to write a `T..._` template index.
1473
     *
1474
     * Params:
1475
     *   idx   = Index of `param` in the template argument list
1476
     *   param = Template parameter to mangle
1477
     */
1478
    private void writeTemplateArgIndex(size_t idx, TemplateParameter param)
1479
    {
1480
        // expressions are mangled in <X..E>
1481 1
        if (param.isTemplateValueParameter())
1482 1
            buf.writeByte('X');
1483 1
        buf.writeByte('T');
1484 1
        writeSequenceFromIndex(idx);
1485 1
        buf.writeByte('_');
1486 1
        if (param.isTemplateValueParameter())
1487 1
            buf.writeByte('E');
1488
    }
1489

1490
    /**
1491
     * Given an array of template parameters and an identifier,
1492
     * returns the index of the identifier in that array.
1493
     *
1494
     * Params:
1495
     *   ident = Identifier for which substitution is attempted
1496
     *           (e.g. `void func(T)(T param)` => `T` from `T param`)
1497
     *   params = `TemplateParameters` of the enclosing symbol
1498
     *           (in the previous example, `func`'s template parameters)
1499
     *
1500
     * Returns:
1501
     *   The index of the identifier match in `params`,
1502
     *   or `params.length` if there wasn't any match.
1503
     */
1504
    private static size_t templateParamIndex(
1505
        const ref Identifier ident, TemplateParameters* params)
1506
    {
1507 1
        foreach (idx, param; *params)
1508 1
            if (param.ident == ident)
1509 1
                return idx;
1510 1
        return params.length;
1511
    }
1512

1513
    /**
1514
     * Given a template instance `t`, write its qualified name
1515
     * without the template parameter list
1516
     *
1517
     * Params:
1518
     *   t = Post-parsing `TemplateInstance` pointing to the symbol
1519
     *       to mangle (one level deep)
1520
     *   dg = Delegate to execute after writing the qualified symbol
1521
     *
1522
     */
1523
    private void writeQualified(TemplateInstance t, scope void delegate() dg)
1524
    {
1525 1
        auto type = isType(this.context.res);
1526 1
        if (!type)
1527
        {
1528 0
            this.writeIdentifier(t.name);
1529 0
            return dg();
1530
        }
1531 1
        auto sym1 = type.toDsymbol(null);
1532 1
        if (!sym1)
1533
        {
1534 0
            this.writeIdentifier(t.name);
1535 0
            return dg();
1536
        }
1537
        // Get the template instance
1538 1
        auto sym = getQualifier(sym1);
1539 1
        auto sym2 = getQualifier(sym);
1540 1
        if (sym2 && isStd(sym2)) // Nspace path
1541
        {
1542 0
            bool unused;
1543 0
            assert(sym.isTemplateInstance());
1544 0
            if (this.writeStdSubstitution(sym.isTemplateInstance(), unused))
1545 0
                return dg();
1546
            // std names don't require `N..E`
1547 0
            buf.writestring("St");
1548 0
            this.writeIdentifier(t.name);
1549 0
            this.append(t);
1550 0
            return dg();
1551
        }
1552 1
        else if (sym2)
1553
        {
1554 1
            buf.writestring("N");
1555 1
            if (!this.substitute(sym2))
1556 1
                sym2.accept(this);
1557
        }
1558 1
        this.writeNamespace(
1559
            sym1.cppnamespace, () {
1560 1
                this.writeIdentifier(t.name);
1561 1
                this.append(t);
1562 1
                dg();
1563
            });
1564 1
        if (sym2)
1565 1
            buf.writestring("E");
1566
    }
1567

1568
extern(C++):
1569

1570
    alias visit = Visitor.visit;
1571

1572
    override void visit(TypeNull t)
1573
    {
1574 1
        if (t.isImmutable() || t.isShared())
1575 0
            return error(t);
1576

1577 1
        writeBasicType(t, 'D', 'n');
1578
    }
1579

1580
    override void visit(TypeBasic t)
1581
    {
1582 1
        if (t.isImmutable() || t.isShared())
1583 0
            return error(t);
1584

1585
        // Handle any target-specific basic types.
1586 1
        if (auto tm = target.cpp.typeMangle(t))
1587
        {
1588
            // Only do substitutions for non-fundamental types.
1589 0
            if (!isFundamentalType(t) || t.isConst())
1590
            {
1591 0
                if (substitute(t))
1592 0
                    return;
1593
                else
1594 0
                    append(t);
1595
            }
1596 0
            CV_qualifiers(t);
1597 0
            buf.writestring(tm);
1598 0
            return;
1599
        }
1600

1601
        /* <builtin-type>:
1602
         * v        void
1603
         * w        wchar_t
1604
         * b        bool
1605
         * c        char
1606
         * a        signed char
1607
         * h        unsigned char
1608
         * s        short
1609
         * t        unsigned short
1610
         * i        int
1611
         * j        unsigned int
1612
         * l        long
1613
         * m        unsigned long
1614
         * x        long long, __int64
1615
         * y        unsigned long long, __int64
1616
         * n        __int128
1617
         * o        unsigned __int128
1618
         * f        float
1619
         * d        double
1620
         * e        long double, __float80
1621
         * g        __float128
1622
         * z        ellipsis
1623
         * Dd       64 bit IEEE 754r decimal floating point
1624
         * De       128 bit IEEE 754r decimal floating point
1625
         * Df       32 bit IEEE 754r decimal floating point
1626
         * Dh       16 bit IEEE 754r half-precision floating point
1627
         * Di       char32_t
1628
         * Ds       char16_t
1629
         * u <source-name>  # vendor extended type
1630
         */
1631 1
        char c;
1632 1
        char p = 0;
1633 1
        switch (t.ty)
1634
        {
1635 1
            case Tvoid:                 c = 'v';        break;
1636 1
            case Tint8:                 c = 'a';        break;
1637 1
            case Tuns8:                 c = 'h';        break;
1638 1
            case Tint16:                c = 's';        break;
1639 1
            case Tuns16:                c = 't';        break;
1640 1
            case Tint32:                c = 'i';        break;
1641 1
            case Tuns32:                c = 'j';        break;
1642 1
            case Tfloat32:              c = 'f';        break;
1643 1
            case Tint64:
1644 1
                c = target.c.longsize == 8 ? 'l' : 'x';
1645 1
                break;
1646 1
            case Tuns64:
1647 1
                c = target.c.longsize == 8 ? 'm' : 'y';
1648 1
                break;
1649 0
            case Tint128:                c = 'n';       break;
1650 0
            case Tuns128:                c = 'o';       break;
1651 1
            case Tfloat64:               c = 'd';       break;
1652 1
            case Tfloat80:               c = 'e';       break;
1653 1
            case Tbool:                  c = 'b';       break;
1654 1
            case Tchar:                  c = 'c';       break;
1655 1
            case Twchar:        p = 'D'; c = 's';       break;  // since C++11
1656 1
            case Tdchar:        p = 'D'; c = 'i';       break;  // since C++11
1657 0
            case Timaginary32:  p = 'G'; c = 'f';       break;  // 'G' means imaginary
1658 0
            case Timaginary64:  p = 'G'; c = 'd';       break;
1659 0
            case Timaginary80:  p = 'G'; c = 'e';       break;
1660 0
            case Tcomplex32:    p = 'C'; c = 'f';       break;  // 'C' means complex
1661 0
            case Tcomplex64:    p = 'C'; c = 'd';       break;
1662 0
            case Tcomplex80:    p = 'C'; c = 'e';       break;
1663

1664 0
            default:
1665 0
                return error(t);
1666
        }
1667 1
        writeBasicType(t, p, c);
1668
    }
1669

1670
    override void visit(TypeVector t)
1671
    {
1672 0
        if (t.isImmutable() || t.isShared())
1673 0
            return error(t);
1674

1675 0
        if (substitute(t))
1676 0
            return;
1677 0
        append(t);
1678 0
        CV_qualifiers(t);
1679

1680
        // Handle any target-specific vector types.
1681 0
        if (auto tm = target.cpp.typeMangle(t))
1682
        {
1683 0
            buf.writestring(tm);
1684
        }
1685
        else
1686
        {
1687 0
            assert(t.basetype && t.basetype.ty == Tsarray);
1688 0
            assert((cast(TypeSArray)t.basetype).dim);
1689
            version (none)
1690
            {
1691
                buf.writestring("Dv");
1692
                buf.print((cast(TypeSArray *)t.basetype).dim.toInteger()); // -- Gnu ABI v.4
1693
                buf.writeByte('_');
1694
            }
1695
            else
1696 0
                buf.writestring("U8__vector"); //-- Gnu ABI v.3
1697 0
            t.basetype.nextOf().accept(this);
1698
        }
1699
    }
1700

1701
    override void visit(TypeSArray t)
1702
    {
1703 1
        if (t.isImmutable() || t.isShared())
1704 0
            return error(t);
1705

1706 1
        if (!substitute(t))
1707 1
            append(t);
1708 1
        CV_qualifiers(t);
1709 1
        buf.writeByte('A');
1710 1
        buf.print(t.dim ? t.dim.toInteger() : 0);
1711 1
        buf.writeByte('_');
1712 1
        t.next.accept(this);
1713
    }
1714

1715
    override void visit(TypePointer t)
1716
    {
1717 1
        if (t.isImmutable() || t.isShared())
1718 0
            return error(t);
1719

1720
        // Check for const - Since we cannot represent C++'s `char* const`,
1721
        // and `const char* const` (a.k.a `const(char*)` in D) is mangled
1722
        // the same as `const char*` (`const(char)*` in D), we need to add
1723
        // an extra `K` if `nextOf()` is `const`, before substitution
1724 1
        CV_qualifiers(t);
1725 1
        if (substitute(t))
1726 1
            return;
1727 1
        buf.writeByte('P');
1728 1
        auto prev = this.context.push(this.context.res.asType().nextOf());
1729 1
        scope (exit) this.context.pop(prev);
1730 1
        t.next.accept(this);
1731 1
        append(t);
1732
    }
1733

1734
    override void visit(TypeReference t)
1735
    {
1736 1
        if (substitute(t))
1737 1
            return;
1738 1
        buf.writeByte('R');
1739 1
        CV_qualifiers(t.nextOf());
1740 1
        headOfType(t.nextOf());
1741 1
        if (t.nextOf().isConst())
1742 1
            append(t.nextOf());
1743 1
        append(t);
1744
    }
1745

1746
    override void visit(TypeFunction t)
1747
    {
1748
        /*
1749
         *  <function-type> ::= F [Y] <bare-function-type> E
1750
         *  <bare-function-type> ::= <signature type>+
1751
         *  # types are possible return type, then parameter types
1752
         */
1753
        /* ABI says:
1754
            "The type of a non-static member function is considered to be different,
1755
            for the purposes of substitution, from the type of a namespace-scope or
1756
            static member function whose type appears similar. The types of two
1757
            non-static member functions are considered to be different, for the
1758
            purposes of substitution, if the functions are members of different
1759
            classes. In other words, for the purposes of substitution, the class of
1760
            which the function is a member is considered part of the type of
1761
            function."
1762

1763
            BUG: Right now, types of functions are never merged, so our simplistic
1764
            component matcher always finds them to be different.
1765
            We should use Type.equals on these, and use different
1766
            TypeFunctions for non-static member functions, and non-static
1767
            member functions of different classes.
1768
         */
1769 1
        if (substitute(t))
1770 1
            return;
1771 1
        buf.writeByte('F');
1772 1
        if (t.linkage == LINK.c)
1773 0
            buf.writeByte('Y');
1774 1
        Type tn = t.next;
1775 1
        if (t.isref)
1776 0
            tn = tn.referenceTo();
1777 1
        tn.accept(this);
1778 1
        mangleFunctionParameters(t.parameterList);
1779 1
        buf.writeByte('E');
1780 1
        append(t);
1781
    }
1782

1783
    override void visit(TypeStruct t)
1784
    {
1785 1
        if (t.isImmutable() || t.isShared())
1786 0
            return error(t);
1787
        //printf("TypeStruct %s\n", t.toChars());
1788 1
        doSymbol(t);
1789
    }
1790

1791
    override void visit(TypeEnum t)
1792
    {
1793 1
        if (t.isImmutable() || t.isShared())
1794 0
            return error(t);
1795

1796
        /* __c_(u)long(long) get special mangling
1797
         */
1798 1
        const id = t.sym.ident;
1799
        //printf("enum id = '%s'\n", id.toChars());
1800 1
        if (id == Id.__c_long)
1801 1
            return writeBasicType(t, 0, 'l');
1802 1
        else if (id == Id.__c_ulong)
1803 1
            return writeBasicType(t, 0, 'm');
1804 1
        else if (id == Id.__c_wchar_t)
1805 1
            return writeBasicType(t, 0, 'w');
1806 1
        else if (id == Id.__c_longlong)
1807 1
            return writeBasicType(t, 0, 'x');
1808 1
        else if (id == Id.__c_ulonglong)
1809 1
            return writeBasicType(t, 0, 'y');
1810

1811 1
        doSymbol(t);
1812
    }
1813

1814
    override void visit(TypeClass t)
1815
    {
1816 1
        mangleTypeClass(t, false);
1817
    }
1818

1819
    /**
1820
     * Performs template parameter substitution
1821
     *
1822
     * Mangling is performed on a copy of the post-parsing AST before
1823
     * any semantic pass is run.
1824
     * There is no easy way to link a type to the template parameters
1825
     * once semantic has run, because:
1826
     * - the `TemplateInstance` installs aliases in its scope to its params
1827
     * - `AliasDeclaration`s are resolved in many places
1828
     * - semantic passes are destructive, so the `TypeIdentifier` gets lost
1829
     *
1830
     * As a result, the best approach with the current architecture is to:
1831
     * - Run the visitor on the `originalType` of the function,
1832
     *   looking up any `TypeIdentifier` at the template scope when found.
1833
     * - Fallback to the post-semantic `TypeFunction` when the identifier is
1834
     *   not a template parameter.
1835
     */
1836
    override void visit(TypeIdentifier t)
1837
    {
1838 1
        auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl;
1839 1
        assert(decl.parameters !is null);
1840 1
        auto idx = templateParamIndex(t.ident, decl.parameters);
1841
        // If not found, default to the post-semantic type
1842 1
        if (idx >= decl.parameters.length)
1843 1
            return this.context.res.visitObject(this);
1844

1845 1
        auto param = (*decl.parameters)[idx];
1846 1
        if (auto type = this.context.res.isType())
1847 1
            CV_qualifiers(type);
1848
        // Otherwise, attempt substitution (`S_` takes precedence on `T_`)
1849 1
        if (this.substitute(param))
1850 1
            return;
1851

1852
        // If substitution failed, write `TX_` where `X` is the index
1853 1
        this.writeTemplateArgIndex(idx, param);
1854 1
        this.append(param);
1855
        // Write the ABI tags, if any
1856 1
        if (auto sym = this.context.res.isDsymbol())
1857 0
            this.abiTags.writeSymbol(sym, this);
1858
    }
1859

1860
    /// Ditto
1861
    override void visit(TypeInstance t)
1862
    {
1863 1
        assert(t.tempinst !is null);
1864 1
        t.tempinst.accept(this);
1865
    }
1866

1867
    /**
1868
     * Mangles a `TemplateInstance`
1869
     *
1870
     * A `TemplateInstance` can be found either in the parameter,
1871
     * or the return value.
1872
     * Arguments to the template instance needs to be mangled but the template
1873
     * can be partially substituted, so for example the following:
1874
     * `Container!(T, Val) func16479_12 (alias Container, T, int Val) ()`
1875
     * will mangle the return value part to "T_IT0_XT1_EE"
1876
     */
1877
    override void visit(TemplateInstance t)
1878
    {
1879
        // Template names are substituted, but args still need to be written
1880
        void writeArgs ()
1881
        {
1882 1
            buf.writeByte('I');
1883
            // When visiting the arguments, the context will be set to the
1884
            // resolved type
1885 1
            auto analyzed_ti = this.context.res.asType().toDsymbol(null).isInstantiated();
1886 1
            auto prev = this.context;
1887 1
            scope (exit) this.context.pop(prev);
1888 1
            foreach (idx, RootObject o; *t.tiargs)
1889
            {
1890 1
                this.context.res = (*analyzed_ti.tiargs)[idx];
1891 1
                o.visitObject(this);
1892
            }
1893 1
            if (analyzed_ti.tiargs.dim > t.tiargs.dim)
1894
            {
1895
                // If the resolved AST has more args than the parse one,
1896
                // we have default arguments
1897 1
                auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters;
1898 1
                foreach (idx, arg; (*oparams)[t.tiargs.dim .. $])
1899
                {
1900 1
                    this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.dim];
1901

1902 1
                    if (auto ttp = arg.isTemplateTypeParameter())
1903 1
                        ttp.defaultType.accept(this);
1904 0
                    else if (auto tvp = arg.isTemplateValueParameter())
1905 0
                        tvp.defaultValue.accept(this);
1906 0
                    else if (auto tvp = arg.isTemplateThisParameter())
1907 0
                        tvp.defaultType.accept(this);
1908 0
                    else if (auto tvp = arg.isTemplateAliasParameter())
1909 0
                        tvp.defaultAlias.visitObject(this);
1910
                    else
1911 0
                        assert(0, arg.toString());
1912
                }
1913
            }
1914 1
            buf.writeByte('E');
1915
        }
1916

1917
        // `name` is used, not `ident`
1918 1
        assert(t.name !is null);
1919 1
        assert(t.tiargs !is null);
1920

1921 1
        bool needsTa;
1922 1
        auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl;
1923
        // Attempt to substitute the template itself
1924 1
        auto idx = templateParamIndex(t.name, decl.parameters);
1925 1
        if (idx < decl.parameters.length)
1926
        {
1927 1
            auto param = (*decl.parameters)[idx];
1928 1
            if (auto type = t.getType())
1929 0
                CV_qualifiers(type);
1930 1
            if (this.substitute(param))
1931 0
                return;
1932 1
            this.writeTemplateArgIndex(idx, param);
1933 1
            this.append(param);
1934 1
            writeArgs();
1935
        }
1936 1
        else if (this.writeStdSubstitution(t, needsTa))
1937
        {
1938 1
            if (needsTa)
1939 1
                writeArgs();
1940
        }
1941 1
        else if (!this.substitute(t))
1942 1
            this.writeQualified(t, &writeArgs);
1943
    }
1944

1945
    /// Ditto
1946
    override void visit(IntegerExp t)
1947
    {
1948 1
        this.buf.writeByte('L');
1949 1
        t.type.accept(this);
1950 1
        this.buf.print(t.getInteger());
1951 1
        this.buf.writeByte('E');
1952
    }
1953

1954
    override void visit(Nspace t)
1955
    {
1956 1
        if (auto p = getQualifier(t))
1957 0
            p.accept(this);
1958

1959 1
        if (isStd(t))
1960 0
            buf.writestring("St");
1961
        else
1962
        {
1963 1
            this.writeIdentifier(t.ident);
1964 1
            this.append(t);
1965
        }
1966
    }
1967

1968
    override void visit(Type t)
1969
    {
1970 0
        error(t);
1971
    }
1972

1973
    void visit(Tuple t)
1974
    {
1975 0
        assert(0);
1976
    }
1977
}
1978

1979
/// Helper code to visit `RootObject`, as it doesn't define `accept`,
1980
/// only its direct subtypes do.
1981
private void visitObject(V : Visitor)(RootObject o, V this_)
1982
{
1983 1
    assert(o !is null);
1984 1
    if (Type ta = isType(o))
1985 1
        ta.accept(this_);
1986 1
    else if (Expression ea = isExpression(o))
1987 1
        ea.accept(this_);
1988 1
    else if (Dsymbol sa = isDsymbol(o))
1989 1
        sa.accept(this_);
1990 1
    else if (TemplateParameter t = isTemplateParameter(o))
1991 1
        t.accept(this_);
1992 0
    else if (Tuple t = isTuple(o))
1993
        // `Tuple` inherits `RootObject` and does not define accept
1994
        // For this reason, this uses static dispatch on the visitor
1995 0
        this_.visit(t);
1996
    else
1997 0
        assert(0, o.toString());
1998
}
1999

2000
/// Helper function to safely get a type out of a `RootObject`
2001
private Type asType(RootObject o)
2002
{
2003 1
    Type ta = isType(o);
2004 1
    assert(ta !is null, o.toString());
2005 1
    return ta;
2006
}
2007

2008
/// Helper function to safely get a `FuncDeclaration` out of a `RootObject`
2009
private FuncDeclaration asFuncDecl(RootObject o)
2010
{
2011 1
    Dsymbol d = isDsymbol(o);
2012 1
    assert(d !is null);
2013 1
    auto fd = d.isFuncDeclaration();
2014 1
    assert(fd !is null);
2015 1
    return fd;
2016
}
2017

2018
/// Helper class to compare entries in components
2019
private extern(C++) final class ComponentVisitor : Visitor
2020
{
2021
    /// Only one of the following is not `null`, it's always
2022
    /// the most specialized type, set from the ctor
2023
    private Nspace namespace;
2024

2025
    /// Ditto
2026
    private CPPNamespaceDeclaration namespace2;
2027

2028
    /// Ditto
2029
    private TypePointer tpointer;
2030

2031
    /// Ditto
2032
    private TypeReference tref;
2033

2034
    /// Ditto
2035
    private TypeIdentifier tident;
2036

2037
    /// Least specialized type
2038
    private RootObject object;
2039

2040
    /// Set to the result of the comparison
2041
    private bool result;
2042

2043 1
    public this(RootObject base)
2044
    {
2045 1
        switch (base.dyncast())
2046
        {
2047 1
        case DYNCAST.dsymbol:
2048 1
            if (auto ns = (cast(Dsymbol)base).isNspace())
2049 1
                this.namespace = ns;
2050 1
            else if (auto ns = (cast(Dsymbol)base).isCPPNamespaceDeclaration())
2051 1
                this.namespace2 = ns;
2052
            else
2053 1
                goto default;
2054 1
            break;
2055

2056 1
        case DYNCAST.type:
2057 1
            auto t = cast(Type)base;
2058 1
            if (t.ty == Tpointer)
2059 1
                this.tpointer = cast(TypePointer)t;
2060 1
            else if (t.ty == Treference)
2061 1
                this.tref = cast(TypeReference)t;
2062 1
            else if (t.ty == Tident)
2063 1
                this.tident = cast(TypeIdentifier)t;
2064
            else
2065 1
                goto default;
2066 1
            break;
2067

2068
        // Note: ABI tags are also handled here (they are TupleExp of StringExp)
2069 1
        default:
2070 1
            this.object = base;
2071
        }
2072
    }
2073

2074
    /// Introduce base class overloads
2075
    alias visit = Visitor.visit;
2076

2077
    /// Least specialized overload of each direct child of `RootObject`
2078
    public override void visit(Dsymbol o)
2079
    {
2080 1
        this.result = this.object && this.object == o;
2081
    }
2082

2083
    /// Ditto
2084
    public override void visit(Expression o)
2085
    {
2086 0
        this.result = this.object && this.object == o;
2087
    }
2088

2089
    /// Ditto
2090
    public void visit(Tuple o)
2091
    {
2092 0
        this.result = this.object && this.object == o;
2093
    }
2094

2095
    /// Ditto
2096
    public override void visit(Type o)
2097
    {
2098 1
        this.result = this.object && this.object == o;
2099
    }
2100

2101
    /// Ditto
2102
    public override void visit(TemplateParameter o)
2103
    {
2104 1
        this.result = this.object && this.object == o;
2105
    }
2106

2107
    /**
2108
     * This overload handles composed types including template parameters
2109
     *
2110
     * Components for substitutions include "next" type.
2111
     * For example, if `ref T` is present, `ref T` and `T` will be present
2112
     * in the substitution array.
2113
     * But since we don't have the final/merged type, we cannot rely on
2114
     * object comparison, and need to recurse instead.
2115
     */
2116
    public override void visit(TypeReference o)
2117
    {
2118 1
        if (!this.tref)
2119 1
            return;
2120 1
        if (this.tref == o)
2121 1
            this.result = true;
2122
        else
2123
        {
2124
            // It might be a reference to a template parameter that we already
2125
            // saw, so we need to recurse
2126 1
            scope v = new ComponentVisitor(this.tref.next);
2127 1
            o.next.visitObject(v);
2128 1
            this.result = v.result;
2129
        }
2130
    }
2131

2132
    /// Ditto
2133
    public override void visit(TypePointer o)
2134
    {
2135 1
        if (!this.tpointer)
2136 1
            return;
2137 1
        if (this.tpointer == o)
2138 1
            this.result = true;
2139
        else
2140
        {
2141
            // It might be a pointer to a template parameter that we already
2142
            // saw, so we need to recurse
2143 1
            scope v = new ComponentVisitor(this.tpointer.next);
2144 1
            o.next.visitObject(v);
2145 1
            this.result = v.result;
2146
        }
2147
    }
2148

2149
    /// Ditto
2150
    public override void visit(TypeIdentifier o)
2151
    {
2152
        /// Since we know they are at the same level, scope resolution will
2153
        /// give us the same symbol, thus we can just compare ident.
2154 1
        this.result = (this.tident && (this.tident.ident == o.ident));
2155
    }
2156

2157
    /**
2158
     * Overload which accepts a Namespace
2159
     *
2160
     * It is very common for large C++ projects to have multiple files sharing
2161
     * the same `namespace`. If any D project adopts the same approach
2162
     * (e.g. separating data structures from functions), it will lead to two
2163
     * `Nspace` objects being instantiated, with different addresses.
2164
     * At the same time, we cannot compare just any Dsymbol via identifier,
2165
     * because it messes with templates.
2166
     *
2167
     * See_Also:
2168
     *  https://issues.dlang.org/show_bug.cgi?id=18922
2169
     *
2170
     * Params:
2171
     *   ns = C++ namespace to do substitution for
2172
     */
2173
    public override void visit(Nspace ns)
2174
    {
2175 1
        this.result = isNamespaceEqual(this.namespace, ns)
2176 1
            || isNamespaceEqual(this.namespace2, ns);
2177
    }
2178

2179
    /// Ditto
2180
    public override void visit(CPPNamespaceDeclaration ns)
2181
    {
2182 1
        this.result = isNamespaceEqual(this.namespace, ns)
2183 1
            || isNamespaceEqual(this.namespace2, ns);
2184
    }
2185
}
2186

2187
/// Transitional functions for `CPPNamespaceDeclaration` / `Nspace`
2188
/// Remove when `Nspace` is removed.
2189
private bool isNamespaceEqual (Nspace a, Nspace b)
2190
{
2191 1
    if (a is null || b is null)
2192 1
        return false;
2193 1
    return a.equals(b);
2194
}
2195

2196
/// Ditto
2197
private bool isNamespaceEqual (Nspace a, CPPNamespaceDeclaration b)
2198
{
2199 1
    return isNamespaceEqual(b, a);
2200
}
2201

2202
/// Ditto
2203
private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = 0)
2204
{
2205 1
    if ((a is null) != (b is null))
2206 1
        return false;
2207 1
    if (!a.ident.equals(b.ident))
2208 1
        return false;
2209

2210
    // We need to see if there's more ident enclosing
2211 1
    if (auto pb = b.toParent().isNspace())
2212 1
        return isNamespaceEqual(a.cppnamespace, pb);
2213
    else
2214 1
        return a.cppnamespace is null;
2215
}
2216

2217
/// Returns:
2218
///   Whether  two `CPPNamespaceDeclaration` are equals
2219
private bool isNamespaceEqual (CPPNamespaceDeclaration a, CPPNamespaceDeclaration b)
2220
{
2221 1
    if (a is null || b is null)
2222 1
        return false;
2223

2224 1
    if ((a.cppnamespace is null) != (b.cppnamespace is null))
2225 1
        return false;
2226 1
    if (a.ident != b.ident)
2227 1
        return false;
2228 1
    return a.cppnamespace is null ? true : isNamespaceEqual(a.cppnamespace, b.cppnamespace);
2229
}
2230

2231
/**
2232
 * A container for ABI tags
2233
 *
2234
 * At its hearth, there is a sorted array of ABI tags having been written
2235
 * already. ABI tags can be present on parameters, template parameters,
2236
 * return value, and varaible. ABI tags for a given type needs to be written
2237
 * sorted. When a function returns a type that has ABI tags, only the tags that
2238
 * haven't been printed as part of the mangling (e.g. arguments) are written
2239
 * directly after the function name.
2240
 *
2241
 * This means that:
2242
 * ---
2243
 * /++ C++ type definitions:
2244
 * struct [[gnu::abi_tag("tag1")]] Struct1 {};
2245
 * struct [[gnu::abi_tag("tag2")]] Struct2 {};
2246
 * // Can also be: "tag2", "tag1", since tags are sorted.
2247
 * struct [[gnu::abi_tag("tag1", "tag2")]] Struct3 {};
2248
 * +/
2249
 * // Functions definitions:
2250
 * Struct3 func1 (Struct1);
2251
 * Struct3 func2 (Struct2);
2252
 * Struct3 func3 (Struct2, Struct1);
2253
 * ---
2254
 * Will be respectively pseudo-mangled (part of interest between stars) as:
2255
 * "_Z4 func1 *B4tag2* ParamsMangling" (ParamsMangling includes tag1),
2256
 * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes tag2),
2257
 * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes both).
2258
 *
2259
 * This is why why need to keep a list of tags that were written,
2260
 * and insert the missing one after parameter mangling has been written.
2261
 * Since there's a lot of operations that are not easily doable in DMD
2262
 * (since we can't use Phobos), this special container is implemented.
2263
 */
2264
private struct ABITagContainer
2265
{
2266
    private Array!StringExp written;
2267

2268
    static ArrayLiteralExp forSymbol (Dsymbol s)
2269
    {
2270 1
        if (!s)
2271 1
            return null;
2272
        // If this is a template instance, we want the declaration,
2273
        // as that's where the UDAs are
2274 1
        if (auto ti = s.isTemplateInstance())
2275 1
            s = ti.tempdecl;
2276 1
        if (!s.userAttribDecl || !s.userAttribDecl.atts)
2277 1
            return null;
2278

2279 1
        foreach (exp; *s.userAttribDecl.atts)
2280
        {
2281 1
            if (UserAttributeDeclaration.isGNUABITag(exp))
2282 1
                return (*exp.isStructLiteralExp().elements)[0]
2283
                    .isArrayLiteralExp();
2284
        }
2285 1
        return null;
2286
    }
2287

2288
    void writeSymbol(Dsymbol s, CppMangleVisitor self)
2289
    {
2290 1
        auto tale = forSymbol(s);
2291 1
        if (!tale) return;
2292 1
        if (self.substitute(tale))
2293 0
            return;
2294 1
        this.write(*self.buf, tale);
2295
    }
2296

2297
    /**
2298
     * Write an ArrayLiteralExp (expected to be an ABI tag) to the buffer
2299
     *
2300
     * Params:
2301
     *   buf = Buffer to write mangling to
2302
     *   ale = GNU ABI tag array literal expression, semantically analyzed
2303
     */
2304
    void write (ref OutBuffer buf, ArrayLiteralExp ale, bool skipKnown = false)
2305
    {
2306
        void writeElem (StringExp exp)
2307
        {
2308 1
            const tag = exp.peekString();
2309 1
            buf.writestring("B");
2310 1
            buf.print(tag.length);
2311 1
            buf.writestring(tag);
2312
        }
2313

2314 1
        bool match;
2315 1
        foreach (exp; *ale.elements)
2316
        {
2317 1
            auto elem = exp.toStringExp();
2318 1
            auto idx = closestIndex(this.written[], elem, match);
2319 1
            if (!match)
2320
            {
2321 1
                writeElem(elem);
2322 1
                this.written.insert(idx, elem);
2323
            }
2324 1
            else if (!skipKnown)
2325 1
                writeElem(elem);
2326
        }
2327
    }
2328
}
2329

2330
/**
2331
 * Returns the closest index to to `exp` in `slice`
2332
 *
2333
 * Performs a binary search on `slice` (assumes `slice` is sorted),
2334
 * and returns either `exp`'s index in `slice` if `exact` is `true`,
2335
 * or the index at which `exp` can be inserted in `slice` if `exact is `false`.
2336
 * Inserting `exp` at the return value will keep the array sorted.
2337
 *
2338
 * Params:
2339
 *   slice = The sorted slice to search into
2340
 *   exp   = The string expression to search for
2341
 *   exact = If `true` on return, `exp` was found in `slice`
2342
 *
2343
 * Returns:
2344
 *   Either the index to insert `exp` at (if `exact == false`),
2345
 *   or the index of `exp` in `slice`.
2346
 */
2347
private size_t closestIndex (const(StringExp)[] slice, StringExp exp, out bool exact)
2348
{
2349 1
    if (!slice.length) return 0;
2350

2351 1
    const StringExp* first = slice.ptr;
2352 1
    while (true)
2353
    {
2354 1
        int res = dstrcmp(exp.peekString(), slice[$ / 2].peekString());
2355 1
        if (res == 0)
2356
        {
2357 1
            exact = true;
2358 1
            return (&slice[$/2] - first);
2359
        }
2360

2361 1
        if (slice.length == 1)
2362 1
            return (slice.ptr - first) + (res > 0);
2363 1
        slice = slice[(res > 0 ? $ / 2 : 0) .. (res > 0 ? $ : $ / 2)];
2364
    }
2365
}
2366

2367
//
2368
unittest
2369
{
2370
    bool match;
2371
    auto s1 = new StringExp(Loc.initial, "Amande");
2372
    auto s2 = new StringExp(Loc.initial, "Baguette");
2373
    auto s3 = new StringExp(Loc.initial, "Croissant");
2374
    auto s4 = new StringExp(Loc.initial, "Framboises");
2375
    auto s5 = new StringExp(Loc.initial, "Proscuitto");
2376

2377
    // Found, odd size
2378
    assert(closestIndex([s1, s2, s3, s4, s5], s1, match) == 0 && match);
2379
    assert(closestIndex([s1, s2, s3, s4, s5], s2, match) == 1 && match);
2380
    assert(closestIndex([s1, s2, s3, s4, s5], s3, match) == 2 && match);
2381
    assert(closestIndex([s1, s2, s3, s4, s5], s4, match) == 3 && match);
2382
    assert(closestIndex([s1, s2, s3, s4, s5], s5, match) == 4 && match);
2383

2384
    // Not found, even size
2385
    assert(closestIndex([s2, s3, s4, s5], s1, match) == 0 && !match);
2386
    assert(closestIndex([s1, s3, s4, s5], s2, match) == 1 && !match);
2387
    assert(closestIndex([s1, s2, s4, s5], s3, match) == 2 && !match);
2388
    assert(closestIndex([s1, s2, s3, s5], s4, match) == 3 && !match);
2389
    assert(closestIndex([s1, s2, s3, s4], s5, match) == 4 && !match);
2390

2391
    // Found, even size
2392
    assert(closestIndex([s1, s2, s3, s4], s1, match) == 0 && match);
2393
    assert(closestIndex([s1, s2, s3, s4], s2, match) == 1 && match);
2394
    assert(closestIndex([s1, s2, s3, s4], s3, match) == 2 && match);
2395
    assert(closestIndex([s1, s2, s3, s4], s4, match) == 3 && match);
2396
    assert(closestIndex([s1, s3, s4, s5], s5, match) == 3 && match);
2397

2398
    // Not found, odd size
2399
    assert(closestIndex([s2, s4, s5], s1, match) == 0 && !match);
2400
    assert(closestIndex([s1, s4, s5], s2, match) == 1 && !match);
2401
    assert(closestIndex([s1, s2, s4], s3, match) == 2 && !match);
2402
    assert(closestIndex([s1, s3, s5], s4, match) == 2 && !match);
2403
    assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match);
2404
}
2405

2406
/**
2407
 * Visits the return type of a function and writes leftover ABI tags
2408
 */
2409
extern(C++) private final class LeftoverVisitor : Visitor
2410
{
2411
    /// List of tags to write
2412
    private Array!StringExp toWrite;
2413
    /// List of tags to ignore
2414
    private const(Array!StringExp)* ignore;
2415

2416
    ///
2417 1
    public this(const(Array!StringExp)* previous)
2418
    {
2419 1
        this.ignore = previous;
2420
    }
2421

2422
    /// Reintroduce base class overloads
2423
    public alias visit = Visitor.visit;
2424

2425
    /// Least specialized overload of each direct child of `RootObject`
2426
    public override void visit(Dsymbol o)
2427
    {
2428 1
        auto ale = ABITagContainer.forSymbol(o);
2429 1
        if (!ale) return;
2430

2431 1
        bool match;
2432 1
        foreach (elem; *ale.elements)
2433
        {
2434 1
            auto se = elem.toStringExp();
2435 1
            closestIndex((*this.ignore)[], se, match);
2436 1
            if (match) continue;
2437 1
            auto idx = closestIndex(this.toWrite[], se, match);
2438 1
            if (!match)
2439 1
                this.toWrite.insert(idx, se);
2440
        }
2441
    }
2442

2443
    /// Ditto
2444
    public override void visit(Type o)
2445
    {
2446 1
        if (auto sym = o.toDsymbol(null))
2447 1
            sym.accept(this);
2448
    }
2449

2450
    /// Composite type
2451
    public override void visit(TypePointer o)
2452
    {
2453 1
        o.next.accept(this);
2454
    }
2455

2456
    public override void visit(TypeReference o)
2457
    {
2458 0
        o.next.accept(this);
2459
    }
2460
}

Read our documentation on viewing source code .

Loading