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