1
/**
2
 * Handle introspection functionality of the `__traits()` construct.
3
 *
4
 * Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits)
5
 *
6
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/traits.d, _traits.d)
10
 * Documentation:  https://dlang.org/phobos/dmd_traits.html
11
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/traits.d
12
 */
13

14
module dmd.traits;
15

16
import core.stdc.stdio;
17

18
import dmd.aggregate;
19
import dmd.arraytypes;
20
import dmd.astcodegen;
21
import dmd.attrib;
22
import dmd.canthrow;
23
import dmd.dclass;
24
import dmd.declaration;
25
import dmd.denum;
26
import dmd.dimport;
27
import dmd.dmodule;
28
import dmd.dscope;
29
import dmd.dsymbol;
30
import dmd.dsymbolsem;
31
import dmd.dtemplate;
32
import dmd.errors;
33
import dmd.expression;
34
import dmd.expressionsem;
35
import dmd.func;
36
import dmd.globals;
37
import dmd.hdrgen;
38
import dmd.id;
39
import dmd.identifier;
40
import dmd.mtype;
41
import dmd.nogc;
42
import dmd.parse;
43
import dmd.root.array;
44
import dmd.root.speller;
45
import dmd.root.stringtable;
46
import dmd.target;
47
import dmd.tokens;
48
import dmd.typesem;
49
import dmd.visitor;
50
import dmd.root.rootobject;
51
import dmd.root.outbuffer;
52
import dmd.root.string;
53

54
enum LOGSEMANTIC = false;
55

56
/************************ TraitsExp ************************************/
57

58
/**************************************
59
 * Convert `Expression` or `Type` to corresponding `Dsymbol`, additionally
60
 * stripping off expression contexts.
61
 *
62
 * Some symbol related `__traits` ignore arguments expression contexts.
63
 * For example:
64
 * ----
65
 *  struct S { void f() {} }
66
 *  S s;
67
 *  pragma(msg, __traits(isNested, s.f));
68
 *  // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`.
69
 * ----
70
 *
71
 * This is used for that common `__traits` behavior.
72
 *
73
 * Input:
74
 *      oarg     object to get the symbol for
75
 * Returns:
76
 *      Dsymbol  the corresponding symbol for oarg
77
 */
78
private Dsymbol getDsymbolWithoutExpCtx(RootObject oarg)
79
{
80 1
    if (auto e = isExpression(oarg))
81
    {
82 1
        if (e.op == TOK.dotVariable)
83 1
            return (cast(DotVarExp)e).var;
84 1
        if (e.op == TOK.dotTemplateDeclaration)
85 0
            return (cast(DotTemplateExp)e).td;
86
    }
87 1
    return getDsymbol(oarg);
88
}
89

90
private const StringTable!bool traitsStringTable;
91

92
shared static this()
93
{
94
    static immutable string[] names =
95
    [
96
        "isAbstractClass",
97
        "isArithmetic",
98
        "isAssociativeArray",
99
        "isDisabled",
100
        "isDeprecated",
101
        "isFuture",
102
        "isFinalClass",
103
        "isPOD",
104
        "isNested",
105
        "isFloating",
106
        "isIntegral",
107
        "isScalar",
108
        "isStaticArray",
109
        "isUnsigned",
110
        "isVirtualFunction",
111
        "isVirtualMethod",
112
        "isAbstractFunction",
113
        "isFinalFunction",
114
        "isOverrideFunction",
115
        "isStaticFunction",
116
        "isModule",
117
        "isPackage",
118
        "isRef",
119
        "isOut",
120
        "isLazy",
121
        "isReturnOnStack",
122
        "hasMember",
123
        "identifier",
124
        "getProtection",
125
        "parent",
126
        "child",
127
        "getLinkage",
128
        "getMember",
129
        "getOverloads",
130
        "getVirtualFunctions",
131
        "getVirtualMethods",
132
        "classInstanceSize",
133
        "allMembers",
134
        "derivedMembers",
135
        "isSame",
136
        "compiles",
137
        "parameters",
138
        "getAliasThis",
139
        "getAttributes",
140
        "getFunctionAttributes",
141
        "getFunctionVariadicStyle",
142
        "getParameterStorageClasses",
143
        "getUnitTests",
144
        "getVirtualIndex",
145
        "getPointerBitmap",
146
        "isZeroInit",
147
        "getTargetInfo",
148
        "getLocation",
149
        "hasPostblit",
150
        "hasCopyConstructor",
151
        "isCopyable",
152
    ];
153

154 1
    StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable;
155 1
    stringTable._init(names.length);
156

157 1
    foreach (s; names)
158
    {
159 1
        auto sv = stringTable.insert(s, true);
160 1
        assert(sv);
161
    }
162
}
163

164
/**
165
 * get an array of size_t values that indicate possible pointer words in memory
166
 *  if interpreted as the type given as argument
167
 * Returns: the size of the type in bytes, d_uns64.max on error
168
 */
169
d_uns64 getTypePointerBitmap(Loc loc, Type t, Array!(d_uns64)* data)
170
{
171 1
    d_uns64 sz;
172 1
    if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration())
173 1
        sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(loc);
174
    else
175 1
        sz = t.size(loc);
176 1
    if (sz == SIZE_INVALID)
177 0
        return d_uns64.max;
178

179 1
    const sz_size_t = Type.tsize_t.size(loc);
180 1
    if (sz > sz.max - sz_size_t)
181
    {
182 0
        error(loc, "size overflow for type `%s`", t.toChars());
183 0
        return d_uns64.max;
184
    }
185

186 1
    d_uns64 bitsPerWord = sz_size_t * 8;
187 1
    d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t;
188 1
    d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord;
189

190 1
    data.setDim(cast(size_t)cntdata);
191 1
    data.zero();
192

193
    extern (C++) final class PointerBitmapVisitor : Visitor
194
    {
195
        alias visit = Visitor.visit;
196
    public:
197 1
        extern (D) this(Array!(d_uns64)* _data, d_uns64 _sz_size_t)
198
        {
199 1
            this.data = _data;
200 1
            this.sz_size_t = _sz_size_t;
201
        }
202

203
        void setpointer(d_uns64 off)
204
        {
205 1
            d_uns64 ptroff = off / sz_size_t;
206 1
            (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t));
207
        }
208

209
        override void visit(Type t)
210
        {
211 1
            Type tb = t.toBasetype();
212 1
            if (tb != t)
213 1
                tb.accept(this);
214
        }
215

216
        override void visit(TypeError t)
217
        {
218 0
            visit(cast(Type)t);
219
        }
220

221
        override void visit(TypeNext t)
222
        {
223 0
            assert(0);
224
        }
225

226
        override void visit(TypeBasic t)
227
        {
228 1
            if (t.ty == Tvoid)
229 1
                setpointer(offset);
230
        }
231

232
        override void visit(TypeVector t)
233
        {
234
        }
235

236
        override void visit(TypeArray t)
237
        {
238 0
            assert(0);
239
        }
240

241
        override void visit(TypeSArray t)
242
        {
243 1
            d_uns64 arrayoff = offset;
244 1
            d_uns64 nextsize = t.next.size();
245 1
            if (nextsize == SIZE_INVALID)
246 0
                error = true;
247 1
            d_uns64 dim = t.dim.toInteger();
248 1
            for (d_uns64 i = 0; i < dim; i++)
249
            {
250 1
                offset = arrayoff + i * nextsize;
251 1
                t.next.accept(this);
252
            }
253 1
            offset = arrayoff;
254
        }
255

256
        override void visit(TypeDArray t)
257
        {
258 1
            setpointer(offset + sz_size_t);
259
        }
260

261
        // dynamic array is {length,ptr}
262
        override void visit(TypeAArray t)
263
        {
264 1
            setpointer(offset);
265
        }
266

267
        override void visit(TypePointer t)
268
        {
269 1
            if (t.nextOf().ty != Tfunction) // don't mark function pointers
270 1
                setpointer(offset);
271
        }
272

273
        override void visit(TypeReference t)
274
        {
275 0
            setpointer(offset);
276
        }
277

278
        override void visit(TypeClass t)
279
        {
280 1
            setpointer(offset);
281
        }
282

283
        override void visit(TypeFunction t)
284
        {
285
        }
286

287
        override void visit(TypeDelegate t)
288
        {
289 1
            setpointer(offset);
290
        }
291

292
        // delegate is {context, function}
293
        override void visit(TypeQualified t)
294
        {
295 0
            assert(0);
296
        }
297

298
        // assume resolved
299
        override void visit(TypeIdentifier t)
300
        {
301 0
            assert(0);
302
        }
303

304
        override void visit(TypeInstance t)
305
        {
306 0
            assert(0);
307
        }
308

309
        override void visit(TypeTypeof t)
310
        {
311 0
            assert(0);
312
        }
313

314
        override void visit(TypeReturn t)
315
        {
316 0
            assert(0);
317
        }
318

319
        override void visit(TypeEnum t)
320
        {
321 1
            visit(cast(Type)t);
322
        }
323

324
        override void visit(TypeTuple t)
325
        {
326 0
            visit(cast(Type)t);
327
        }
328

329
        override void visit(TypeSlice t)
330
        {
331 0
            assert(0);
332
        }
