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-2023 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 https://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.astenums;
22 import dmd.attrib;
23 import dmd.canthrow;
24 import dmd.dclass;
25 import dmd.declaration;
26 import dmd.dimport;
27 import dmd.dinterpret;
28 import dmd.dmangle;
29 import dmd.dmodule;
30 import dmd.dscope;
31 import dmd.dsymbol;
32 import dmd.dsymbolsem;
33 import dmd.dtemplate;
34 import dmd.errors;
35 import dmd.expression;
36 import dmd.expressionsem;
37 import dmd.func;
38 import dmd.globals;
39 import dmd.hdrgen;
40 import dmd.id;
41 import dmd.identifier;
42 import dmd.location;
43 import dmd.mtype;
44 import dmd.nogc;
45 import dmd.parse;
46 import dmd.root.array;
47 import dmd.root.speller;
48 import dmd.root.stringtable;
49 import dmd.target;
50 import dmd.tokens;
51 import dmd.typesem;
52 import dmd.visitor;
53 import dmd.rootobject;
54 import dmd.common.outbuffer;
55 import dmd.root.string;
56 
57 enum LOGSEMANTIC = false;
58 
59 /************************ TraitsExp ************************************/
60 
61 /**************************************
62  * Convert `Expression` or `Type` to corresponding `Dsymbol`, additionally
63  * stripping off expression contexts.
64  *
65  * Some symbol related `__traits` ignore arguments expression contexts.
66  * For example:
67  * ----
68  *  struct S { void f() {} }
69  *  S s;
70  *  pragma(msg, __traits(isNested, s.f));
71  *  // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`.
72  * ----
73  *
74  * This is used for that common `__traits` behavior.
75  *
76  * Input:
77  *      oarg     object to get the symbol for
78  * Returns:
79  *      Dsymbol  the corresponding symbol for oarg
80  */
81 private Dsymbol getDsymbolWithoutExpCtx(RootObject oarg)
82 {
83     if (auto e = isExpression(oarg))
84     {
85         if (auto dve = e.isDotVarExp())
86             return dve.var;
87         if (auto dte = e.isDotTemplateExp())
88             return dte.td;
89     }
90     return getDsymbol(oarg);
91 }
92 
93 /**
94  * get an array of size_t values that indicate possible pointer words in memory
95  *  if interpreted as the type given as argument
96  * Returns: the size of the type in bytes, ulong.max on error
97  */
98 ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data)
99 {
100     ulong sz;
101     if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration())
102         sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(loc);
103     else
104         sz = t.size(loc);
105     if (sz == SIZE_INVALID)
106         return ulong.max;
107 
108     const sz_size_t = Type.tsize_t.size(loc);
109     if (sz > sz.max - sz_size_t)
110     {
111         error(loc, "size overflow for type `%s`", t.toChars());
112         return ulong.max;
113     }
114 
115     ulong bitsPerWord = sz_size_t * 8;
116     ulong cntptr = (sz + sz_size_t - 1) / sz_size_t;
117     ulong cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord;
118 
119     data.setDim(cast(size_t)cntdata);
120     data.zero();
121 
122     ulong offset;
123     bool error;
124 
125     void visit(Type t)
126     {
127         void setpointer(ulong off)
128         {
129             ulong ptroff = off / sz_size_t;
130             (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t));
131         }
132 
133         void visitType(Type t)
134         {
135             Type tb = t.toBasetype();
136             if (tb != t)
137                 visit(tb);
138         }
139 
140         void visitError(TypeError t)
141         {
142             visitType(t);
143         }
144 
145         void visitBasic(TypeBasic t)
146         {
147             if (t.ty == Tvoid)
148                 setpointer(offset);
149         }
150 
151         void visitVector(TypeVector t)
152         {
153         }
154 
155         void visitSArray(TypeSArray t)
156         {
157             ulong arrayoff = offset;
158             ulong nextsize = t.next.size();
159             if (nextsize == SIZE_INVALID)
160                 error = true;
161             ulong dim = t.dim.toInteger();
162             for (ulong i = 0; i < dim; i++)
163             {
164                 offset = arrayoff + i * nextsize;
165                 visit(t.next);
166             }
167             offset = arrayoff;
168         }
169 
170         void visitDArray(TypeDArray t)
171         {
172             setpointer(offset + sz_size_t);
173         }
174 
175         // dynamic array is {length,ptr}
176         void visitAArray(TypeAArray t)
177         {
178             setpointer(offset);
179         }
180 
181         void visitPointer(TypePointer t)
182         {
183             if (t.nextOf().ty != Tfunction) // don't mark function pointers
184                 setpointer(offset);
185         }
186 
187         void visitReference(TypeReference t)
188         {
189             setpointer(offset);
190         }
191 
192         void visitClass(TypeClass t)
193         {
194             setpointer(offset);
195         }
196 
197         void visitFunction(TypeFunction t)
198         {
199         }
200 
201         void visitDelegate(TypeDelegate t)
202         {
203             setpointer(offset);
204         }
205 
206         void visitEnum(TypeEnum t)
207         {
208             visitType(t);
209         }
210 
211         void visitTuple(TypeTuple t)
212         {
213             visitType(t);
214         }
215 
216         void visitNull(TypeNull t)
217         {
218             // always a null pointer
219         }
220 
221         void visitNoreturn(TypeNoreturn t)
222         {
223         }
224 
225         void visitStruct(TypeStruct t)
226         {
227             ulong structoff = offset;
228             foreach (v; t.sym.fields)
229             {
230                 offset = structoff + v.offset;
231                 if (v.type.ty == Tclass)
232                     setpointer(offset);
233                 else
234                     visit(v.type);
235             }
236             offset = structoff;
237         }
238 
239         void visitDefaultCase(Type t)
240         {
241             //printf("ty = %d\n", t.ty);
242             assert(0);
243         }
244 
245         mixin VisitType!void visit;
246         visit.VisitType(t);
247     }
248 
249     if (auto tc = t.isTypeClass())
250     {
251         // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
252         void visitTopLevelClass(TypeClass t)
253         {
254             ulong classoff = offset;
255             // skip vtable-ptr and monitor
256             if (t.sym.baseClass)
257                 visitTopLevelClass(t.sym.baseClass.type.isTypeClass());
258             foreach (v; t.sym.fields)
259             {
260                 offset = classoff + v.offset;
261                 visit(v.type);
262             }
263             offset = classoff;
264         }
265 
266         visitTopLevelClass(tc);
267     }
268     else
269         visit(t);
270     return error ? ulong.max : sz;
271 }
272 
273 /**
274  * get an array of size_t values that indicate possible pointer words in memory
275  *  if interpreted as the type given as argument
276  * the first array element is the size of the type for independent interpretation
277  *  of the array
278  * following elements bits represent one word (4/8 bytes depending on the target
279  *  architecture). If set the corresponding memory might contain a pointer/reference.
280  *
281  *  Returns: [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
282  */
283 private Expression pointerBitmap(TraitsExp e)
284 {
285     if (!e.args || e.args.length != 1)
286     {
287         error(e.loc, "a single type expected for trait pointerBitmap");
288         return ErrorExp.get();
289     }
290 
291     Type t = getType((*e.args)[0]);
292     if (!t)
293     {
294         error(e.loc, "`%s` is not a type", (*e.args)[0].toChars());
295         return ErrorExp.get();
296     }
297 
298     Array!(ulong) data;
299     ulong sz = getTypePointerBitmap(e.loc, t, &data);
300     if (sz == ulong.max)
301         return ErrorExp.get();
302 
303     auto exps = new Expressions(data.length + 1);
304     (*exps)[0] = new IntegerExp(e.loc, sz, Type.tsize_t);
305     foreach (size_t i; 1 .. exps.length)
306         (*exps)[i] = new IntegerExp(e.loc, data[cast(size_t) (i - 1)], Type.tsize_t);
307 
308     auto ale = new ArrayLiteralExp(e.loc, Type.tsize_t.sarrayOf(data.length + 1), exps);
309     return ale;
310 }
311 
312 Expression semanticTraits(TraitsExp e, Scope* sc)
313 {
314     static if (LOGSEMANTIC)
315     {
316         printf("TraitsExp::semantic() %s\n", e.toChars());
317     }
318 
319     if (e.ident != Id.compiles &&
320         e.ident != Id.isSame &&
321         e.ident != Id.identifier &&
322         e.ident != Id.getProtection && e.ident != Id.getVisibility &&
323         e.ident != Id.getAttributes)
324     {
325         // Pretend we're in a deprecated scope so that deprecation messages
326         // aren't triggered when checking if a symbol is deprecated
327         const save = sc.stc;
328         if (e.ident == Id.isDeprecated)
329             sc.stc |= STC.deprecated_;
330         if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1))
331         {
332             sc.stc = save;
333             return ErrorExp.get();
334         }
335         sc.stc = save;
336     }
337     size_t dim = e.args ? e.args.length : 0;
338 
339     Expression dimError(int expected)
340     {
341         error(e.loc, "expected %d arguments for `%s` but had %d", expected, e.ident.toChars(), cast(int)dim);
342         return ErrorExp.get();
343     }
344 
345     static IntegerExp True()
346     {
347         return IntegerExp.createBool(true);
348     }
349 
350     static IntegerExp False()
351     {
352         return IntegerExp.createBool(false);
353     }
354 
355     /********
356      * Gets the function type from a given AST node
357      * if the node is a function of some sort.
358      * Params:
359      *   o = an AST node to check for a `TypeFunction`
360      *   fdp = if `o` is a FuncDeclaration then fdp is set to that, otherwise `null`
361      * Returns:
362      *   a type node if `o` is a declaration of
363      *   a delegate, function, function-pointer or a variable of the former.
364      *   Otherwise, `null`.
365      */
366     static TypeFunction toTypeFunction(RootObject o, out FuncDeclaration fdp)
367     {
368         Type t;
369         if (auto s = getDsymbolWithoutExpCtx(o))
370         {
371             if (auto fd = s.isFuncDeclaration())
372             {
373                 t = fd.type;
374                 fdp = fd;
375             }
376             else if (auto vd = s.isVarDeclaration())
377                 t = vd.type;
378             else
379                 t = isType(o);
380         }
381         else
382             t = isType(o);
383 
384         if (t)
385         {
386             if (auto tf = t.isFunction_Delegate_PtrToFunction())
387                 return tf;
388         }
389 
390         return null;
391     }
392 
393     IntegerExp isX(T)(bool delegate(T) fp)
394     {
395         if (!dim)
396             return False();
397         foreach (o; *e.args)
398         {
399             static if (is(T == Type))
400                 auto y = getType(o);
401 
402             static if (is(T : Dsymbol))
403             {
404                 auto s = getDsymbolWithoutExpCtx(o);
405                 if (!s)
406                     return False();
407             }
408             static if (is(T == Dsymbol))
409                 alias y = s;
410             static if (is(T == Declaration))
411                 auto y = s.isDeclaration();
412             static if (is(T == FuncDeclaration))
413                 auto y = s.isFuncDeclaration();
414 
415             if (!y || !fp(y))
416                 return False();
417         }
418         return True();
419     }
420 
421     alias isTypeX = isX!Type;
422     alias isDsymX = isX!Dsymbol;
423     alias isDeclX = isX!Declaration;
424     alias isFuncX = isX!FuncDeclaration;
425 
426     Expression isPkgX(bool function(Package) fp)
427     {
428         return isDsymX((Dsymbol sym) {
429             Package p = resolveIsPackage(sym);
430             return (p !is null) && fp(p);
431         });
432     }
433 
434     if (e.ident == Id.isArithmetic)
435     {
436         return isTypeX(t => t.isintegral() || t.isfloating());
437     }
438     if (e.ident == Id.isFloating)
439     {
440         return isTypeX(t => t.isfloating());
441     }
442     if (e.ident == Id.isIntegral)
443     {
444         return isTypeX(t => t.isintegral());
445     }
446     if (e.ident == Id.isScalar)
447     {
448         return isTypeX(t => t.isscalar());
449     }
450     if (e.ident == Id.isUnsigned)
451     {
452         return isTypeX(t => t.isunsigned());
453     }
454     if (e.ident == Id.isAssociativeArray)
455     {
456         return isTypeX(t => t.toBasetype().ty == Taarray);
457     }
458     if (e.ident == Id.isDeprecated)
459     {
460         if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true))
461             return True();
462         return isDsymX(t => t.isDeprecated());
463     }
464     if (e.ident == Id.isFuture)
465     {
466        return isDeclX(t => t.isFuture());
467     }
468     if (e.ident == Id.isStaticArray)
469     {
470         return isTypeX(t => t.toBasetype().ty == Tsarray);
471     }
472     if (e.ident == Id.isAbstractClass)
473     {
474         return isTypeX(t => t.toBasetype().ty == Tclass &&
475                             (cast(TypeClass)t.toBasetype()).sym.isAbstract());
476     }
477     if (e.ident == Id.isFinalClass)
478     {
479         return isTypeX(t => t.toBasetype().ty == Tclass &&
480                             ((cast(TypeClass)t.toBasetype()).sym.storage_class & STC.final_) != 0);
481     }
482     if (e.ident == Id.isTemplate)
483     {
484         if (dim != 1)
485             return dimError(1);
486 
487         return isDsymX((s)
488         {
489             if (!s.toAlias().isOverloadable())
490                 return false;
491             return overloadApply(s,
492                 sm => sm.isTemplateDeclaration() !is null) != 0;
493         });
494     }
495     if (e.ident == Id.isPOD)
496     {
497         if (dim != 1)
498             return dimError(1);
499 
500         auto o = (*e.args)[0];
501         auto t = isType(o);
502         if (!t)
503         {
504             error(e.loc, "type expected as second argument of __traits `%s` instead of `%s`",
505                 e.ident.toChars(), o.toChars());
506             return ErrorExp.get();
507         }
508 
509         Type tb = t.baseElemOf();
510         if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
511         {
512             return sd.isPOD() ? True() : False();
513         }
514         return True();
515     }
516     if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit)
517     {
518         if (dim != 1)
519             return dimError(1);
520 
521         auto o = (*e.args)[0];
522         auto t = isType(o);
523         if (!t)
524         {
525             error(e.loc, "type expected as second argument of __traits `%s` instead of `%s`",
526                 e.ident.toChars(), o.toChars());
527             return ErrorExp.get();
528         }
529 
530         Type tb = t.baseElemOf();
531         if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
532         {
533             return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False())
534                  : (sd.hasCopyCtor ? True() : False());
535         }
536         return False();
537     }
538     if (e.ident == Id.isCopyable)
539     {
540         if (dim != 1)
541             return dimError(1);
542 
543         auto o = (*e.args)[0];
544         auto t = isType(o);
545         if (!t)
546         {
547             error(e.loc, "type expected as second argument of __traits `%s` instead of `%s`",
548                     e.ident.toChars(), o.toChars());
549             return ErrorExp.get();
550         }
551 
552         t = t.toBasetype();     // get the base in case `t` is an `enum`
553 
554         if (auto ts = t.isTypeStruct())
555         {
556             ts.sym.dsymbolSemantic(sc);
557         }
558 
559         return isCopyable(t) ? True() : False();
560     }
561 
562     if (e.ident == Id.isNested)
563     {
564         if (dim != 1)
565             return dimError(1);
566 
567         auto o = (*e.args)[0];
568         auto s = getDsymbolWithoutExpCtx(o);
569         if (!s)
570         {
571         }
572         else if (auto ad = s.isAggregateDeclaration())
573         {
574             return ad.isNested() ? True() : False();
575         }
576         else if (auto fd = s.isFuncDeclaration())
577         {
578             return fd.isNested() ? True() : False();
579         }
580 
581         error(e.loc, "aggregate or function expected instead of `%s`", o.toChars());
582         return ErrorExp.get();
583     }
584     if (e.ident == Id.isDisabled)
585     {
586         if (dim != 1)
587             return dimError(1);
588 
589         return isDeclX(f => f.isDisabled());
590     }
591     if (e.ident == Id.isAbstractFunction)
592     {
593         if (dim != 1)
594             return dimError(1);
595 
596         return isFuncX(f => f.isAbstract());
597     }
598     if (e.ident == Id.isVirtualFunction)
599     {
600         // @@@DEPRECATED2.121@@@
601         // Deprecated in 2.101 - Can be removed from 2.121
602         deprecation(e.loc, "`traits(isVirtualFunction)` is deprecated. Use `traits(isVirtualMethod)` instead");
603 
604         if (dim != 1)
605             return dimError(1);
606 
607         return isFuncX(f => f.isVirtual());
608     }
609     if (e.ident == Id.isVirtualMethod)
610     {
611         if (dim != 1)
612             return dimError(1);
613 
614         return isFuncX(f => f.isVirtualMethod());
615     }
616     if (e.ident == Id.isFinalFunction)
617     {
618         if (dim != 1)
619             return dimError(1);
620 
621         return isFuncX(f => f.isFinalFunc());
622     }
623     if (e.ident == Id.isOverrideFunction)
624     {
625         if (dim != 1)
626             return dimError(1);
627 
628         return isFuncX(f => f.isOverride());
629     }
630     if (e.ident == Id.isStaticFunction)
631     {
632         if (dim != 1)
633             return dimError(1);
634 
635         return isFuncX(f => !f.needThis() && !f.isNested());
636     }
637     if (e.ident == Id.isModule)
638     {
639         if (dim != 1)
640             return dimError(1);
641 
642         return isPkgX(p => p.isModule() || p.isPackageMod());
643     }
644     if (e.ident == Id.isPackage)
645     {
646         if (dim != 1)
647             return dimError(1);
648 
649         return isPkgX(p => p.isModule() is null);
650     }
651     if (e.ident == Id.isRef)
652     {
653         if (dim != 1)
654             return dimError(1);
655 
656         return isDeclX(d => d.isRef());
657     }
658     if (e.ident == Id.isOut)
659     {
660         if (dim != 1)
661             return dimError(1);
662 
663         return isDeclX(d => d.isOut());
664     }
665     if (e.ident == Id.isLazy)
666     {
667         if (dim != 1)
668             return dimError(1);
669 
670         return isDeclX(d => (d.storage_class & STC.lazy_) != 0);
671     }
672     if (e.ident == Id.identifier)
673     {
674         // Get identifier for symbol as a string literal
675         /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
676          * a symbol should not be folded to a constant.
677          * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
678          */
679         if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2))
680             return ErrorExp.get();
681         if (dim != 1)
682             return dimError(1);
683 
684         auto o = (*e.args)[0];
685         Identifier id;
686         if (auto po = isParameter(o))
687         {
688             if (!po.ident)
689             {
690                 error(e.loc, "argument `%s` has no identifier", po.type.toChars());
691                 return ErrorExp.get();
692             }
693             id = po.ident;
694         }
695         else
696         {
697             Dsymbol s = getDsymbolWithoutExpCtx(o);
698             if (!s || !s.ident)
699             {
700                 error(e.loc, "argument `%s` has no identifier", o.toChars());
701                 return ErrorExp.get();
702             }
703             id = s.ident;
704         }
705 
706         auto se = new StringExp(e.loc, id.toString());
707         return se.expressionSemantic(sc);
708     }
709     if (e.ident == Id.fullyQualifiedName) // https://dlang.org/spec/traits.html#fullyQualifiedName
710     {
711         if (dim != 1)
712             return dimError(1);
713 
714         Scope* sc2 = sc.push();
715         sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
716         bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
717         sc2.pop();
718         if (!ok)
719             return ErrorExp.get();
720 
721         const(char)[] fqn;
722         auto o = (*e.args)[0];
723         if (auto s = getDsymbolWithoutExpCtx(o))
724         {
725             if (s.semanticRun == PASS.initial)
726                 s.dsymbolSemantic(null);
727 
728             fqn = s.toPrettyChars().toDString();
729         }
730         else if (auto t = getType(o))
731         {
732             fqn = t.toPrettyChars(true).toDString();
733         }
734         else
735         {
736             if (!isError(o))
737                 error(e.loc, "argument `%s` has no identifier", o.toChars());
738             return ErrorExp.get();
739         }
740         assert(fqn);
741         auto se = new StringExp(e.loc, fqn);
742         return se.expressionSemantic(sc);
743 
744     }
745     if (e.ident == Id.getProtection || e.ident == Id.getVisibility)
746     {
747         if (dim != 1)
748             return dimError(1);
749 
750         Scope* sc2 = sc.push();
751         sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
752         bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
753         sc2.pop();
754         if (!ok)
755             return ErrorExp.get();
756 
757         auto o = (*e.args)[0];
758         auto s = getDsymbolWithoutExpCtx(o);
759         if (!s)
760         {
761             if (!isError(o))
762                 error(e.loc, "argument `%s` has no visibility", o.toChars());
763             return ErrorExp.get();
764         }
765         if (s.semanticRun == PASS.initial)
766             s.dsymbolSemantic(null);
767 
768         auto protName = visibilityToString(s.visible().kind); // TODO: How about package(names)
769         assert(protName);
770         auto se = new StringExp(e.loc, protName);
771         return se.expressionSemantic(sc);
772     }
773     if (e.ident == Id.parent)
774     {
775         if (dim != 1)
776             return dimError(1);
777 
778         auto o = (*e.args)[0];
779         auto s = getDsymbolWithoutExpCtx(o);
780         if (s)
781         {
782             // https://issues.dlang.org/show_bug.cgi?id=12496
783             // Consider:
784             // class T1
785             // {
786             //     class C(uint value) { }
787             // }
788             // __traits(parent, T1.C!2)
789             if (auto ad = s.isAggregateDeclaration())  // `s` is `C`
790             {
791                 if (ad.isNested())                     // `C` is nested
792                 {
793                     if (auto p = s.toParent())         // `C`'s parent is `C!2`, believe it or not
794                     {
795                         if (p.isTemplateInstance())    // `C!2` is a template instance
796                         {
797                             s = p;                     // `C!2`'s parent is `T1`
798                             auto td = (cast(TemplateInstance)p).tempdecl;
799                             if (td)
800                                 s = td;                // get the declaration context just in case there's two contexts
801                         }
802                     }
803                 }
804             }
805 
806             if (auto fd = s.isFuncDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=8943
807                 s = fd.toAliasFunc();
808             if (!s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=8922
809                 s = s.toParent();
810         }
811         if (!s || s.isImport())
812         {
813             error(e.loc, "argument `%s` has no parent", o.toChars());
814             return ErrorExp.get();
815         }
816 
817         if (auto f = s.isFuncDeclaration())
818         {
819             if (auto td = getFuncTemplateDecl(f))
820             {
821                 if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
822                     td = td.overroot; // then get the start
823                 Expression ex = new TemplateExp(e.loc, td, f);
824                 ex = ex.expressionSemantic(sc);
825                 return ex;
826             }
827             if (auto fld = f.isFuncLiteralDeclaration())
828             {
829                 // Directly translate to VarExp instead of FuncExp
830                 Expression ex = new VarExp(e.loc, fld, true);
831                 return ex.expressionSemantic(sc);
832             }
833         }
834         return symbolToExp(s, e.loc, sc, false);
835     }
836     if (e.ident == Id.child)
837     {
838         if (dim != 2)
839             return dimError(2);
840 
841         Expression ex;
842         auto op = (*e.args)[0];
843         if (auto symp = getDsymbol(op))
844             ex = new DsymbolExp(e.loc, symp);
845         else if (auto exp = op.isExpression())
846             ex = exp;
847         else
848         {
849             error(e.loc, "symbol or expression expected as first argument of __traits `child` instead of `%s`", op.toChars());
850             return ErrorExp.get();
851         }
852 
853         ex = ex.expressionSemantic(sc);
854         auto oc = (*e.args)[1];
855         auto symc = getDsymbol(oc);
856         if (!symc)
857         {
858             error(e.loc, "symbol expected as second argument of __traits `child` instead of `%s`", oc.toChars());
859             return ErrorExp.get();
860         }
861 
862         if (auto d = symc.isDeclaration())
863             ex = new DotVarExp(e.loc, ex, d);
864         else if (auto td = symc.isTemplateDeclaration())
865             ex = new DotExp(e.loc, ex, new TemplateExp(e.loc, td));
866         else if (auto ti = symc.isScopeDsymbol())
867             ex = new DotExp(e.loc, ex, new ScopeExp(e.loc, ti));
868         else
869             assert(0);
870 
871         ex = ex.expressionSemantic(sc);
872         return ex;
873     }
874     if (e.ident == Id.toType)
875     {
876         if (dim != 1)
877             return dimError(1);
878 
879         auto ex = isExpression((*e.args)[0]);
880         if (!ex)
881         {
882             error(e.loc, "expression expected as second argument of __traits `%s`", e.ident.toChars());
883             return ErrorExp.get();
884         }
885         ex = ex.ctfeInterpret();
886 
887         StringExp se = semanticString(sc, ex, "__traits(toType, string)");
888         if (!se)
889         {
890             return ErrorExp.get();
891         }
892         Type t = decoToType(se.toUTF8(sc).peekString());
893         if (!t)
894         {
895             error(e.loc, "cannot determine `%s`", e.toChars());
896             return ErrorExp.get();
897         }
898         return (new TypeExp(e.loc, t)).expressionSemantic(sc);
899     }
900     if (e.ident == Id.hasMember ||
901         e.ident == Id.getMember ||
902         e.ident == Id.getOverloads ||
903         e.ident == Id.getVirtualMethods ||
904         e.ident == Id.getVirtualFunctions)
905     {
906         if (dim != 2 && !(dim == 3 && e.ident == Id.getOverloads))
907             return dimError(2);
908 
909         auto o = (*e.args)[0];
910         auto ex = isExpression((*e.args)[1]);
911         if (!ex)
912         {
913             error(e.loc, "expression expected as second argument of __traits `%s`", e.ident.toChars());
914             return ErrorExp.get();
915         }
916         ex = ex.ctfeInterpret();
917 
918         bool includeTemplates = false;
919         if (dim == 3 && e.ident == Id.getOverloads)
920         {
921             auto b = isExpression((*e.args)[2]);
922             b = b.ctfeInterpret();
923             if (!b.type.equals(Type.tbool))
924             {
925                 error(e.loc, "`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b.toChars(), b.type.toChars());
926                 return ErrorExp.get();
927             }
928             includeTemplates = b.toBool().get();
929         }
930 
931         StringExp se = ex.toStringExp();
932         if (!se || se.len == 0)
933         {
934             error(e.loc, "string expected as second argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars());
935             return ErrorExp.get();
936         }
937         se = se.toUTF8(sc);
938 
939         if (se.sz != 1)
940         {
941             error(e.loc, "string must be chars");
942             return ErrorExp.get();
943         }
944         auto id = Identifier.idPool(se.peekString());
945 
946         /* Prefer a Type, because getDsymbol(Type) can lose type modifiers.
947            Then a Dsymbol, because it might need some runtime contexts.
948          */
949 
950         Dsymbol sym = getDsymbol(o);
951 
952         if (sym && e.ident == Id.hasMember)
953         {
954             if (auto sm = sym.search(e.loc, id))
955                 return True();
956 
957             // https://issues.dlang.org/show_bug.cgi?id=23951
958             if (auto decl = sym.isDeclaration())
959             {
960                 ex = typeDotIdExp(e.loc, decl.type, id);
961                 goto doSemantic;
962             }
963         }
964 
965         if (auto t = isType(o))
966             ex = typeDotIdExp(e.loc, t, id);
967         else if (sym)
968         {
969             ex = new DsymbolExp(e.loc, sym);
970             ex = new DotIdExp(e.loc, ex, id);
971         }
972         else if (auto ex2 = isExpression(o))
973             ex = new DotIdExp(e.loc, ex2, id);
974         else
975         {
976             error(e.loc, "invalid first argument");
977             return ErrorExp.get();
978         }
979     doSemantic:
980         // ignore symbol visibility and disable access checks for these traits
981         Scope* scx = sc.push();
982         scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck;
983         scope (exit) scx.pop();
984 
985         if (e.ident == Id.hasMember)
986         {
987             /* Take any errors as meaning it wasn't found
988              */
989             ex = ex.trySemantic(scx);
990             return ex ? True() : False();
991         }
992         else if (e.ident == Id.getMember)
993         {
994             if (auto die = ex.isDotIdExp())
995                 // Prevent semantic() from replacing Symbol with its initializer
996                 die.wantsym = true;
997             ex = ex.expressionSemantic(scx);
998             return ex;
999         }
1000         else if (e.ident == Id.getVirtualFunctions ||
1001                  e.ident == Id.getVirtualMethods ||
1002                  e.ident == Id.getOverloads)
1003         {
1004             uint errors = global.errors;
1005             Expression eorig = ex;
1006             ex = ex.expressionSemantic(scx);
1007             if (errors < global.errors)
1008                 error(e.loc, "`%s` cannot be resolved", eorig.toChars());
1009 
1010             if (e.ident == Id.getVirtualFunctions)
1011             {
1012                 // @@@DEPRECATED2.121@@@
1013                 // Deprecated in 2.101 - Can be removed from 2.121
1014                 deprecation(e.loc, "`traits(getVirtualFunctions)` is deprecated. Use `traits(getVirtualMethods)` instead");
1015             }
1016 
1017             /* Create tuple of functions of ex
1018              */
1019             auto exps = new Expressions();
1020             Dsymbol f;
1021             if (auto ve = ex.isVarExp)
1022             {
1023                 if (ve.var.isFuncDeclaration() || ve.var.isOverDeclaration())
1024                     f = ve.var;
1025                 ex = null;
1026             }
1027             else if (auto dve = ex.isDotVarExp)
1028             {
1029                 if (dve.var.isFuncDeclaration() || dve.var.isOverDeclaration())
1030                     f = dve.var;
1031                 if (dve.e1.op == EXP.dotType || dve.e1.op == EXP.this_)
1032                     ex = null;
1033                 else
1034                     ex = dve.e1;
1035             }
1036             else if (auto te = ex.isTemplateExp)
1037             {
1038                 auto td = te.td;
1039                 f = td;
1040                 if (td && td.funcroot)
1041                     f = td.funcroot;
1042                 ex = null;
1043             }
1044             else if (auto dte = ex.isDotTemplateExp)
1045             {
1046                 auto td = dte.td;
1047                 f = td;
1048                 if (td && td.funcroot)
1049                     f = td.funcroot;
1050                 ex = null;
1051                 if (dte.e1.op != EXP.dotType && dte.e1.op != EXP.this_)
1052                     ex = dte.e1;
1053             }
1054             bool[string] funcTypeHash;
1055 
1056             /* Compute the function signature and insert it in the
1057              * hashtable, if not present. This is needed so that
1058              * traits(getOverlods, F3, "visit") does not count `int visit(int)`
1059              * twice in the following example:
1060              *
1061              * =============================================
1062              * interface F1 { int visit(int);}
1063              * interface F2 { int visit(int); void visit(); }
1064              * interface F3 : F2, F1 {}
1065              *==============================================
1066              */
1067             void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e)
1068             {
1069                 auto signature = fd.type.toString();
1070                 //printf("%s - %s\n", fd.toChars, signature);
1071                 if (signature !in funcTypeHash)
1072                 {
1073                     funcTypeHash[signature] = true;
1074                     exps.push(e);
1075                 }
1076             }
1077 
1078             int dg(Dsymbol s)
1079             {
1080                 auto fd = s.isFuncDeclaration();
1081                 if (!fd)
1082                 {
1083                     if (includeTemplates)
1084                     {
1085                         if (auto td = s.isTemplateDeclaration())
1086                         {
1087                             // if td is part of an overload set we must take a copy
1088                             // which shares the same `instances` cache but without
1089                             // `overroot` and `overnext` set to avoid overload
1090                             // behaviour in the result.
1091                             if (td.overnext !is null)
1092                             {
1093                                 if (td.instances is null)
1094                                 {
1095                                     // create an empty AA just to copy it
1096                                     scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
1097                                     auto tib = TemplateInstanceBox(ti);
1098                                     td.instances[tib] = null;
1099                                     td.instances.clear();
1100                                 }
1101                                 td = td.syntaxCopy(null);
1102                                 import core.stdc.string : memcpy;
1103                                 memcpy(cast(void*) td, cast(void*) s,
1104                                         __traits(classInstanceSize, TemplateDeclaration));
1105                                 td.overroot = null;
1106                                 td.overnext = null;
1107                             }
1108 
1109                             auto e = ex ? new DotTemplateExp(Loc.initial, ex, td)
1110                                         : new DsymbolExp(Loc.initial, td);
1111                             exps.push(e);
1112                         }
1113                     }
1114                     return 0;
1115                 }
1116                 if (e.ident == Id.getVirtualFunctions && !fd.isVirtual())
1117                     return 0;
1118                 if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod())
1119                     return 0;
1120 
1121                 auto fa = new FuncAliasDeclaration(fd.ident, fd, false);
1122                 fa.visibility = fd.visibility;
1123 
1124                 auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false)
1125                             : new DsymbolExp(Loc.initial, fa, false);
1126 
1127                 // if the parent is an interface declaration
1128                 // we must check for functions with the same signature
1129                 // in different inherited interfaces
1130                 if (sym && sym.isInterfaceDeclaration())
1131                     insertInterfaceInheritedFunction(fd, e);
1132                 else
1133                     exps.push(e);
1134                 return 0;
1135             }
1136 
1137             InterfaceDeclaration ifd = null;
1138             if (sym)
1139                 ifd = sym.isInterfaceDeclaration();
1140             // If the symbol passed as a parameter is an
1141             // interface that inherits other interfaces
1142             overloadApply(f, &dg);
1143             if (ifd && ifd.interfaces && f)
1144             {
1145                 // check the overloads of each inherited interface individually
1146                 foreach (bc; ifd.interfaces)
1147                 {
1148                     if (auto fd = bc.sym.search(e.loc, f.ident))
1149                         overloadApply(fd, &dg);
1150                 }
1151             }
1152 
1153             auto tup = new TupleExp(e.loc, exps);
1154             return tup.expressionSemantic(scx);
1155         }
1156         else
1157             assert(0);
1158     }
1159     if (e.ident == Id.classInstanceSize || e.ident == Id.classInstanceAlignment)
1160     {
1161         if (dim != 1)
1162             return dimError(1);
1163 
1164         auto o = (*e.args)[0];
1165         auto s = getDsymbol(o);
1166         auto cd = s ? s.isClassDeclaration() : null;
1167         if (!cd)
1168         {
1169             error(e.loc, "first argument is not a class");
1170             return ErrorExp.get();
1171         }
1172         if (cd.sizeok != Sizeok.done)
1173         {
1174             cd.size(e.loc);
1175         }
1176         if (cd.sizeok != Sizeok.done)
1177         {
1178             error(e.loc, "%s `%s` is forward referenced", cd.kind(), cd.toChars());
1179             return ErrorExp.get();
1180         }
1181 
1182         return new IntegerExp(e.loc, e.ident == Id.classInstanceSize ? cd.structsize : cd.alignsize, Type.tsize_t);
1183     }
1184     if (e.ident == Id.getAliasThis)
1185     {
1186         if (dim != 1)
1187             return dimError(1);
1188 
1189         auto o = (*e.args)[0];
1190         auto s = getDsymbol(o);
1191         auto ad = s ? s.isAggregateDeclaration() : null;
1192 
1193         auto exps = new Expressions();
1194         if (ad && ad.aliasthis)
1195             exps.push(new StringExp(e.loc, ad.aliasthis.ident.toString()));
1196         Expression ex = new TupleExp(e.loc, exps);
1197         ex = ex.expressionSemantic(sc);
1198         return ex;
1199     }
1200     if (e.ident == Id.getAttributes)
1201     {
1202         /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
1203          * a symbol should not be folded to a constant.
1204          * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
1205          */
1206         if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3))
1207             return ErrorExp.get();
1208 
1209         if (dim != 1)
1210             return dimError(1);
1211 
1212         auto o = (*e.args)[0];
1213         auto po = isParameter(o);
1214         auto s = getDsymbolWithoutExpCtx(o);
1215         auto typeOfArg = isType(o);
1216         UserAttributeDeclaration udad = null;
1217         if (po)
1218         {
1219             udad = po.userAttribDecl;
1220         }
1221         else if (s)
1222         {
1223             // @@@DEPRECATION 2.100.2
1224             if (auto fd = s.isFuncDeclaration())
1225             {
1226                 if (fd.overnext)
1227                 {
1228                     deprecation(e.loc, "`__traits(getAttributes)` may only be used for individual functions, not the overload set `%s`", fd.toChars());
1229                     deprecationSupplemental(e.loc, "the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from");
1230                 }
1231             }
1232 
1233             // @@@DEPRECATION 2.100.2
1234             if (auto td = s.isTemplateDeclaration())
1235             {
1236                 if (td.overnext || td.overroot)
1237                 {
1238                     deprecation(e.loc, "`__traits(getAttributes)` may only be used for individual functions, not the overload set `%s`", td.ident.toChars());
1239                     deprecationSupplemental(e.loc, "the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from");
1240                 }
1241             }
1242             if (s.isImport())
1243             {
1244                 s = s.isImport().mod;
1245             }
1246             //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s._scope);
1247             udad = s.userAttribDecl;
1248         }
1249         else if (typeOfArg)
1250         {
1251             // If there is a type but no symbol, do nothing rather than erroring.
1252         }
1253         else
1254         {
1255             version (none)
1256             {
1257                 Expression x = isExpression(o);
1258                 Type t = isType(o);
1259                 if (x)
1260                     printf("e = %s %s\n", EXPtoString(x.op).ptr, x.toChars());
1261                 if (t)
1262                     printf("t = %d %s\n", t.ty, t.toChars());
1263             }
1264             error(e.loc, "first argument is not a symbol");
1265             return ErrorExp.get();
1266         }
1267 
1268         auto exps = udad ? udad.getAttributes() : new Expressions();
1269         auto tup = new TupleExp(e.loc, exps);
1270         return tup.expressionSemantic(sc);
1271     }
1272     if (e.ident == Id.getFunctionAttributes)
1273     {
1274         /* Extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
1275          * https://dlang.org/spec/traits.html#getFunctionAttributes
1276          */
1277         if (dim != 1)
1278             return dimError(1);
1279 
1280         FuncDeclaration fd;
1281         TypeFunction tf = toTypeFunction((*e.args)[0], fd);
1282 
1283         if (!tf)
1284         {
1285             error(e.loc, "first argument is not a function");
1286             return ErrorExp.get();
1287         }
1288 
1289         // https://issues.dlang.org/show_bug.cgi?id=19706
1290         // When getting the attributes of the instance of a
1291         // templated member function semantic tiargs does
1292         // not perform semantic3 on the instance.
1293         // For more information see FuncDeclaration.functionSemantic.
1294         // For getFunctionAttributes it is mandatory to do
1295         // attribute inference.
1296         if (fd && fd.parent && fd.parent.isTemplateInstance)
1297         {
1298             fd.functionSemantic3();
1299             tf = cast(TypeFunction)fd.type;
1300         }
1301 
1302         auto mods = new Expressions();
1303 
1304         void addToMods(string str)
1305         {
1306             mods.push(new StringExp(Loc.initial, str));
1307         }
1308         tf.modifiersApply(&addToMods);
1309         tf.attributesApply(&addToMods, TRUSTformatSystem);
1310 
1311         auto tup = new TupleExp(e.loc, mods);
1312         return tup.expressionSemantic(sc);
1313     }
1314     if (e.ident == Id.isReturnOnStack)
1315     {
1316         /* Extract as a boolean if function return value is on the stack
1317          * https://dlang.org/spec/traits.html#isReturnOnStack
1318          */
1319         if (dim != 1)
1320             return dimError(1);
1321 
1322         RootObject o = (*e.args)[0];
1323         FuncDeclaration fd;
1324         TypeFunction tf = toTypeFunction(o, fd);
1325 
1326         if (!tf)
1327         {
1328             error(e.loc, "argument to `__traits(isReturnOnStack, %s)` is not a function", o.toChars());
1329             return ErrorExp.get();
1330         }
1331 
1332         bool value = target.isReturnOnStack(tf, fd && fd.needThis());
1333         return IntegerExp.createBool(value);
1334     }
1335     if (e.ident == Id.getFunctionVariadicStyle)
1336     {
1337         /* Accept a symbol or a type. Returns one of the following:
1338          *  "none"      not a variadic function
1339          *  "argptr"    extern(D) void dstyle(...), use `__argptr` and `__arguments`
1340          *  "stdarg"    extern(C) void cstyle(int, ...), use core.stdc.stdarg
1341          *  "typesafe"  void typesafe(T[] ...)
1342          *  "KR"        old K+R style
1343          */
1344         // get symbol linkage as a string
1345         if (dim != 1)
1346             return dimError(1);
1347 
1348         LINK link;
1349         VarArg varargs;
1350         auto o = (*e.args)[0];
1351 
1352         FuncDeclaration fd;
1353         TypeFunction tf = toTypeFunction(o, fd);
1354 
1355         if (tf)
1356         {
1357             link = tf.linkage;
1358             varargs = tf.parameterList.varargs;
1359         }
1360         else
1361         {
1362             if (!fd)
1363             {
1364                 error(e.loc, "argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o.toChars());
1365                 return ErrorExp.get();
1366             }
1367             link = fd._linkage;
1368             varargs = fd.getParameterList().varargs;
1369         }
1370         string style;
1371         final switch (varargs)
1372         {
1373             case VarArg.none:     style = "none";           break;
1374             case VarArg.variadic: style = (link == LINK.d)
1375                                              ? "argptr"
1376                                              : "stdarg";    break;
1377             case VarArg.KRvariadic: style = "KR";           break;
1378             case VarArg.typesafe: style = "typesafe";       break;
1379         }
1380         auto se = new StringExp(e.loc, style);
1381         return se.expressionSemantic(sc);
1382     }
1383     if (e.ident == Id.getParameterStorageClasses)
1384     {
1385         /* Accept a function symbol or a type, followed by a parameter index.
1386          * Returns a tuple of strings of the parameter's storage classes.
1387          */
1388         // get symbol linkage as a string
1389         if (dim != 2)
1390             return dimError(2);
1391 
1392         auto o = (*e.args)[0];
1393         auto o1 = (*e.args)[1];
1394 
1395         ParameterList fparams;
1396 
1397         CallExp ce;
1398         if (auto exp = isExpression(o))
1399             ce = exp.isCallExp();
1400 
1401         if (ce)
1402         {
1403             fparams = ce.f.getParameterList();
1404         }
1405         else
1406         {
1407             FuncDeclaration fd;
1408             auto tf = toTypeFunction(o, fd);
1409             if (tf)
1410                 fparams = tf.parameterList;
1411             else if (fd)
1412                 fparams = fd.getParameterList();
1413             else
1414             {
1415                 error(e.loc, "first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function or a function call",
1416                     o.toChars(), o1.toChars());
1417                 return ErrorExp.get();
1418             }
1419         }
1420 
1421         // Avoid further analysis for invalid functions leading to misleading error messages
1422         if (!fparams.parameters)
1423             return ErrorExp.get();
1424 
1425         StorageClass stc;
1426 
1427         // Set stc to storage class of the ith parameter
1428         auto ex = isExpression((*e.args)[1]);
1429         if (!ex)
1430         {
1431             error(e.loc, "expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
1432                 o.toChars(), o1.toChars());
1433             return ErrorExp.get();
1434         }
1435         ex = ex.ctfeInterpret();
1436         auto ii = ex.toUInteger();
1437         if (ii >= fparams.length)
1438         {
1439             error(e.loc, "parameter index must be in range 0..%u not %s", cast(uint)fparams.length, ex.toChars());
1440             return ErrorExp.get();
1441         }
1442 
1443         uint n = cast(uint)ii;
1444         Parameter p = fparams[n];
1445         stc = p.storageClass;
1446 
1447         // This mirrors hdrgen.visit(Parameter p)
1448         if (p.type && p.type.mod & MODFlags.shared_)
1449             stc &= ~STC.shared_;
1450 
1451         auto exps = new Expressions;
1452 
1453         void push(string s)
1454         {
1455             exps.push(new StringExp(e.loc, s));
1456         }
1457 
1458         if (stc & STC.auto_)
1459             push("auto");
1460         if (stc & STC.return_)
1461             push("return");
1462 
1463         if (stc & STC.out_)
1464             push("out");
1465         else if (stc & STC.in_)
1466             push("in");
1467         else if (stc & STC.ref_)
1468             push("ref");
1469         else if (stc & STC.lazy_)
1470             push("lazy");
1471         else if (stc & STC.alias_)
1472             push("alias");
1473 
1474         if (stc & STC.const_)
1475             push("const");
1476         if (stc & STC.immutable_)
1477             push("immutable");
1478         if (stc & STC.wild)
1479             push("inout");
1480         if (stc & STC.shared_)
1481             push("shared");
1482         if (stc & STC.scope_ && !(stc & STC.scopeinferred))
1483             push("scope");
1484 
1485         auto tup = new TupleExp(e.loc, exps);
1486         return tup.expressionSemantic(sc);
1487     }
1488     if (e.ident == Id.getLinkage)
1489     {
1490         // get symbol linkage as a string
1491         if (dim != 1)
1492             return dimError(1);
1493 
1494         LINK link;
1495         auto o = (*e.args)[0];
1496 
1497         FuncDeclaration fd;
1498         TypeFunction tf = toTypeFunction(o, fd);
1499 
1500         if (tf)
1501         {
1502             link = fd ? fd.toAliasFunc()._linkage : tf.linkage;
1503         }
1504         else
1505         {
1506             auto s = getDsymbol(o);
1507             Declaration d;
1508             AggregateDeclaration agg;
1509             if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null))
1510             {
1511                 error(e.loc, "argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars());
1512                 return ErrorExp.get();
1513             }
1514 
1515             if (d !is null)
1516                 link = d._linkage;
1517             else
1518             {
1519                 // Resolves forward references
1520                 if (agg.sizeok != Sizeok.done)
1521                 {
1522                     agg.size(e.loc);
1523                     if (agg.sizeok != Sizeok.done)
1524                     {
1525                         error(e.loc, "%s `%s` is forward referenced", agg.kind(), agg.toChars());
1526                         return ErrorExp.get();
1527                     }
1528                 }
1529 
1530                 final switch (agg.classKind)
1531                 {
1532                     case ClassKind.d:
1533                         link = LINK.d;
1534                         break;
1535                     case ClassKind.cpp:
1536                         link = LINK.cpp;
1537                         break;
1538                     case ClassKind.objc:
1539                         link = LINK.objc;
1540                         break;
1541                     case ClassKind.c:
1542                         link = LINK.c;
1543                         break;
1544                 }
1545             }
1546         }
1547         auto linkage = linkageToChars(link);
1548         auto se = new StringExp(e.loc, linkage.toDString());
1549         return se.expressionSemantic(sc);
1550     }
1551     if (e.ident == Id.allMembers ||
1552         e.ident == Id.derivedMembers)
1553     {
1554         if (dim != 1)
1555             return dimError(1);
1556 
1557         auto o = (*e.args)[0];
1558         auto s = getDsymbol(o);
1559         if (!s)
1560         {
1561             error(e.loc, "in expression `%s` `%s` can't have members", e.toChars(), o.toChars());
1562             errorSupplemental(e.loc, "`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", o.toChars());
1563 
1564             return ErrorExp.get();
1565         }
1566         if (auto imp = s.isImport())
1567         {
1568             // https://issues.dlang.org/show_bug.cgi?id=9692
1569             // https://issues.dlang.org/show_bug.cgi?id=20008
1570             if (imp.pkg)
1571                 s = imp.pkg;
1572         }
1573 
1574         // https://issues.dlang.org/show_bug.cgi?id=16044
1575         if (auto p = s.isPackage())
1576         {
1577             if (auto pm = p.isPackageMod())
1578                 s = pm;
1579         }
1580 
1581         auto sds = s.isScopeDsymbol();
1582         if (!sds || sds.isTemplateDeclaration())
1583         {
1584             error(e.loc, "in expression `%s` %s `%s` has no members", e.toChars(), s.kind(), s.toChars());
1585             errorSupplemental(e.loc, "`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", s.toChars());
1586             return ErrorExp.get();
1587         }
1588 
1589         auto idents = new Identifiers();
1590 
1591         int pushIdentsDg(size_t n, Dsymbol sm)
1592         {
1593             if (!sm)
1594                 return 1;
1595 
1596             // skip local symbols, such as static foreach loop variables
1597             if (auto decl = sm.isDeclaration())
1598             {
1599                 if (decl.storage_class & STC.local)
1600                 {
1601                     return 0;
1602                 }
1603                 // skip 'this' context pointers
1604                 else if (decl.isThisDeclaration())
1605                     return 0;
1606             }
1607 
1608             // https://issues.dlang.org/show_bug.cgi?id=20915
1609             // skip version and debug identifiers
1610             if (sm.isVersionSymbol() || sm.isDebugSymbol())
1611                 return 0;
1612 
1613             //printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
1614             if (sm.ident)
1615             {
1616                 // https://issues.dlang.org/show_bug.cgi?id=10096
1617                 // https://issues.dlang.org/show_bug.cgi?id=10100
1618                 // Skip over internal members in __traits(allMembers)
1619                 if ((sm.isCtorDeclaration() && sm.ident != Id.ctor) ||
1620                     (sm.isDtorDeclaration() && sm.ident != Id.dtor) ||
1621                     (sm.isPostBlitDeclaration() && sm.ident != Id.postblit) ||
1622                     sm.isInvariantDeclaration() ||
1623                     sm.isUnitTestDeclaration())
1624 
1625                 {
1626                     return 0;
1627                 }
1628                 if (sm.ident == Id.empty)
1629                 {
1630                     return 0;
1631                 }
1632                 if (sm.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177
1633                     return 0;
1634                 if ((!sds.isModule() && !sds.isPackage()) && sm.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057
1635                     return 0;
1636 
1637                 //printf("\t%s\n", sm.ident.toChars());
1638 
1639                 /* Skip if already present in idents[]
1640                  */
1641                 foreach (id; *idents)
1642                 {
1643                     if (id == sm.ident)
1644                         return 0;
1645 
1646                     // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
1647                     debug
1648                     {
1649                         import core.stdc.string : strcmp;
1650                         assert(strcmp(id.toChars(), sm.ident.toChars()) != 0);
1651                     }
1652                 }
1653                 idents.push(sm.ident);
1654             }
1655             else if (auto ed = sm.isEnumDeclaration())
1656             {
1657                 ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg);
1658             }
1659             return 0;
1660         }
1661 
1662         ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg);
1663         auto cd = sds.isClassDeclaration();
1664         if (cd && e.ident == Id.allMembers)
1665         {
1666             if (cd.semanticRun < PASS.semanticdone)
1667                 cd.dsymbolSemantic(null); // https://issues.dlang.org/show_bug.cgi?id=13668
1668                                    // Try to resolve forward reference
1669 
1670             void pushBaseMembersDg(ClassDeclaration cd)
1671             {
1672                 for (size_t i = 0; i < cd.baseclasses.length; i++)
1673                 {
1674                     auto cb = (*cd.baseclasses)[i].sym;
1675                     assert(cb);
1676                     ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg);
1677                     if (cb.baseclasses.length)
1678                         pushBaseMembersDg(cb);
1679                 }
1680             }
1681 
1682             pushBaseMembersDg(cd);
1683         }
1684 
1685         // Turn Identifiers into StringExps reusing the allocated array
1686         assert(Expressions.sizeof == Identifiers.sizeof);
1687         auto exps = cast(Expressions*)idents;
1688         foreach (i, id; *idents)
1689         {
1690             auto se = new StringExp(e.loc, id.toString());
1691             (*exps)[i] = se;
1692         }
1693 
1694         /* Making this a tuple is more flexible, as it can be statically unrolled.
1695          * To make an array literal, enclose __traits in [ ]:
1696          *   [ __traits(allMembers, ...) ]
1697          */
1698         Expression ex = new TupleExp(e.loc, exps);
1699         ex = ex.expressionSemantic(sc);
1700         return ex;
1701     }
1702     if (e.ident == Id.compiles)
1703     {
1704         /* Determine if all the objects - types, expressions, or symbols -
1705          * compile without error
1706          */
1707         if (!dim)
1708             return False();
1709 
1710         foreach (o; *e.args)
1711         {
1712             uint errors = global.startGagging();
1713             Scope* sc2 = sc.push();
1714             sc2.tinst = null;
1715             sc2.minst = null;   // this is why code for these are not emitted to object file
1716             sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst;
1717 
1718             bool err = false;
1719 
1720             auto t = isType(o);
1721             auto ex = isExpression(o);
1722             if (t)
1723             {
1724                 Dsymbol s;
1725                 t.resolve(e.loc, sc2, ex, t, s);
1726                 if (t)
1727                 {
1728                     t.typeSemantic(e.loc, sc2);
1729                     if (t.ty == Terror)
1730                         err = true;
1731                 }
1732                 else if (s && s.errors)
1733                     err = true;
1734             }
1735             if (ex)
1736             {
1737                 ex = ex.expressionSemantic(sc2);
1738                 ex = resolvePropertiesOnly(sc2, ex);
1739                 ex = ex.optimize(WANTvalue);
1740                 if (sc2.func && sc2.func.type.ty == Tfunction)
1741                 {
1742                     const tf = cast(TypeFunction)sc2.func.type;
1743                     err |= tf.isnothrow && canThrow(ex, sc2.func, null);
1744                 }
1745                 ex = checkGC(sc2, ex);
1746                 if (ex.op == EXP.error)
1747                     err = true;
1748             }
1749 
1750             // Carefully detach the scope from the parent and throw it away as
1751             // we only need it to evaluate the expression
1752             // https://issues.dlang.org/show_bug.cgi?id=15428
1753             sc2.detach();
1754 
1755             if (global.endGagging(errors) || err)
1756             {
1757                 return False();
1758             }
1759         }
1760         return True();
1761     }
1762     if (e.ident == Id.isSame)
1763     {
1764         /* Determine if two symbols are the same
1765          */
1766         if (dim != 2)
1767             return dimError(2);
1768 
1769         // https://issues.dlang.org/show_bug.cgi?id=20761
1770         // tiarg semantic may expand in place the list of arguments, for example:
1771         //
1772         //     before tiarg sema:  __traits(isSame, seq!(0,0), seq!(1,1))
1773         //     after            :  __traits(isSame, 0, 0, 1, 1)
1774         //
1775         // so we split in two lists
1776         Objects ob1;
1777         ob1.push((*e.args)[0]);
1778         Objects ob2;
1779         ob2.push((*e.args)[1]);
1780         if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0))
1781             return ErrorExp.get();
1782         if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0))
1783             return ErrorExp.get();
1784         if (ob1.length != ob2.length)
1785             return False();
1786         foreach (immutable i; 0 .. ob1.length)
1787             if (!ob1[i].isSame(ob2[i], sc))
1788                 return False();
1789         return True();
1790     }
1791     if (e.ident == Id.getUnitTests)
1792     {
1793         if (dim != 1)
1794             return dimError(1);
1795 
1796         auto o = (*e.args)[0];
1797         auto s = getDsymbolWithoutExpCtx(o);
1798         if (!s)
1799         {
1800             error(e.loc, "argument `%s` to __traits(getUnitTests) must be a module or aggregate",
1801                 o.toChars());
1802             return ErrorExp.get();
1803         }
1804         if (auto imp = s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990
1805             s = imp.mod;
1806 
1807         auto sds = s.isScopeDsymbol();
1808         if (!sds || sds.isTemplateDeclaration())
1809         {
1810             error(e.loc, "argument `%s` to __traits(getUnitTests) must be a module or aggregate, not a %s",
1811                 s.toChars(), s.kind());
1812             return ErrorExp.get();
1813         }
1814 
1815         auto exps = new Expressions();
1816         if (global.params.useUnitTests)
1817         {
1818             bool[void*] uniqueUnitTests;
1819 
1820             void symbolDg(Dsymbol s)
1821             {
1822                 if (auto ad = s.isAttribDeclaration())
1823                 {
1824                     ad.include(null).foreachDsymbol(&symbolDg);
1825                 }
1826                 else if (auto tm = s.isTemplateMixin())
1827                 {
1828                     tm.members.foreachDsymbol(&symbolDg);
1829                 }
1830                 else if (auto ud = s.isUnitTestDeclaration())
1831                 {
1832                     if (cast(void*)ud in uniqueUnitTests)
1833                         return;
1834 
1835                     uniqueUnitTests[cast(void*)ud] = true;
1836 
1837                     auto ad = new FuncAliasDeclaration(ud.ident, ud, false);
1838                     ad.visibility = ud.visibility;
1839 
1840                     auto e = new DsymbolExp(Loc.initial, ad, false);
1841                     exps.push(e);
1842                 }
1843             }
1844 
1845             sds.members.foreachDsymbol(&symbolDg);
1846         }
1847         auto te = new TupleExp(e.loc, exps);
1848         return te.expressionSemantic(sc);
1849     }
1850     if (e.ident == Id.getVirtualIndex)
1851     {
1852         if (dim != 1)
1853             return dimError(1);
1854 
1855         auto o = (*e.args)[0];
1856         auto s = getDsymbolWithoutExpCtx(o);
1857 
1858         auto fd = s ? s.isFuncDeclaration() : null;
1859         if (!fd)
1860         {
1861             error(e.loc, "first argument to __traits(getVirtualIndex) must be a function");
1862             return ErrorExp.get();
1863         }
1864 
1865         fd = fd.toAliasFunc(); // Necessary to support multiple overloads.
1866         return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t);
1867     }
1868     if (e.ident == Id.getPointerBitmap)
1869     {
1870         return pointerBitmap(e);
1871     }
1872     if (e.ident == Id.initSymbol)
1873     {
1874         if (dim != 1)
1875             return dimError(1);
1876 
1877         auto o = (*e.args)[0];
1878         Type t = isType(o);
1879         AggregateDeclaration ad = t ? isAggregate(t) : null;
1880 
1881         // Interfaces don't have an init symbol and hence cause linker errors
1882         if (!ad || ad.isInterfaceDeclaration())
1883         {
1884             error(e.loc, "struct / class type expected as argument to __traits(initSymbol) instead of `%s`", o.toChars());
1885             return ErrorExp.get();
1886         }
1887 
1888         Declaration d = new SymbolDeclaration(ad.loc, ad);
1889         d.type = Type.tvoid.arrayOf().constOf();
1890         d.storage_class |= STC.rvalue;
1891         return new VarExp(e.loc, d);
1892     }
1893     if (e.ident == Id.isZeroInit)
1894     {
1895         if (dim != 1)
1896             return dimError(1);
1897 
1898         auto o = (*e.args)[0];
1899         Type t = isType(o);
1900         if (!t)
1901         {
1902             error(e.loc, "type expected as second argument of __traits `%s` instead of `%s`",
1903                 e.ident.toChars(), o.toChars());
1904             return ErrorExp.get();
1905         }
1906 
1907         // https://issues.dlang.org/show_bug.cgi?id=23534
1908         //
1909         // For enums, we need to get the enum initializer
1910         // (the first enum member), not the initializer of the
1911         // type of the enum members.
1912         Type tb = t.isTypeEnum ? t : t.baseElemOf();
1913         return tb.isZeroInit(e.loc) ? True() : False();
1914     }
1915     if (e.ident == Id.getTargetInfo)
1916     {
1917         if (dim != 1)
1918             return dimError(1);
1919 
1920         auto ex = isExpression((*e.args)[0]);
1921         StringExp se = ex ? ex.ctfeInterpret().toStringExp() : null;
1922         if (!ex || !se || se.len == 0)
1923         {
1924             error(e.loc, "string expected as argument of __traits `%s` instead of `%s`", e.ident.toChars(), (*e.args)[0].toChars());
1925             return ErrorExp.get();
1926         }
1927         se = se.toUTF8(sc);
1928 
1929         const slice = se.peekString();
1930         Expression r = target.getTargetInfo(slice.ptr, e.loc); // BUG: reliance on terminating 0
1931         if (!r)
1932         {
1933             error(e.loc, "`getTargetInfo` key `\"%.*s\"` not supported by this implementation",
1934                 cast(int)slice.length, slice.ptr);
1935             return ErrorExp.get();
1936         }
1937         return r.expressionSemantic(sc);
1938     }
1939     if (e.ident == Id.getLocation)
1940     {
1941         if (dim != 1)
1942             return dimError(1);
1943         auto arg0 = (*e.args)[0];
1944         Dsymbol s = getDsymbolWithoutExpCtx(arg0);
1945         if (!s || !s.loc.isValid())
1946         {
1947             error(e.loc, "can only get the location of a symbol, not `%s`", arg0.toChars());
1948             return ErrorExp.get();
1949         }
1950 
1951         const fd = s.isFuncDeclaration();
1952         // FIXME:td.overnext is always set, even when using an index on it
1953         //const td = s.isTemplateDeclaration();
1954         if ((fd && fd.overnext) /*|| (td && td.overnext)*/)
1955         {
1956             error(e.loc, "cannot get location of an overload set, " ~
1957                     "use `__traits(getOverloads, ..., \"%s\"%s)[N]` " ~
1958                     "to get the Nth overload",
1959                     arg0.toChars(), /*td ? ", true".ptr :*/ "".ptr);
1960             return ErrorExp.get();
1961         }
1962 
1963         auto exps = new Expressions(3);
1964         (*exps)[0] = new StringExp(e.loc, s.loc.filename.toDString());
1965         (*exps)[1] = new IntegerExp(e.loc, s.loc.linnum,Type.tint32);
1966         (*exps)[2] = new IntegerExp(e.loc, s.loc.charnum,Type.tint32);
1967         auto tup = new TupleExp(e.loc, exps);
1968         return tup.expressionSemantic(sc);
1969     }
1970     if (e.ident == Id.getCppNamespaces)
1971     {
1972         auto o = (*e.args)[0];
1973         auto s = getDsymbolWithoutExpCtx(o);
1974         auto exps = new Expressions(0);
1975         if (auto d = s.isDeclaration())
1976         {
1977             if (d.inuse)
1978             {
1979                 .error(d.loc, "%s `%s` circular reference in `__traits(GetCppNamespaces,...)`", d.kind, d.toPrettyChars);
1980                 return ErrorExp.get();
1981             }
1982             d.inuse = 1;
1983         }
1984 
1985         /**
1986          Prepend the namespaces in the linked list `ns` to `es`.
1987 
1988          Returns: true if `ns` contains an `ErrorExp`.
1989          */
1990         bool prependNamespaces(Expressions* es, CPPNamespaceDeclaration ns)
1991         {
1992             // Semantic processing will convert `extern(C++, "a", "b", "c")`
1993             // into `extern(C++, "a") extern(C++, "b") extern(C++, "c")`,
1994             // creating a linked list what `a`'s `cppnamespace` points to `b`,
1995             // and `b`'s points to `c`. Our entry point is `a`.
1996             for (; ns !is null; ns = ns.cppnamespace)
1997             {
1998                 ns.dsymbolSemantic(sc);
1999 
2000                 if (ns.exp.isErrorExp())
2001                     return true;
2002 
2003                 auto se = ns.exp.toStringExp();
2004                 // extern(C++, (emptyTuple))
2005                 // struct D {}
2006                 // will produce a blank ident
2007                 if (!se.len)
2008                     continue;
2009                 es.insert(0, se);
2010             }
2011             return false;
2012         }
2013         for (auto p = s; !p.isModule(); p = p.toParent())
2014         {
2015             p.dsymbolSemantic(sc);
2016             auto pp = p.toParent();
2017             if (pp.isTemplateInstance())
2018             {
2019                 if (!p.cppnamespace)
2020                     continue;
2021                 //if (!p.toParent().cppnamespace)
2022                 //    continue;
2023                 auto inner = new Expressions(0);
2024                 auto outer = new Expressions(0);
2025                 if (prependNamespaces(inner,  p.cppnamespace)) return ErrorExp.get();
2026                 if (prependNamespaces(outer, pp.cppnamespace)) return ErrorExp.get();
2027 
2028                 size_t i = 0;
2029                 while(i < outer.length && ((*inner)[i]) == (*outer)[i])
2030                     i++;
2031 
2032                 foreach_reverse (ns; (*inner)[][i .. $])
2033                     exps.insert(0, ns);
2034                 continue;
2035             }
2036 
2037             if (p.isNspace())
2038                 exps.insert(0, new StringExp(p.loc, p.ident.toString()));
2039 
2040             if (prependNamespaces(exps, p.cppnamespace))
2041                 return ErrorExp.get();
2042         }
2043         if (auto d = s.isDeclaration())
2044             d.inuse = 0;
2045         auto tup = new TupleExp(e.loc, exps);
2046         return tup.expressionSemantic(sc);
2047     }
2048     //https://issues.dlang.org/show_bug.cgi?id=22291
2049     if (e.ident == Id.parameters)
2050     {
2051         //No args are valid
2052         if (e.args)
2053         {
2054             char[] contents = cast(char[]) e.args.toString();
2055             contents = contents[1..$];
2056             contents[$-1] = '\0';
2057             error(e.loc, "`__traits(parameters)` cannot have arguments, but `%s` was supplied", contents.ptr);
2058             return ErrorExp.get();
2059         }
2060 
2061         auto fd = sc.getEnclosingFunction();
2062         if (!fd)
2063         {
2064             error(e.loc, "`__traits(parameters)` may only be used inside a function");
2065             return ErrorExp.get();
2066         }
2067 
2068         auto tf = fd.type.isTypeFunction();
2069         assert(tf);
2070         auto exps = new Expressions(0);
2071         int addParameterDG(size_t idx, Parameter x)
2072         {
2073             assert(x.ident);
2074             exps.push(new IdentifierExp(e.loc, x.ident));
2075             return 0;
2076         }
2077         /*
2078             This is required since not all "parameters" actually have a name
2079             until they (tuples) are expanded e.g. an anonymous tuple parameter's
2080             contents get given names but not the tuple itself.
2081         */
2082         Parameter._foreach(tf.parameterList.parameters, &addParameterDG);
2083         auto tup = new TupleExp(e.loc, exps);
2084         return tup.expressionSemantic(sc);
2085     }
2086 
2087     /* Can't find the identifier. Try a spell check for a better error message
2088      */
2089     traitNotFound(e);
2090 
2091     return ErrorExp.get();
2092 }
2093 
2094 /// compare arguments of __traits(isSame)
2095 private bool isSame(RootObject o1, RootObject o2, Scope* sc)
2096 {
2097     static FuncLiteralDeclaration isLambda(RootObject oarg)
2098     {
2099         if (auto t = isDsymbol(oarg))
2100         {
2101             if (auto td = t.isTemplateDeclaration())
2102             {
2103                 if (td.members && td.members.length == 1)
2104                 {
2105                     if (auto fd = (*td.members)[0].isFuncLiteralDeclaration())
2106                         return fd;
2107                 }
2108             }
2109         }
2110         else if (auto ea = isExpression(oarg))
2111         {
2112             if (ea.op == EXP.function_)
2113             {
2114                 if (auto fe = ea.isFuncExp())
2115                     return fe.fd;
2116             }
2117         }
2118         return null;
2119     }
2120 
2121     auto l1 = isLambda(o1);
2122     auto l2 = isLambda(o2);
2123 
2124     if (l1 && l2)
2125     {
2126         import dmd.lambdacomp : isSameFuncLiteral;
2127         if (isSameFuncLiteral(l1, l2, sc))
2128             return true;
2129     }
2130 
2131     // https://issues.dlang.org/show_bug.cgi?id=12001, allow isSame, <BasicType>, <BasicType>
2132     Type t1 = isType(o1);
2133     Type t2 = isType(o2);
2134     if (t1 && t2 && t1.equals(t2))
2135         return true;
2136 
2137     auto s1 = getDsymbol(o1);
2138     auto s2 = getDsymbol(o2);
2139     //printf("isSame: %s, %s\n", o1.toChars(), o2.toChars());
2140     version (none)
2141     {
2142         printf("o1: %p\n", o1);
2143         printf("o2: %p\n", o2);
2144         if (!s1)
2145         {
2146             if (auto ea = isExpression(o1))
2147                 printf("%s\n", ea.toChars());
2148             if (auto ta = isType(o1))
2149                 printf("%s\n", ta.toChars());
2150             return false;
2151         }
2152         else
2153             printf("%s %s\n", s1.kind(), s1.toChars());
2154     }
2155     if (!s1 && !s2)
2156     {
2157         auto ea1 = isExpression(o1);
2158         auto ea2 = isExpression(o2);
2159         if (ea1 && ea2)
2160         {
2161             if (ea1.equals(ea2))
2162                 return true;
2163         }
2164     }
2165     if (!s1 || !s2)
2166         return false;
2167 
2168     s1 = s1.toAlias();
2169     s2 = s2.toAlias();
2170 
2171     if (auto fa1 = s1.isFuncAliasDeclaration())
2172         s1 = fa1.toAliasFunc();
2173     if (auto fa2 = s2.isFuncAliasDeclaration())
2174         s2 = fa2.toAliasFunc();
2175 
2176     // https://issues.dlang.org/show_bug.cgi?id=11259
2177     // compare import symbol to a package symbol
2178     static bool cmp(Dsymbol s1, Dsymbol s2)
2179     {
2180         auto imp = s1.isImport();
2181         return imp && imp.pkg && imp.pkg == s2.isPackage();
2182     }
2183 
2184     if (cmp(s1,s2) || cmp(s2,s1))
2185         return true;
2186 
2187     if (s1 == s2)
2188         return true;
2189 
2190     // https://issues.dlang.org/show_bug.cgi?id=18771
2191     // OverloadSets are equal if they contain the same functions
2192     auto overSet1 = s1.isOverloadSet();
2193     if (!overSet1)
2194         return false;
2195 
2196     auto overSet2 = s2.isOverloadSet();
2197     if (!overSet2)
2198         return false;
2199 
2200     if (overSet1.a.length != overSet2.a.length)
2201         return false;
2202 
2203     // OverloadSets contain array of Dsymbols => O(n*n)
2204     // to compare for equality as the order of overloads
2205     // might not be the same
2206 Lnext:
2207     foreach(overload1; overSet1.a)
2208     {
2209         foreach(overload2; overSet2.a)
2210         {
2211             if (overload1 == overload2)
2212                 continue Lnext;
2213         }
2214         return false;
2215     }
2216     return true;
2217 }
2218 
2219 
2220 /***********************************
2221  * A trait was not found. Give a decent error message
2222  * by trying a spell check.
2223  * Params:
2224  *      e = the offending trait
2225  */
2226 private void traitNotFound(TraitsExp e)
2227 {
2228     __gshared const StringTable!bool traitsStringTable;
2229     __gshared bool initialized;
2230 
2231     if (!initialized)
2232     {
2233         initialized = true;     // lazy initialization
2234 
2235         // All possible traits
2236         __gshared Identifier*[59] idents =
2237         [
2238             &Id.allMembers,
2239             &Id.child,
2240             &Id.classInstanceAlignment,
2241             &Id.classInstanceSize,
2242             &Id.compiles,
2243             &Id.derivedMembers,
2244             &Id.fullyQualifiedName,
2245             &Id.getAliasThis,
2246             &Id.getAttributes,
2247             &Id.getFunctionAttributes,
2248             &Id.getFunctionVariadicStyle,
2249             &Id.getLinkage,
2250             &Id.getLocation,
2251             &Id.getMember,
2252             &Id.getOverloads,
2253             &Id.getParameterStorageClasses,
2254             &Id.getPointerBitmap,
2255             &Id.getProtection,
2256             &Id.getTargetInfo,
2257             &Id.getUnitTests,
2258             &Id.getVirtualFunctions,
2259             &Id.getVirtualIndex,
2260             &Id.getVirtualMethods,
2261             &Id.getVisibility,
2262             &Id.hasCopyConstructor,
2263             &Id.hasMember,
2264             &Id.hasPostblit,
2265             &Id.identifier,
2266             &Id.isAbstractClass,
2267             &Id.isAbstractFunction,
2268             &Id.isArithmetic,
2269             &Id.isAssociativeArray,
2270             &Id.isCopyable,
2271             &Id.isDeprecated,
2272             &Id.isDisabled,
2273             &Id.isFinalClass,
2274             &Id.isFinalFunction,
2275             &Id.isFloating,
2276             &Id.isFuture,
2277             &Id.isIntegral,
2278             &Id.isLazy,
2279             &Id.isModule,
2280             &Id.isNested,
2281             &Id.isOut,
2282             &Id.isOverrideFunction,
2283             &Id.isPackage,
2284             &Id.isPOD,
2285             &Id.isRef,
2286             &Id.isReturnOnStack,
2287             &Id.isSame,
2288             &Id.isScalar,
2289             &Id.isStaticArray,
2290             &Id.isStaticFunction,
2291             &Id.isUnsigned,
2292             &Id.isVirtualFunction,
2293             &Id.isVirtualMethod,
2294             &Id.isZeroInit,
2295             &Id.parameters,
2296             &Id.parent,
2297         ];
2298 
2299         StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable;
2300         stringTable._init(idents.length);
2301 
2302         foreach (id; idents)
2303         {
2304             auto sv = stringTable.insert((*id).toString(), true);
2305             assert(sv);
2306         }
2307     }
2308 
2309     static const(char)[] trait_search_fp(const(char)[] seed, out int cost)
2310     {
2311         //printf("trait_search_fp('%s')\n", seed);
2312         if (!seed.length)
2313             return null;
2314         cost = 0;       // all the same cost
2315         const sv = traitsStringTable.lookup(seed);
2316         return sv ? sv.toString() : null;
2317     }
2318 
2319     if (auto sub = speller!trait_search_fp(e.ident.toString()))
2320         error(e.loc, "unrecognized trait `%s`, did you mean `%.*s`?", e.ident.toChars(), cast(int) sub.length, sub.ptr);
2321     else
2322         error(e.loc, "unrecognized trait `%s`", e.ident.toChars());
2323 }