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