333

334
        override void visit(TypeNull t)
335
        {
336
            // always a null pointer
337
        }
338

339
        override void visit(TypeStruct t)
340
        {
341 1
            d_uns64 structoff = offset;
342 1
            foreach (v; t.sym.fields)
343
            {
344 1
                offset = structoff + v.offset;
345 1
                if (v.type.ty == Tclass)
346 1
                    setpointer(offset);
347
                else
348 1
                    v.type.accept(this);
349
            }
350 1
            offset = structoff;
351
        }
352

353
        // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
354
        void visitClass(TypeClass t)
355
        {
356 1
            d_uns64 classoff = offset;
357
            // skip vtable-ptr and monitor
358 1
            if (t.sym.baseClass)
359 1
                visitClass(cast(TypeClass)t.sym.baseClass.type);
360 1
            foreach (v; t.sym.fields)
361
            {
362 1
                offset = classoff + v.offset;
363 1
                v.type.accept(this);
364
            }
365 1
            offset = classoff;
366
        }
367

368
        Array!(d_uns64)* data;
369
        d_uns64 offset;
370
        d_uns64 sz_size_t;
371
        bool error;
372
    }
373

374 1
    scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t);
375 1
    if (t.ty == Tclass)
376 1
        pbv.visitClass(cast(TypeClass)t);
377
    else
378 1
        t.accept(pbv);
379 1
    return pbv.error ? d_uns64.max : sz;
380
}
381

382
/**
383
 * get an array of size_t values that indicate possible pointer words in memory
384
 *  if interpreted as the type given as argument
385
 * the first array element is the size of the type for independent interpretation
386
 *  of the array
387
 * following elements bits represent one word (4/8 bytes depending on the target
388
 *  architecture). If set the corresponding memory might contain a pointer/reference.
389
 *
390
 *  Returns: [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
391
 */
392
private Expression pointerBitmap(TraitsExp e)
393
{
394 1
    if (!e.args || e.args.dim != 1)
395
    {
396 1
        error(e.loc, "a single type expected for trait pointerBitmap");
397 1
        return ErrorExp.get();
398
    }
399

400 1
    Type t = getType((*e.args)[0]);
401 1
    if (!t)
402
    {
403 0
        error(e.loc, "`%s` is not a type", (*e.args)[0].toChars());
404 0
        return ErrorExp.get();
405
    }
406

407 1
    Array!(d_uns64) data;
408 1
    d_uns64 sz = getTypePointerBitmap(e.loc, t, &data);
409 1
    if (sz == d_uns64.max)
410 0
        return ErrorExp.get();
411

412 1
    auto exps = new Expressions(data.dim + 1);
413 1
    (*exps)[0] = new IntegerExp(e.loc, sz, Type.tsize_t);
414 1
    foreach (size_t i; 1 .. exps.dim)
415 1
        (*exps)[i] = new IntegerExp(e.loc, data[cast(size_t) (i - 1)], Type.tsize_t);
416

417 1
    auto ale = new ArrayLiteralExp(e.loc, Type.tsize_t.sarrayOf(data.dim + 1), exps);
418 1
    return ale;
419
}
420

421
Expression semanticTraits(TraitsExp e, Scope* sc)
422
{
423
    static if (LOGSEMANTIC)
424
    {
425
        printf("TraitsExp::semantic() %s\n", e.toChars());
426
    }
427

428 1
    if (e.ident != Id.compiles &&
429 1
        e.ident != Id.isSame &&
430 1
        e.ident != Id.identifier &&
431 1
        e.ident != Id.getProtection &&
432 1
        e.ident != Id.getAttributes)
433
    {
434
        // Pretend we're in a deprecated scope so that deprecation messages
435
        // aren't triggered when checking if a symbol is deprecated
436 1
        const save = sc.stc;
437 1
        if (e.ident == Id.isDeprecated)
438 1
            sc.stc |= STC.deprecated_;
439 1
        if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1))
440
        {
441 1
            sc.stc = save;
442 1
            return ErrorExp.get();
443
        }
444 1
        sc.stc = save;
445
    }
446 1
    size_t dim = e.args ? e.args.dim : 0;
447

448
    Expression dimError(int expected)
449
    {
450 1
        e.error("expected %d arguments for `%s` but had %d", expected, e.ident.toChars(), cast(int)dim);
451 1
        return ErrorExp.get();
452
    }
453

454
    static IntegerExp True()
455
    {
456 1
        return IntegerExp.createBool(true);
457
    }
458

459
    static IntegerExp False()
460
    {
461 1
        return IntegerExp.createBool(false);
462
    }
463

464
    /********
465
     * Gets the function type from a given AST node
466
     * if the node is a function of some sort.
467
     * Params:
468
     *   o = an AST node to check for a `TypeFunction`
469
     *   fdp = if `o` is a FuncDeclaration then fdp is set to that, otherwise `null`
470
     * Returns:
471
     *   a type node if `o` is a declaration of
472
     *   a delegate, function, function-pointer or a variable of the former.
473
     *   Otherwise, `null`.
474
     */
475
    static TypeFunction toTypeFunction(RootObject o, out FuncDeclaration fdp)
476
    {
477 1
        Type t;
478 1
        if (auto s = getDsymbolWithoutExpCtx(o))
479
        {
480 1
            if (auto fd = s.isFuncDeclaration())
481
            {
482 1
                t = fd.type;
483 1
                fdp = fd;
484
            }
485 1
            else if (auto vd = s.isVarDeclaration())
486 1
                t = vd.type;
487
            else
488 1
                t = isType(o);
489
        }
490
        else
491 1
            t = isType(o);
492

493 1
        if (t)
494
        {
495 1
            if (t.ty == Tfunction)
496 1
                return cast(TypeFunction)t;
497 1
            else if (t.ty == Tdelegate)
498 1
                return cast(TypeFunction)t.nextOf();
499 1
            else if (t.ty == Tpointer && t.nextOf().ty == Tfunction)
500 1
                return cast(TypeFunction)t.nextOf();
501
        }
502

503 1
        return null;
504
    }
505

506
    IntegerExp isX(T)(bool delegate(T) fp)
507
    {
508 1
        if (!dim)
509 1
            return False();
510 1
        foreach (o; *e.args)
511
        {
512
            static if (is(T == Type))
513 1
                auto y = getType(o);
514

515
            static if (is(T : Dsymbol))
516
            {
517 1
                auto s = getDsymbolWithoutExpCtx(o);
518 1
                if (!s)
519 1
                    return False();
520
            }
521
            static if (is(T == Dsymbol))
522
                alias y = s;
523
            static if (is(T == Declaration))
524 1
                auto y = s.isDeclaration();
525
            static if (is(T == FuncDeclaration))
526 1
                auto y = s.isFuncDeclaration();
527
            static if (is(T == EnumMember))
528 0
                auto y = s.isEnumMember();
529

530 1
            if (!y || !fp(y))
531 1
                return False();
532
        }
533 1
        return True();
534
    }
535

536
    alias isTypeX = isX!Type;
537
    alias isDsymX = isX!Dsymbol;
538
    alias isDeclX = isX!Declaration;
539
    alias isFuncX = isX!FuncDeclaration;
540
    alias isEnumMemX = isX!EnumMember;
541

542
    Expression isPkgX(bool function(Package) fp)
543
    {
544 1
        return isDsymX((Dsymbol sym) {
545 1
            Package p = resolveIsPackage(sym);
546 1
            return (p !is null) && fp(p);
547
        });
548
    }
549

550 1
    if (e.ident == Id.isArithmetic)
551
    {
552 1
        return isTypeX(t => t.isintegral() || t.isfloating());
553
    }
554 1
    if (e.ident == Id.isFloating)
555
    {
556 1
        return isTypeX(t => t.isfloating());
557
    }
558 1
    if (e.ident == Id.isIntegral)
559
    {
560 1
        return isTypeX(t => t.isintegral());
561
    }
562 1
    if (e.ident == Id.isScalar)
563
    {
564 1
        return isTypeX(t => t.isscalar());
565
    }
566 1
    if (e.ident == Id.isUnsigned)
567
    {
568 1
        return isTypeX(t => t.isunsigned());
569
    }
570 1
    if (e.ident == Id.isAssociativeArray)
571
    {
572 1
        return isTypeX(t => t.toBasetype().ty == Taarray);
573
    }
574 1
    if (e.ident == Id.isDeprecated)
575
    {
576 1
        if (global.params.vcomplex)
577
        {
578 1
            if (isTypeX(t => t.iscomplex() || t.isimaginary()).isBool(true))
579 1
                return True();
580
        }
581 1
        return isDsymX(t => t.isDeprecated());
582
    }
583 1
    if (e.ident == Id.isFuture)
584
    {
585 1
       return isDeclX(t => t.isFuture());
586
    }
587 1
    if (e.ident == Id.isStaticArray)
588
    {
589 1
        return isTypeX(t => t.toBasetype().ty == Tsarray);
590
    }
591 1
    if (e.ident == Id.isAbstractClass)
592
    {
593 1
        return isTypeX(t => t.toBasetype().ty == Tclass &&
594 1
                            (cast(TypeClass)t.toBasetype()).sym.isAbstract());
595
    }
596 1
    if (e.ident == Id.isFinalClass)
597
    {
598 1
        return isTypeX(t => t.toBasetype().ty == Tclass &&
599 1
                            ((cast(TypeClass)t.toBasetype()).sym.storage_class & STC.final_) != 0);
600
    }
601 1
    if (e.ident == Id.isTemplate)
602
    {
603 1
        if (dim != 1)
604 0
            return dimError(1);
605

606 1
        return isDsymX((s)
607
        {
608 1
            if (!s.toAlias().isOverloadable())
609 1
                return false;
610 1
            return overloadApply(s,
611 1
                sm => sm.isTemplateDeclaration() !is null) != 0;
612
        });
613
    }
614 1
    if (e.ident == Id.isPOD)
615
    {
616 1
        if (dim != 1)
617 1
            return dimError(1);
618

619 1
        auto o = (*e.args)[0];
620 1
        auto t = isType(o);
621 1
        if (!t)
622
        {
623 1
            e.error("type expected as second argument of __traits `%s` instead of `%s`",
624
                e.ident.toChars(), o.toChars());
625 1
            return ErrorExp.get();
626
        }
627

628 1
        Type tb = t.baseElemOf();
629 1
        if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
630
        {
631 1
            return sd.isPOD() ? True() : False();
632
        }
633 1
        return True();
634
    }
635 1
    if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit)
636
    {
637 1
        if (dim != 1)
638 1
            return dimError(1);
639

640 1
        auto o = (*e.args)[0];
641 1
        auto t = isType(o);
642 1
        if (!t)
643
        {
644 1
            e.error("type expected as second argument of __traits `%s` instead of `%s`",
645
                e.ident.toChars(), o.toChars());
646 1
            return ErrorExp.get();
647
        }
648

649 1
        Type tb = t.baseElemOf();
650 1
        if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
651
        {
652 1
            return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False())
653 1
                 : (sd.hasCopyCtor ? True() : False());
654
        }
655 1
        return False();
656
    }
657 1
    if (e.ident == Id.isCopyable)
658
    {
659 1
        if (dim != 1)
660 0
            return dimError(1);
661

662 1
        auto o = (*e.args)[0];
663 1
        auto t = isType(o);
664 1
        if (!t)
665
        {
666 0
            e.error("type expected as second argument of __traits `%s` instead of `%s`",
667
                    e.ident.toChars(), o.toChars());
668 0
            return ErrorExp.get();
669
        }
670

671 1
        t = t.toBasetype();     // get the base in case `t` is an `enum`
672

673 1
        if (auto ts = t.isTypeStruct())
674
        {
675 1
            ts.sym.dsymbolSemantic(sc);
676
        }
677

678 1
        return isCopyable(t) ? True() : False();
679
    }
680

681 1
    if (e.ident == Id.isNested)
682
    {
683 1
        if (dim != 1)
684 1
            return dimError(1);
685

686 1
        auto o = (*e.args)[0];
687 1
        auto s = getDsymbolWithoutExpCtx(o);
688 1
        if (!s)
689
        {
690
        }
691 1
        else if (auto ad = s.isAggregateDeclaration())
692
        {
693 1
            return ad.isNested() ? True() : False();
694
        }
695 1
        else if (auto fd = s.isFuncDeclaration())
696
        {
697 1
            return fd.isNested() ? True() : False();
698
        }
699

700 1
        e.error("aggregate or function expected instead of `%s`", o.toChars());
701 1
        return ErrorExp.get();
702
    }
703 1
    if (e.ident == Id.isDisabled)
704
    {
705 1
        if (dim != 1)
706 0
            return dimError(1);
707

708 1
        return isDeclX(f => f.isDisabled());
709
    }
710 1
    if (e.ident == Id.isAbstractFunction)
711
    {
712 1
        if (dim != 1)
713 1
            return dimError(1);
714

715 1
        return isFuncX(f => f.isAbstract());
716
    }
717 1
    if (e.ident == Id.isVirtualFunction)
718
    {
719 1
        if (dim != 1)
720 1
            return dimError(1);
721

722 1
        return isFuncX(f => f.isVirtual());
723
    }
724 1
    if (e.ident == Id.isVirtualMethod)
725
    {
726 1
        if (dim != 1)
727 1
            return dimError(1);
728

729 1
        return isFuncX(f => f.isVirtualMethod());
730
    }
731 1
    if (e.ident == Id.isFinalFunction)
732
    {
733 1
        if (dim != 1)
734 1
            return dimError(1);
735

736 1
        return isFuncX(f => f.isFinalFunc());
737
    }
738 1
    if (e.ident == Id.isOverrideFunction)
739
    {
740 1
        if (dim != 1)
741 1
            return dimError(1);
742

743 1
        return isFuncX(f => f.isOverride());
744
    }
745 1
    if (e.ident == Id.isStaticFunction)
746
    {
747 1
        if (dim != 1)
748 1
            return dimError(1);
749

750 1
        return isFuncX(f => !f.needThis() && !f.isNested());
751
    }
752 1
    if (e.ident == Id.isModule)
753
    {
754 1
        if (dim != 1)
755 1
            return dimError(1);
756

757 1
        return isPkgX(p => p.isModule() || p.isPackageMod());
758
    }
759 1
    if (e.ident == Id.isPackage)
760
    {
761 1
        if (dim != 1)
762 1
            return dimError(1);
763

764 1
        return isPkgX(p => p.isModule() is null);
765
    }
766 1
    if (e.ident == Id.isRef)
767
    {
768 1
        if (dim != 1)
769 1
            return dimError(1);
770

771 1
        return isDeclX(d => d.isRef());
772
    }
773 1
    if (e.ident == Id.isOut)
774
    {
775 1
        if (dim != 1)
776 1
            return dimError(1);
777

778 1
        return isDeclX(d => d.isOut());
779
    }
780 1
    if (e.ident == Id.isLazy)
781
    {
782 1
        if (dim != 1)
783 1
            return dimError(1);
784

785 1
        return isDeclX(d => (d.storage_class & STC.lazy_) != 0);
786
    }
787 1
    if (e.ident == Id.identifier)
788
    {
789
        // Get identifier for symbol as a string literal
790
        /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
791
         * a symbol should not be folded to a constant.
792
         * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
793
         */
794 1
        if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2))
795 0
            return ErrorExp.get();
796 1
        if (dim != 1)
797 1
            return dimError(1);
798

799 1
        auto o = (*e.args)[0];
800 1
        Identifier id;
801 1
        if (auto po = isParameter(o))
802
        {
803 1
            if (!po.ident)
804
            {
805 0
                e.error("argument `%s` has no identifier", po.type.toChars());
806 0
                return ErrorExp.get();
807
            }
808 1
            id = po.ident;
809
        }
810
        else
811
        {
812 1
            Dsymbol s = getDsymbolWithoutExpCtx(o);
813 1
            if (!s || !s.ident)
814
            {
815 0
                e.error("argument `%s` has no identifier", o.toChars());
816 0
                return ErrorExp.get();
817
            }
818 1
            id = s.ident;
819
        }
820

821 1
        auto se = new StringExp(e.loc, id.toString());
822 1
        return se.expressionSemantic(sc);
823
    }
824 1
    if (e.ident == Id.getProtection)
825
    {
826 1
        if (dim != 1)
827 1
            return dimError(1);
828

829 1
        Scope* sc2 = sc.push();
830 1
        sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
831 1
        bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
832 1
        sc2.pop();
833 1
        if (!ok)
834 0
            return ErrorExp.get();
835

836 1
        auto o = (*e.args)[0];
837 1
        auto s = getDsymbolWithoutExpCtx(o);
838 1
        if (!s)
839
        {
840 1
            if (!isError(o))
841 1
                e.error("argument `%s` has no protection", o.toChars());
842 1
            return ErrorExp.get();
843
        }
844 1
        if (s.semanticRun == PASS.init)
845 1
            s.dsymbolSemantic(null);
846

847 1
        auto protName = protectionToString(s.prot().kind); // TODO: How about package(names)
848 1
        assert(protName);
849 1
        auto se = new StringExp(e.loc, protName);
850 1
        return se.expressionSemantic(sc);
851
    }
852 1
    if (e.ident == Id.parent)
853
    {
854 1
        if (dim != 1)
855 1
            return dimError(1);
856

857 1
        auto o = (*e.args)[0];
858 1
        auto s = getDsymbolWithoutExpCtx(o);
859 1
        if (s)
860
        {
861
            // https://issues.dlang.org/show_bug.cgi?id=12496
862
            // Consider:
863
            // class T1
864
            // {
865
            //     class C(uint value) { }
866
            // }
867
            // __traits(parent, T1.C!2)
868 1
            if (auto ad = s.isAggregateDeclaration())  // `s` is `C`
869
            {
870 1
                if (ad.isNested())                     // `C` is nested
871
                {
872 1
                    if (auto p = s.toParent())         // `C`'s parent is `C!2`, believe it or not
873
                    {
874 1
                        if (p.isTemplateInstance())    // `C!2` is a template instance
875
                        {
876 1
                            s = p;                     // `C!2`'s parent is `T1`
877 1
                            auto td = (cast(TemplateInstance)p).tempdecl;
878 1
                            if (td)
879 1
                                s = td;                // get the declaration context just in case there's two contexts
880
                        }
881
                    }
882
                }
883
            }
884

885 1
            if (auto fd = s.isFuncDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=8943
886 1
                s = fd.toAliasFunc();
887 1
            if (!s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=8922
888 1
                s = s.toParent();
889
        }
890 1
        if (!s || s.isImport())
891
        {
892 1
            e.error("argument `%s` has no parent", o.toChars());
893 1
            return ErrorExp.get();
894
        }
895

896 1
        if (auto f = s.isFuncDeclaration())
897
        {
898 1
            if (auto td = getFuncTemplateDecl(f))
899
            {
900 1
                if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
901 0
                    td = td.overroot; // then get the start
902 1
                Expression ex = new TemplateExp(e.loc, td, f);
903 1
                ex = ex.expressionSemantic(sc);
904 1
                return ex;
905
            }
906 1
            if (auto fld = f.isFuncLiteralDeclaration())
907
            {
908
                // Directly translate to VarExp instead of FuncExp
909 1
                Expression ex = new VarExp(e.loc, fld, true);
910 1
                return ex.expressionSemantic(sc);
911
            }
912
        }
913 1
        return symbolToExp(s, e.loc, sc, false);
914
    }
915 1
    if (e.ident == Id.child)
916
    {
917 1
        if (dim != 2)
918 1
            return dimError(2);
919

920 1
        Expression ex;
921 1
        auto op = (*e.args)[0];
922 1
        if (auto symp = getDsymbol(op))
923 1
            ex = new DsymbolExp(e.loc, symp);
924 1
        else if (auto exp = op.isExpression())
925 1
            ex = exp;
926
        else
927
        {
928 1
            e.error("symbol or expression expected as first argument of __traits `child` instead of `%s`", op.toChars());
929 1
            return ErrorExp.get();
930
        }
931

932 1
        ex = ex.expressionSemantic(sc);
933 1
        auto oc = (*e.args)[1];
934 1
        auto symc = getDsymbol(oc);
935 1
        if (!symc)
936
        {
937 1
            e.error("symbol expected as second argument of __traits `child` instead of `%s`", oc.toChars());
938 1
            return ErrorExp.get();
939
        }
940

941 1
        if (auto d = symc.isDeclaration())
942 1
            ex = new DotVarExp(e.loc, ex, d);
943 1
        else if (auto td = symc.isTemplateDeclaration())
944 1
            ex = new DotExp(e.loc, ex, new TemplateExp(e.loc, td));
945 1
        else if (auto ti = symc.isScopeDsymbol())
946 1
            ex = new DotExp(e.loc, ex, new ScopeExp(e.loc, ti));
947
        else
948 0
            assert(0);
949

950 1
        ex = ex.expressionSemantic(sc);
951 1
        return ex;
952
    }
953 1
    if (e.ident == Id.hasMember ||
954 1
        e.ident == Id.getMember ||
955 1
        e.ident == Id.getOverloads ||
956 1
        e.ident == Id.getVirtualMethods ||
957 1
        e.ident == Id.getVirtualFunctions)
958
    {
959 1
        if (dim != 2 && !(dim == 3 && e.ident == Id.getOverloads))
960 1
            return dimError(2);
961

962 1
        auto o = (*e.args)[0];
963 1
        auto ex = isExpression((*e.args)[1]);
964 1
        if (!ex)
965
        {
966 0
            e.error("expression expected as second argument of __traits `%s`", e.ident.toChars());
967 0
            return ErrorExp.get();
968
        }
969 1
        ex = ex.ctfeInterpret();
970

971 1
        bool includeTemplates = false;
972 1
        if (dim == 3 && e.ident == Id.getOverloads)
973
        {
974 1
            auto b = isExpression((*e.args)[2]);
975 1
            b = b.ctfeInterpret();
976 1
            if (!b.type.equals(Type.tbool))
977
            {
978 1
                e.error("`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b.toChars(), b.type.toChars());
979 1
                return ErrorExp.get();
980
            }
981 1
            includeTemplates = b.isBool(true);
982
        }
983

984 1
        StringExp se = ex.toStringExp();
985 1
        if (!se || se.len == 0)
986
        {
987 0
            e.error("string expected as second argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars());
988 0
            return ErrorExp.get();
989
        }
990 1
        se = se.toUTF8(sc);
991

992 1
        if (se.sz != 1)
993
        {
994 0
            e.error("string must be chars");
995 0
            return ErrorExp.get();
996
        }
997 1
        auto id = Identifier.idPool(se.peekString());
998

999
        /* Prefer a Type, because getDsymbol(Type) can lose type modifiers.
1000
           Then a Dsymbol, because it might need some runtime contexts.
1001
         */
1002

1003 1
        Dsymbol sym = getDsymbol(o);
1004 1
        if (auto t = isType(o))
1005 1
            ex = typeDotIdExp(e.loc, t, id);
1006 1
        else if (sym)
1007
        {
1008 1
            if (e.ident == Id.hasMember)
1009
            {
1010 1
                if (auto sm = sym.search(e.loc, id))
1011 1
                    return True();
1012
            }
1013 1
            ex = new DsymbolExp(e.loc, sym);
1014 1
            ex = new DotIdExp(e.loc, ex, id);
1015
        }
1016 1
        else if (auto ex2 = isExpression(o))
1017 1
            ex = new DotIdExp(e.loc, ex2, id);
1018
        else
1019
        {
1020 0
            e.error("invalid first argument");
1021 0
            return ErrorExp.get();
1022
        }
1023

1024
        // ignore symbol visibility and disable access checks for these traits
1025 1
        Scope* scx = sc.push();
1026 1
        scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck;
1027 1
        scope (exit) scx.pop();
1028

1029 1
        if (e.ident == Id.hasMember)
1030
        {
1031
            /* Take any errors as meaning it wasn't found
1032
             */
1033 1
            ex = ex.trySemantic(scx);
1034 1
            return ex ? True() : False();
1035
        }
1036 1
        else if (e.ident == Id.getMember)
1037
        {
1038 1
            if (ex.op == TOK.dotIdentifier)
1039
                // Prevent semantic() from replacing Symbol with its initializer
1040 1
                (cast(DotIdExp)ex).wantsym = true;
1041 1
            ex = ex.expressionSemantic(scx);
1042 1
            return ex;
1043
        }
1044 1
        else if (e.ident == Id.getVirtualFunctions ||
1045 1
                 e.ident == Id.getVirtualMethods ||
1046 1
                 e.ident == Id.getOverloads)
1047
        {
1048 1
            uint errors = global.errors;
1049 1
            Expression eorig = ex;
1050 1
            ex = ex.expressionSemantic(scx);
1051 1
            if (errors < global.errors)
1052 1
                e.error("`%s` cannot be resolved", eorig.toChars());
1053

1054
            /* Create tuple of functions of ex
1055
             */
1056 1
            auto exps = new Expressions();
1057 1
            Dsymbol f;
1058 1
            if (ex.op == TOK.variable)
1059
            {
1060 1
                VarExp ve = cast(VarExp)ex;
1061 1
                f = ve.var.isFuncDeclaration();
1062 1
                ex = null;
1063
            }
1064 1
            else if (ex.op == TOK.dotVariable)
1065
            {
1066 1
                DotVarExp dve = cast(DotVarExp)ex;
1067 1
                f = dve.var.isFuncDeclaration();
1068 1
                if (dve.e1.op == TOK.dotType || dve.e1.op == TOK.this_)
1069 1
                    ex = null;
1070
                else
1071 1
                    ex = dve.e1;
1072
            }
1073 1
            else if (ex.op == TOK.template_)
1074
            {
1075 1
                VarExp ve = cast(VarExp)ex;
1076 1
                auto td = ve.var.isTemplateDeclaration();
1077 1
                f = td;
1078 1
                if (td && td.funcroot)
1079 1
                    f = td.funcroot;
1080 1
                ex = null;
1081
            }
1082 1
            else if (ex.op == TOK.dotTemplateDeclaration)
1083
            {
1084 1
                DotTemplateExp dte = cast(DotTemplateExp)ex;
1085 1
                auto td = dte.td;
1086 1
                f = td;
1087 1
                if (td && td.funcroot)
1088 1
                    f = td.funcroot;
1089 1
                ex = null;
1090 1
                if (dte.e1.op != TOK.dotType && dte.e1.op != TOK.this_)
1091 1
                    ex = dte.e1;
1092
            }
1093 1
            bool[string] funcTypeHash;
1094

1095
            /* Compute the function signature and insert it in the
1096
             * hashtable, if not present. This is needed so that
1097
             * traits(getOverlods, F3, "visit") does not count `int visit(int)`
1098
             * twice in the following example:
1099
             *
1100
             * =============================================
1101
             * interface F1 { int visit(int);}
1102
             * interface F2 { int visit(int); void visit(); }
1103
             * interface F3 : F2, F1 {}
1104
             *==============================================
1105
             */
1106
            void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e)
1107
            {
1108 1
                auto signature = fd.type.toString();
1109
                //printf("%s - %s\n", fd.toChars, signature);
1110 1
                if (signature !in funcTypeHash)
1111
                {
1112 1
                    funcTypeHash[signature] = true;
1113 1
                    exps.push(e);
1114
                }
1115
            }
1116

1117
            int dg(Dsymbol s)
1118
            {
1119 1
                auto fd = s.isFuncDeclaration();
1120 1
                if (!fd)
1121
                {
1122 1
                    if (includeTemplates)
1123
                    {
1124 1
                        if (auto td = s.isTemplateDeclaration())
1125
                        {
1126
                            // if td is part of an overload set we must take a copy
1127
                            // which shares the same `instances` cache but without
1128
                            // `overroot` and `overnext` set to avoid overload
1129
                            // behaviour in the result.
1130 1
                            if (td.overnext !is null)
1131
                            {
1132 1
                                if (td.instances is null)
1133
                                {
1134
                                    // create an empty AA just to copy it
1135 1
                                    scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
1136 1
                                    auto tib = TemplateInstanceBox(ti);
1137 1
                                    td.instances[tib] = null;
1138 1
                                    td.instances.clear();
1139
                                }
1140 1
                                td = cast(TemplateDeclaration) td.syntaxCopy(null);
1141
                                import core.stdc.string : memcpy;
1142 1
                                memcpy(cast(void*) td, cast(void*) s,
1143
                                        __traits(classInstanceSize, TemplateDeclaration));
1144 1
                                td.overroot = null;
1145 1
                                td.overnext = null;
1146
                            }
1147 1
                            exps.push(new DsymbolExp(Loc.initial, td, false));
1148
                        }
1149
                    }
1150 1
                    return 0;
1151
                }
1152 1
                if (e.ident == Id.getVirtualFunctions && !fd.isVirtual())
1153 0
                    return 0;
1154 1
                if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod())
1155 1
                    return 0;
1156

1157 1
                auto fa = new FuncAliasDeclaration(fd.ident, fd, false);
1158 1
                fa.protection = fd.protection;
1159

1160 1
                auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false)
1161 1
                            : new DsymbolExp(Loc.initial, fa, false);
1162

1163
                // if the parent is an interface declaration
1164
                // we must check for functions with the same signature
1165
                // in different inherited interfaces
1166 1
                if (sym && sym.isInterfaceDeclaration())
1167 1
                    insertInterfaceInheritedFunction(fd, e);
1168
                else
1169 1
                    exps.push(e);
1170 1
                return 0;
1171
            }
1172

1173 1
            InterfaceDeclaration ifd = null;
1174 1
            if (sym)
1175 1
                ifd = sym.isInterfaceDeclaration();
1176
            // If the symbol passed as a parameter is an
1177
            // interface that inherits other interfaces
1178 1
            overloadApply(f, &dg);
1179 1
            if (ifd && ifd.interfaces && f)
1180
            {
1181
                // check the overloads of each inherited interface individually
1182 1
                foreach (bc; ifd.interfaces)
1183
                {
1184 1
                    if (auto fd = bc.sym.search(e.loc, f.ident))
1185 1
                        overloadApply(fd, &dg);
1186
                }
1187
            }
1188

1189 1
            auto tup = new TupleExp(e.loc, exps);
1190 1
            return tup.expressionSemantic(scx);
1191
        }
1192
        else
1193 0
            assert(0);
1194
    }
1195 1
    if (e.ident == Id.classInstanceSize)
1196
    {
1197 1
        if (dim != 1)
1198 1
            return dimError(1);
1199

1200 1
        auto o = (*e.args)[0];
1201 1
        auto s = getDsymbol(o);
1202 1
        auto cd = s ? s.isClassDeclaration() : null;
1203 1
        if (!cd)
1204
        {
1205 0
            e.error("first argument is not a class");
1206 0
            return ErrorExp.get();
1207
        }
1208 1
        if (cd.sizeok != Sizeok.done)
1209
        {
1210 1
            cd.size(e.loc);
1211
        }
1212 1
        if (cd.sizeok != Sizeok.done)
1213
        {
1214 0
            e.error("%s `%s` is forward referenced", cd.kind(), cd.toChars());
1215 0
            return ErrorExp.get();
1216
        }
1217

1218 1
        return new IntegerExp(e.loc, cd.structsize, Type.tsize_t);
1219
    }
1220 1
    if (e.ident == Id.getAliasThis)
1221
    {
1222 1
        if (dim != 1)
1223 1
            return dimError(1);
1224

1225 1
        auto o = (*e.args)[0];
1226 1
        auto s = getDsymbol(o);
1227 1
        auto ad = s ? s.isAggregateDeclaration() : null;
1228

1229 1
        auto exps = new Expressions();
1230 1
        if (ad && ad.aliasthis)
1231 1
            exps.push(new StringExp(e.loc, ad.aliasthis.ident.toString()));
1232 1
        Expression ex = new TupleExp(e.loc, exps);
1233 1
        ex = ex.expressionSemantic(sc);
1234 1
        return ex;
1235
    }
1236 1
    if (e.ident == Id.getAttributes)
1237
    {
1238
        /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
1239
         * a symbol should not be folded to a constant.
1240
         * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
1241
         */
1242 1
        if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3))
1243 1
            return ErrorExp.get();
1244

1245 1
        if (dim != 1)
1246 1
            return dimError(1);
1247

1248 1
        auto o = (*e.args)[0];
1249 1
        auto po = isParameter(o);
1250 1
        auto s = getDsymbolWithoutExpCtx(o);
1251 1
        UserAttributeDeclaration udad = null;
1252 1
        if (po)
1253
        {
1254 1
            udad = po.userAttribDecl;
1255
        }
1256 1
        else if (s)
1257
        {
1258 1
            if (s.isImport())
1259
            {
1260 0
                s = s.isImport().mod;
1261
            }
1262
            //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope);
1263 1
            udad = s.userAttribDecl;
1264
        }
1265
        else
1266
        {
1267
            version (none)
1268
            {
1269
                Expression x = isExpression(o);
1270
                Type t = isType(o);
1271
                if (x)
1272
                    printf("e = %s %s\n", Token.toChars(x.op), x.toChars());
1273
                if (t)
1274
                    printf("t = %d %s\n", t.ty, t.toChars());
1275
            }
1276 1
            e.error("first argument is not a symbol");
1277 1
            return ErrorExp.get();
1278
        }
1279

1280 1
        auto exps = udad ? udad.getAttributes() : new Expressions();
1281 1
        auto tup = new TupleExp(e.loc, exps);
1282 1
        return tup.expressionSemantic(sc);
1283
    }
1284 1
    if (e.ident == Id.getFunctionAttributes)
1285
    {
1286
        /* Extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
1287
         * https://dlang.org/spec/traits.html#getFunctionAttributes
1288
         */
1289 1
        if (dim != 1)
1290 1
            return dimError(1);
1291

1292 1
        FuncDeclaration fd;
1293 1
        TypeFunction tf = toTypeFunction((*e.args)[0], fd);
1294

1295 1
        if (!tf)
1296
        {
1297 0
            e.error("first argument is not a function");
1298 0
            return ErrorExp.get();
1299
        }
1300

1301 1
        auto mods = new Expressions();
1302

1303
        void addToMods(string str)
1304
        {
1305 1
            mods.push(new StringExp(Loc.initial, str));
1306
        }
1307 1
        tf.modifiersApply(&addToMods);
1308 1
        tf.attributesApply(&addToMods, TRUSTformatSystem);
1309

1310 1
        auto tup = new TupleExp(e.loc, mods);
1311 1
        return tup.expressionSemantic(sc);
1312
    }
1313 1
    if (e.ident == Id.isReturnOnStack)
1314
    {
1315
        /* Extract as a boolean if function return value is on the stack
1316
         * https://dlang.org/spec/traits.html#isReturnOnStack
1317
         */
1318 1
        if (dim != 1)
1319 1
            return dimError(1);
1320

1321 1
        RootObject o = (*e.args)[0];
1322 1
        FuncDeclaration fd;
1323 1
        TypeFunction tf = toTypeFunction(o, fd);
1324

1325 1
        if (!tf)
1326
        {
1327 1
            e.error("argument to `__traits(isReturnOnStack, %s)` is not a function", o.toChars());
1328 1
            return ErrorExp.get();
1329
        }
1330

1331 1
        bool value = target.isReturnOnStack(tf, fd && fd.needThis());
1332 1
        return IntegerExp.createBool(value);
1333
    }
1334 1
    if (e.ident == Id.getFunctionVariadicStyle)
1335
    {
1336
        /* Accept a symbol or a type. Returns one of the following:
1337
         *  "none"      not a variadic function
1338
         *  "argptr"    extern(D) void dstyle(...), use `__argptr` and `__arguments`
1339
         *  "stdarg"    extern(C) void cstyle(int, ...), use core.stdc.stdarg
1340
         *  "typesafe"  void typesafe(T[] ...)
1341
         */
1342
        // get symbol linkage as a string
1343 1
        if (dim != 1)
1344 0
            return dimError(1);
1345

1346 1
        LINK link;
1347 1
        VarArg varargs;
1348 1
        auto o = (*e.args)[0];
1349

1350 1
        FuncDeclaration fd;
1351 1
        TypeFunction tf = toTypeFunction(o, fd);
1352

1353 1
        if (tf)
1354
        {
1355 1
            link = tf.linkage;
1356 1
            varargs = tf.parameterList.varargs;
1357
        }
1358
        else
1359
        {
1360 1
            if (!fd)
1361
            {
1362 1
                e.error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o.toChars());
1363 1
                return ErrorExp.get();
1364
            }
1365 0
            link = fd.linkage;
1366 0
            varargs = fd.getParameterList().varargs;
1367
        }
1368 1
        string style;
1369 1
        final switch (varargs)
1370
        {
1371 1
            case VarArg.none:     style = "none";           break;
1372 1
            case VarArg.variadic: style = (link == LINK.d)
1373 1
                                             ? "argptr"
1374 1
                                             : "stdarg";    break;
1375 1
            case VarArg.typesafe: style = "typesafe";       break;
1376
        }
1377 1
        auto se = new StringExp(e.loc, style);
1378 1
        return se.expressionSemantic(sc);
1379
    }
1380 1
    if (e.ident == Id.getParameterStorageClasses)
1381
    {
1382
        /* Accept a function symbol or a type, followed by a parameter index.
1383
         * Returns a tuple of strings of the parameter's storage classes.
1384
         */
1385
        // get symbol linkage as a string
1386 1
        if (dim != 2)
1387 1
            return dimError(2);
1388

1389 1
        auto o = (*e.args)[0];
1390 1
        auto o1 = (*e.args)[1];
1391

1392 1
        FuncDeclaration fd;
1393 1
        TypeFunction tf = toTypeFunction(o, fd);
1394

1395 1
        ParameterList fparams;
1396 1
        if (tf)
1397 1
            fparams = tf.parameterList;
1398 1
        else if (fd)
1399 0
            fparams = fd.getParameterList();
1400
        else
1401
        {
1402 1
            e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function",
1403
                o.toChars(), o1.toChars());
1404 1
            return ErrorExp.get();
1405
        }
1406

1407 1
        StorageClass stc;
1408

1409
        // Set stc to storage class of the ith parameter
1410 1
        auto ex = isExpression((*e.args)[1]);
1411 1
        if (!ex)
1412
        {
1413 1
            e.error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
1414
                o.toChars(), o1.toChars());
1415 1
            return ErrorExp.get();
1416
        }
1417 1
        ex = ex.ctfeInterpret();
1418 1
        auto ii = ex.toUInteger();
1419 1
        if (ii >= fparams.length)
1420
        {
1421 1
            e.error("parameter index must be in range 0..%u not %s", cast(uint)fparams.length, ex.toChars());
1422 1
            return ErrorExp.get();
1423
        }
1424

1425 1
        uint n = cast(uint)ii;
1426 1
        Parameter p = fparams[n];
1427 1
        stc = p.storageClass;
1428

1429
        // This mirrors hdrgen.visit(Parameter p)
1430 1
        if (p.type && p.type.mod & MODFlags.shared_)
1431 0
            stc &= ~STC.shared_;
1432

1433 1
        auto exps = new Expressions;
1434

1435
        void push(string s)
1436
        {
1437 1
            exps.push(new StringExp(e.loc, s));
1438
        }
1439

1440 1
        if (stc & STC.auto_)
1441 0
            push("auto");
1442 1
        if (stc & STC.return_)
1443 1
            push("return");
1444

1445 1
        if (stc & STC.out_)
1446 1
            push("out");
1447 1
        else if (stc & STC.ref_)
1448 1
            push("ref");
1449 1
        else if (stc & STC.in_)
1450 1
            push("in");
1451 1
        else if (stc & STC.lazy_)
1452 1
            push("lazy");
1453 1
        else if (stc & STC.alias_)
1454 0
            push("alias");
1455

1456 1
        if (stc & STC.const_)
1457 0
            push("const");
1458 1
        if (stc & STC.immutable_)
1459 0
            push("immutable");
1460 1
        if (stc & STC.wild)
1461 0
            push("inout");
1462 1
        if (stc & STC.shared_)
1463 0
            push("shared");
1464 1
        if (stc & STC.scope_ && !(stc & STC.scopeinferred))
1465 1
            push("scope");
1466

1467 1
        auto tup = new TupleExp(e.loc, exps);
1468 1
        return tup.expressionSemantic(sc);
1469
    }
1470 1
    if (e.ident == Id.getLinkage)
1471
    {
1472
        // get symbol linkage as a string
1473 1
        if (dim != 1)
1474 1
            return dimError(1);
1475

1476 1
        LINK link;
1477 1
        auto o = (*e.args)[0];
1478

1479 1
        FuncDeclaration fd;
1480 1
        TypeFunction tf = toTypeFunction(o, fd);
1481

1482 1
        if (tf)
1483 1
            link = tf.linkage;
1484
        else
1485
        {
1486 1
            auto s = getDsymbol(o);
1487 1
            Declaration d;
1488 1
            AggregateDeclaration agg;
1489 1
            if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null))
1490
            {
1491 1
                e.error("argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars());
1492 1
                return ErrorExp.get();
1493
            }
1494

1495 1
            if (d !is null)
1496 1
                link = d.linkage;
1497
            else
1498
            {
1499
                // Resolves forward references
1500 1
                if (agg.sizeok != Sizeok.done)
1501
                {
1502 1
                    agg.size(e.loc);
1503 1
                    if (agg.sizeok != Sizeok.done)
1504
                    {
1505 0
                        e.error("%s `%s` is forward referenced", agg.kind(), agg.toChars());
1506 0
                        return ErrorExp.get();
1507
                    }
1508
                }
1509

1510 1
                final switch (agg.classKind)
1511
                {
1512 1
                    case ClassKind.d:
1513 1
                        link = LINK.d;
1514 1
                        break;
1515 1
                    case ClassKind.cpp:
1516 1
                        link = LINK.cpp;
1517 1
                        break;
1518 0
                    case ClassKind.objc:
1519 0
                        link = LINK.objc;
1520 0
                        break;
1521
                }
1522
            }
1523
        }
1524 1
        auto linkage = linkageToChars(link);
1525 1
        auto se = new StringExp(e.loc, linkage.toDString());
1526 1
        return se.expressionSemantic(sc);
1527
    }
1528 1
    if (e.ident == Id.allMembers ||
1529 1
        e.ident == Id.derivedMembers)
1530
    {
1531 1
        if (dim != 1)
1532 1
            return dimError(1);
1533

1534 1
        auto o = (*e.args)[0];
1535 1
        auto s = getDsymbol(o);
1536 1
        if (!s)
1537
        {
1538 1
            e.error("In expression `%s` `%s` can't have members", e.toChars(), o.toChars());
1539 1
            e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", o.toChars());
1540

1541 1
            return ErrorExp.get();
1542
        }
1543 1
        if (auto imp = s.isImport())
1544
        {
1545
            // https://issues.dlang.org/show_bug.cgi?id=9692
1546 1
            s = imp.mod;
1547
        }
1548

1549 1
        auto sds = s.isScopeDsymbol();
1550 1
        if (!sds || sds.isTemplateDeclaration())
1551
        {
1552 1
            e.error("In expression `%s` %s `%s` has no members", e.toChars(), s.kind(), s.toChars());
1553 1
            e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", s.toChars());
1554 1
            return ErrorExp.get();
1555
        }
1556

1557 1
        auto idents = new Identifiers();
1558

1559
        int pushIdentsDg(size_t n, Dsymbol sm)
1560
        {
1561 1
            if (!sm)
1562 0
                return 1;
1563

1564
            // skip local symbols, such as static foreach loop variables
1565 1
            if (auto decl = sm.isDeclaration())
1566
            {
1567 1
                if (decl.storage_class & STC.local)
1568
                {
1569 1
                    return 0;
1570
                }
1571
            }
1572

1573
            // https://issues.dlang.org/show_bug.cgi?id=20915
1574
            // skip version and debug identifiers
1575 1
            if (sm.isVersionSymbol() || sm.isDebugSymbol())
1576 1
                return 0;
1577

1578
            //printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
1579 1
            if (sm.ident)
1580
            {
1581
                // https://issues.dlang.org/show_bug.cgi?id=10096
1582
                // https://issues.dlang.org/show_bug.cgi?id=10100
1583
                // Skip over internal members in __traits(allMembers)
1584 1
                if ((sm.isCtorDeclaration() && sm.ident != Id.ctor) ||
1585 1
                    (sm.isDtorDeclaration() && sm.ident != Id.dtor) ||
1586 1
                    (sm.isPostBlitDeclaration() && sm.ident != Id.postblit) ||
1587 1
                    sm.isInvariantDeclaration() ||
1588 1
                    sm.isUnitTestDeclaration())
1589

1590
                {
1591 1
                    return 0;
1592
                }
1593 1
                if (sm.ident == Id.empty)
1594
                {
1595 1
                    return 0;
1596
                }
1597 1
                if (sm.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177
1598 1
                    return 0;
1599 1
                if (!sds.isModule() && sm.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057
1600 1
                    return 0;
1601

1602
                //printf("\t%s\n", sm.ident.toChars());
1603

1604
                /* Skip if already present in idents[]
1605
                 */
1606 1
                foreach (id; *idents)
1607
                {
1608 1
                    if (id == sm.ident)
1609 1
                        return 0;
1610

1611
                    // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
1612
                    debug
1613
                    {
1614
                        import core.stdc.string : strcmp;
1615 1
                        assert(strcmp(id.toChars(), sm.ident.toChars()) != 0);
1616
                    }
1617
                }
1618 1
                idents.push(sm.ident);
1619
            }
1620 1
            else if (auto ed = sm.isEnumDeclaration())
1621
            {
1622 1
                ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg);
1623
            }
1624 1
            return 0;
1625
        }
1626

1627 1
        ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg);
1628 1
        auto cd = sds.isClassDeclaration();
1629 1
        if (cd && e.ident == Id.allMembers)
1630
        {
1631 1
            if (cd.semanticRun < PASS.semanticdone)
1632 1
                cd.dsymbolSemantic(null); // https://issues.dlang.org/show_bug.cgi?id=13668
1633
                                   // Try to resolve forward reference
1634

1635
            void pushBaseMembersDg(ClassDeclaration cd)
1636
            {
1637 1
                for (size_t i = 0; i < cd.baseclasses.dim; i++)
1638
                {
1639 1
                    auto cb = (*cd.baseclasses)[i].sym;
1640 1
                    assert(cb);
1641 1
                    ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg);
1642 1
                    if (cb.baseclasses.dim)
1643 1
                        pushBaseMembersDg(cb);
1644
                }
1645
            }
1646

1647 1
            pushBaseMembersDg(cd);
1648
        }
1649

1650
        // Turn Identifiers into StringExps reusing the allocated array
1651 1
        assert(Expressions.sizeof == Identifiers.sizeof);
1652 1
        auto exps = cast(Expressions*)idents;
1653 1
        foreach (i, id; *idents)
1654
        {
1655 1
            auto se = new StringExp(e.loc, id.toString());
1656 1
            (*exps)[i] = se;
1657
        }
1658

1659
        /* Making this a tuple is more flexible, as it can be statically unrolled.
1660
         * To make an array literal, enclose __traits in [ ]:
1661
         *   [ __traits(allMembers, ...) ]
1662
         */
1663 1
        Expression ex = new TupleExp(e.loc, exps);
1664 1
        ex = ex.expressionSemantic(sc);
1665 1
        return ex;
1666
    }
1667 1
    if (e.ident == Id.compiles)
1668
    {
1669
        /* Determine if all the objects - types, expressions, or symbols -
1670
         * compile without error
1671
         */
1672 1
        if (!dim)
1673 1
            return False();
1674

1675 1
        foreach (o; *e.args)
1676
        {
1677 1
            uint errors = global.startGagging();
1678 1
            Scope* sc2 = sc.push();
1679 1
            sc2.tinst = null;
1680 1
            sc2.minst = null;
1681 1
            sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst;
1682

1683 1
            bool err = false;
1684

1685 1
            auto t = isType(o);
1686 1
            while (t)
1687
            {
1688 1
                if (auto tm = t.isTypeMixin())
1689
                {
1690
                    /* The mixin string could be a type or an expression.
1691
                     * Have to try compiling it to see.
1692
                     */
1693 1
                    OutBuffer buf;
1694 1
                    if (expressionsToString(buf, sc, tm.exps))
1695
                    {
1696 0
                        err = true;
1697 0
                        break;
1698
                    }
1699 1
                    const olderrors = global.errors;
1700 1
                    const len = buf.length;
1701 1
                    buf.writeByte(0);
1702 1
                    const str = buf.extractSlice()[0 .. len];
1703 1
                    scope p = new Parser!ASTCodegen(e.loc, sc._module, str, false);
1704 1
                    p.nextToken();
1705
                    //printf("p.loc.linnum = %d\n", p.loc.linnum);
1706

1707 1
                    o = p.parseTypeOrAssignExp(TOK.endOfFile);
1708 1
                    if (olderrors != global.errors || p.token.value != TOK.endOfFile)
1709
                    {
1710 1
                        err = true;
1711 1
                        break;
1712
                    }
1713 1
                    t = o.isType();
1714
                }
1715
                else
1716 1
                    break;
1717
            }
1718

1719 1
            if (!err)
1720
            {
1721 1
                auto ex = t ? t.typeToExpression() : isExpression(o);
1722 1
                if (!ex && t)
1723
                {
1724 1
                    Dsymbol s;
1725 1
                    t.resolve(e.loc, sc2, &ex, &t, &s);
1726 1
                    if (t)
1727
                    {
1728 1
                        t.typeSemantic(e.loc, sc2);
1729 1
                        if (t.ty == Terror)
1730 1
                            err = true;
1731
                    }
1732 1
                    else if (s && s.errors)
1733 0
                        err = true;
1734
                }
1735 1
                if (ex)
1736
                {
1737 1
                    ex = ex.expressionSemantic(sc2);
1738 1
                    ex = resolvePropertiesOnly(sc2, ex);
1739 1
                    ex = ex.optimize(WANTvalue);
1740 1
                    if (sc2.func && sc2.func.type.ty == Tfunction)
1741
                    {
1742 1
                        const tf = cast(TypeFunction)sc2.func.type;
1743 1
                        err |= tf.isnothrow && canThrow(ex, sc2.func, false);
1744
                    }
1745 1
                    ex = checkGC(sc2, ex);
1746 1
                    if (ex.op == TOK.error)
1747 1
                        err = true;
1748
                }
1749
            }
1750

1751
            // Carefully detach the scope from the parent and throw it away as
1752
            // we only need it to evaluate the expression
1753
            // https://issues.dlang.org/show_bug.cgi?id=15428
1754 1
            sc2.detach();
1755

1756 1
            if (global.endGagging(errors) || err)
1757
            {
1758 1
                return False();
1759
            }
1760
        }
1761 1
        return True();
1762
    }
1763 1
    if (e.ident == Id.isSame)
1764
    {
1765
        /* Determine if two symbols are the same
1766
         */
1767 1
        if (dim != 2)
1768 0
            return dimError(2);
1769

1770
        // https://issues.dlang.org/show_bug.cgi?id=20761
1771
        // tiarg semantic may expand in place the list of arguments, for example:
1772
        //
1773
        //     before tiarg sema:  __traits(isSame, seq!(0,0), seq!(1,1))
1774
        //     after            :  __traits(isSame, 0, 0, 1, 1)
1775
        //
1776
        // so we split in two lists
1777 1
        Objects ob1;
1778 1
        ob1.push((*e.args)[0]);
1779 1
        Objects ob2;
1780 1
        ob2.push((*e.args)[1]);
1781 1
        if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0))
1782 0
            return ErrorExp.get();
1783 1
        if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0))
1784 0
            return ErrorExp.get();
1785 1
        if (ob1.dim != ob2.dim)
1786 1
            return False();
1787 1
        foreach (immutable i; 0 .. ob1.dim)
1788 1
            if (!ob1[i].isSame(ob2[i], sc))
1789 1
                return False();
1790 1
        return True();
1791
    }
1792 1
    if (e.ident == Id.getUnitTests)
1793
    {
1794 1
        if (dim != 1)
1795 1
            return dimError(1);
1796

1797 1
        auto o = (*e.args)[0];
1798 1
        auto s = getDsymbolWithoutExpCtx(o);
1799 1
        if (!s)
1800
        {
1801 0
            e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate",
1802
                o.toChars());
1803 0
            return ErrorExp.get();
1804
        }
1805 1
        if (auto imp = s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990
1806 1
            s = imp.mod;
1807

1808 1
        auto sds = s.isScopeDsymbol();
1809 1
        if (!sds)
1810
        {
1811 0
            e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate, not a %s",
1812
                s.toChars(), s.kind());
1813 0
            return ErrorExp.get();
1814
        }
1815

1816 1
        auto exps = new Expressions();
1817 1
        if (global.params.useUnitTests)
1818
        {
1819 1
            bool[void*] uniqueUnitTests;
1820

1821
            void symbolDg(Dsymbol s)
1822
            {
1823 1
                if (auto ad = s.isAttribDeclaration())
1824
                {
1825 1
                    ad.include(null).foreachDsymbol(&symbolDg);
1826
                }
1827 1
                else if (auto ud = s.isUnitTestDeclaration())
1828
                {
1829 1
                    if (cast(void*)ud in uniqueUnitTests)
1830 0
                        return;
1831

1832 1
                    uniqueUnitTests[cast(void*)ud] = true;
1833

1834 1
                    auto ad = new FuncAliasDeclaration(ud.ident, ud, false);
1835 1
                    ad.protection = ud.protection;
1836

1837 1
                    auto e = new DsymbolExp(Loc.initial, ad, false);
1838 1
                    exps.push(e);
1839
                }
1840
            }
1841

1842 1
            sds.members.foreachDsymbol(&symbolDg);
1843
        }
1844 1
        auto te = new TupleExp(e.loc, exps);
1845 1
        return te.expressionSemantic(sc);
1846
    }
1847 1
    if (e.ident == Id.getVirtualIndex)
1848
    {
1849 1
        if (dim != 1)
1850 1
            return dimError(1);
1851

1852 1
        auto o = (*e.args)[0];
1853 1
        auto s = getDsymbolWithoutExpCtx(o);
1854

1855 1
        auto fd = s ? s.isFuncDeclaration() : null;
1856 1
        if (!fd)
1857
        {
1858 0
            e.error("first argument to __traits(getVirtualIndex) must be a function");
1859 0
            return ErrorExp.get();
1860
        }
1861

1862 1
        fd = fd.toAliasFunc(); // Necessary to support multiple overloads.
1863 1
        return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t);
1864
    }
1865 1
    if (e.ident == Id.getPointerBitmap)
1866
    {
1867 1
        return pointerBitmap(e);
1868
    }
1869 1
    if (e.ident == Id.isZeroInit)
1870
    {
1871 1
        if (dim != 1)
1872 0
            return dimError(1);
1873

1874 1
        auto o = (*e.args)[0];
1875 1
        Type t = isType(o);
1876 1
        if (!t)
1877
        {
1878 1
            e.error("type expected as second argument of __traits `%s` instead of `%s`",
1879
                e.ident.toChars(), o.toChars());
1880 1
            return ErrorExp.get();
1881
        }
1882

1883 1
        Type tb = t.baseElemOf();
1884 1
        return tb.isZeroInit(e.loc) ? True() : False();
1885
    }
1886 1
    if (e.ident == Id.getTargetInfo)
1887
    {
1888 1
        if (dim != 1)
1889 1
            return dimError(1);
1890

1891 1
        auto ex = isExpression((*e.args)[0]);
1892 1
        StringExp se = ex ? ex.ctfeInterpret().toStringExp() : null;
1893 1
        if (!ex || !se || se.len == 0)
1894
        {
1895 1
            e.error("string expected as argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars());
1896 1
            return ErrorExp.get();
1897
        }
1898 1
        se = se.toUTF8(sc);
1899

1900 1
        const slice = se.peekString();
1901 1
        Expression r = target.getTargetInfo(slice.ptr, e.loc); // BUG: reliance on terminating 0
1902 1
        if (!r)
1903
        {
1904 1
            e.error("`getTargetInfo` key `\"%.*s\"` not supported by this implementation",
1905
                cast(int)slice.length, slice.ptr);
1906 1
            return ErrorExp.get();
1907
        }
1908 1
        return r.expressionSemantic(sc);
1909
    }
1910 1
    if (e.ident == Id.getLocation)
1911
    {
1912 1
        if (dim != 1)
1913 0
            return dimError(1);
1914 1
        auto arg0 = (*e.args)[0];
1915 1
        Dsymbol s = getDsymbolWithoutExpCtx(arg0);
1916 1
        if (!s || !s.loc.isValid())
1917
        {
1918 1
            e.error("can only get the location of a symbol, not `%s`", arg0.toChars());
1919 1
            return ErrorExp.get();
1920
        }
1921

1922 1
        const fd = s.isFuncDeclaration();
1923
        // FIXME:td.overnext is always set, even when using an index on it
1924
        //const td = s.isTemplateDeclaration();
1925 1
        if ((fd && fd.overnext) /*|| (td && td.overnext)*/)
1926
        {
1927 1
            e.error("cannot get location of an overload set, " ~
1928
                    "use `__traits(getOverloads, ..., \"%s\"%s)[N]` " ~
1929
                    "to get the Nth overload",
1930
                    arg0.toChars(), /*td ? ", true".ptr :*/ "".ptr);
1931 1
            return ErrorExp.get();
1932
        }
1933

1934 1
        auto exps = new Expressions(3);
1935 1
        (*exps)[0] = new StringExp(e.loc, s.loc.filename.toDString());
1936 1
        (*exps)[1] = new IntegerExp(e.loc, s.loc.linnum,Type.tint32);
1937 1
        (*exps)[2] = new IntegerExp(e.loc, s.loc.charnum,Type.tint32);
1938 1
        auto tup = new TupleExp(e.loc, exps);
1939 1
        return tup.expressionSemantic(sc);
1940
    }
1941

1942
    static const(char)[] trait_search_fp(const(char)[] seed, ref int cost)
1943
    {
1944
        //printf("trait_search_fp('%s')\n", seed);
1945 1
        if (!seed.length)
1946 0
            return null;
1947 1
        cost = 0;
1948 1
        const sv = traitsStringTable.lookup(seed);
1949 1
        return sv ? sv.toString() : null;
1950
    }
1951

1952 1
    if (auto sub = speller!trait_search_fp(e.ident.toString()))
1953 1
        e.error("unrecognized trait `%s`, did you mean `%.*s`?", e.ident.toChars(), cast(int) sub.length, sub.ptr);
1954
    else
1955 1
        e.error("unrecognized trait `%s`", e.ident.toChars());
1956 1
    return ErrorExp.get();
1957
}
1958

1959
/// compare arguments of __traits(isSame)
1960
private bool isSame(RootObject o1, RootObject o2, Scope* sc)
1961
{
1962
    static FuncLiteralDeclaration isLambda(RootObject oarg)
1963
    {
1964 1
        if (auto t = isDsymbol(oarg))
1965
        {
1966 1
            if (auto td = t.isTemplateDeclaration())
1967
            {
1968 1
                if (td.members && td.members.dim == 1)
1969
                {
1970 1
                    if (auto fd = (*td.members)[0].isFuncLiteralDeclaration())
1971 1
                        return fd;
1972
                }
1973
            }
1974
        }
1975 1
        else if (auto ea = isExpression(oarg))
1976
        {
1977 1
            if (ea.op == TOK.function_)
1978
            {
1979 1
                if (auto fe = cast(FuncExp)ea)
1980 1
                    return fe.fd;
1981
            }
1982
        }
1983 1
        return null;
1984
    }
1985

1986 1
    auto l1 = isLambda(o1);
1987 1
    auto l2 = isLambda(o2);
1988

1989 1
    if (l1 && l2)
1990
    {
1991
        import dmd.lambdacomp : isSameFuncLiteral;
1992 1
        if (isSameFuncLiteral(l1, l2, sc))
1993 1
            return true;
1994
    }
1995

1996
    // issue 12001, allow isSame, <BasicType>, <BasicType>
1997 1
    Type t1 = isType(o1);
1998 1
    Type t2 = isType(o2);
1999 1
    if (t1 && t2 && t1.equals(t2))
2000 1
        return true;
2001

2002 1
    auto s1 = getDsymbol(o1);
2003 1
    auto s2 = getDsymbol(o2);
2004
    //printf("isSame: %s, %s\n", o1.toChars(), o2.toChars());
2005
    version (none)
2006
    {
2007
        printf("o1: %p\n", o1);
2008
        printf("o2: %p\n", o2);
2009
        if (!s1)
2010
        {
2011
            if (auto ea = isExpression(o1))
2012
                printf("%s\n", ea.toChars());
2013
            if (auto ta = isType(o1))
2014
                printf("%s\n", ta.toChars());
2015
            return false;
2016
        }
2017
        else
2018
            printf("%s %s\n", s1.kind(), s1.toChars());
2019
    }
2020 1
    if (!s1 && !s2)
2021
    {
2022 1
        auto ea1 = isExpression(o1);
2023 1
        auto ea2 = isExpression(o2);
2024 1
        if (ea1 && ea2)
2025
        {
2026 1
            if (ea1.equals(ea2))
2027 1
                return true;
2028
        }
2029
    }
2030 1
    if (!s1 || !s2)
2031 1
        return false;
2032

2033 1
    s1 = s1.toAlias();
2034 1
    s2 = s2.toAlias();
2035

2036 1
    if (auto fa1 = s1.isFuncAliasDeclaration())
2037 0
        s1 = fa1.toAliasFunc();
2038 1
    if (auto fa2 = s2.isFuncAliasDeclaration())
2039 1
        s2 = fa2.toAliasFunc();
2040

2041
    // https://issues.dlang.org/show_bug.cgi?id=11259
2042
    // compare import symbol to a package symbol
2043
    static bool cmp(Dsymbol s1, Dsymbol s2)
2044
    {
2045 1
        auto imp = s1.isImport();
2046 1
        return imp && imp.pkg && imp.pkg == s2.isPackage();
2047
    }
2048

2049 1
    if (cmp(s1,s2) || cmp(s2,s1))
2050 1
        return true;
2051

2052 1
    if (s1 == s2)
2053 1
        return true;
2054

2055
    // https://issues.dlang.org/show_bug.cgi?id=18771
2056
    // OverloadSets are equal if they contain the same functions
2057 1
    auto overSet1 = s1.isOverloadSet();
2058 1
    if (!overSet1)
2059 1
        return false;
2060

2061 1
    auto overSet2 = s2.isOverloadSet();
2062 1
    if (!overSet2)
2063 0
        return false;
2064

2065 1
    if (overSet1.a.dim != overSet2.a.dim)
2066 0
        return false;
2067

2068
    // OverloadSets contain array of Dsymbols => O(n*n)
2069
    // to compare for equality as the order of overloads
2070
    // might not be the same
2071
Lnext:
2072 1
    foreach(overload1; overSet1.a)
2073
    {
2074 1
        foreach(overload2; overSet2.a)
2075
        {
2076 1
            if (overload1 == overload2)
2077 1
                continue Lnext;
2078
        }
2079 0
        return false;
2080
    }
2081 1
    return true;
2082
}

Read our documentation on viewing source code .

Loading