1 /**
2  * Handle introspection functionality of the `__traits()` construct.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits)
5  *
6  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/traits.d, _traits.d)
10  * Documentation:  https://dlang.org/phobos/dmd_traits.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/traits.d
12  */
13 
14 module dmd.traits;
15 
16 import core.stdc.stdio;
17 
18 import dmd.aggregate;
19 import dmd.arraytypes;
20 import dmd.astcodegen;
21 import dmd.attrib;
22 import dmd.canthrow;
23 import dmd.dclass;
24 import dmd.declaration;
25 import dmd.denum;
26 import dmd.dimport;
27 import dmd.dmodule;
28 import dmd.dscope;
29 import dmd.dsymbol;
30 import dmd.dsymbolsem;
31 import dmd.dtemplate;
32 import dmd.errors;
33 import dmd.expression;
34 import dmd.expressionsem;
35 import dmd.func;
36 import dmd.globals;
37 import dmd.hdrgen;
38 import dmd.id;
39 import dmd.identifier;
40 import dmd.mtype;
41 import dmd.nogc;
42 import dmd.parse;
43 import dmd.root.array;
44 import dmd.root.speller;
45 import dmd.root.stringtable;
46 import dmd.target;
47 import dmd.tokens;
48 import dmd.typesem;
49 import dmd.visitor;
50 import dmd.root.rootobject;
51 import dmd.root.outbuffer;
52 import dmd.root.string;
53 
54 enum LOGSEMANTIC = false;
55 
56 /************************ TraitsExp ************************************/
57 
58 /**************************************
59  * Convert `Expression` or `Type` to corresponding `Dsymbol`, additionally
60  * stripping off expression contexts.
61  *
62  * Some symbol related `__traits` ignore arguments expression contexts.
63  * For example:
64  * ----
65  *  struct S { void f() {} }
66  *  S s;
67  *  pragma(msg, __traits(isNested, s.f));
68  *  // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`.
69  * ----
70  *
71  * This is used for that common `__traits` behavior.
72  *
73  * Input:
74  *      oarg     object to get the symbol for
75  * Returns:
76  *      Dsymbol  the corresponding symbol for oarg
77  */
78 private Dsymbol getDsymbolWithoutExpCtx(RootObject oarg)
79 {
80     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         "parent",
126         "child",
127         "getLinkage",
128         "getMember",
129         "getOverloads",
130         "getVirtualFunctions",
131         "getVirtualMethods",
132         "classInstanceSize",
133         "allMembers",
134         "derivedMembers",
135         "isSame",
136         "compiles",
137         "parameters",
138         "getAliasThis",
139         "getAttributes",
140         "getFunctionAttributes",
141         "getFunctionVariadicStyle",
142         "getParameterStorageClasses",
143         "getUnitTests",
144         "getVirtualIndex",
145         "getPointerBitmap",
146         "isZeroInit",
147         "getTargetInfo",
148         "getLocation",
149         "hasPostblit",
150         "hasCopyConstructor",
151         "isCopyable",
152     ];
153 
154     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 &&
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)
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 protection", o.toChars());
842             return ErrorExp.get();
843         }
844         if (s.semanticRun == PASS.init)
845             s.dsymbolSemantic(null);
846 
847         auto protName = protectionToString(s.prot().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 (ex.op == TOK.variable)
1059             {
1060                 VarExp ve = cast(VarExp)ex;
1061                 f = ve.var.isFuncDeclaration();
1062                 ex = null;
1063             }
1064             else if (ex.op == TOK.dotVariable)
1065             {
1066                 DotVarExp dve = cast(DotVarExp)ex;
1067                 f = dve.var.isFuncDeclaration();
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 (ex.op == TOK.template_)
1074             {
1075                 VarExp ve = cast(VarExp)ex;
1076                 auto td = ve.var.isTemplateDeclaration();
1077                 f = td;
1078                 if (td && td.funcroot)
1079                     f = td.funcroot;
1080                 ex = null;
1081             }
1082             else if (ex.op == TOK.dotTemplateDeclaration)
1083             {
1084                 DotTemplateExp dte = cast(DotTemplateExp)ex;
1085                 auto td = dte.td;
1086                 f = td;
1087                 if (td && td.funcroot)
1088                     f = td.funcroot;
1089                 ex = null;
1090                 if (dte.e1.op != TOK.dotType && dte.e1.op != TOK.this_)
1091                     ex = dte.e1;
1092             }
1093             bool[string] funcTypeHash;
1094 
1095             /* Compute the function signature and insert it in the
1096              * hashtable, if not present. This is needed so that
1097              * traits(getOverlods, F3, "visit") does not count `int visit(int)`
1098              * twice in the following example:
1099              *
1100              * =============================================
1101              * interface F1 { int visit(int);}
1102              * interface F2 { int visit(int); void visit(); }
1103              * interface F3 : F2, F1 {}
1104              *==============================================
1105              */
1106             void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e)
1107             {
1108                 auto signature = fd.type.toString();
1109                 //printf("%s - %s\n", fd.toChars, signature);
1110                 if (signature !in funcTypeHash)
1111                 {
1112                     funcTypeHash[signature] = true;
1113                     exps.push(e);
1114                 }
1115             }
1116 
1117             int dg(Dsymbol s)
1118             {
1119                 auto fd = s.isFuncDeclaration();
1120                 if (!fd)
1121                 {
1122                     if (includeTemplates)
1123                     {
1124                         if (auto td = s.isTemplateDeclaration())
1125                         {
1126                             // if td is part of an overload set we must take a copy
1127                             // which shares the same `instances` cache but without
1128                             // `overroot` and `overnext` set to avoid overload
1129                             // behaviour in the result.
1130                             if (td.overnext !is null)
1131                             {
1132                                 if (td.instances is null)
1133                                 {
1134                                     // create an empty AA just to copy it
1135                                     scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
1136                                     auto tib = TemplateInstanceBox(ti);
1137                                     td.instances[tib] = null;
1138                                     td.instances.clear();
1139                                 }
1140                                 td = cast(TemplateDeclaration) td.syntaxCopy(null);
1141                                 import core.stdc.string : memcpy;
1142                                 memcpy(cast(void*) td, cast(void*) s,
1143                                         __traits(classInstanceSize, TemplateDeclaration));
1144                                 td.overroot = null;
1145                                 td.overnext = null;
1146                             }
1147                             exps.push(new DsymbolExp(Loc.initial, td, false));
1148                         }
1149                     }
1150                     return 0;
1151                 }
1152                 if (e.ident == Id.getVirtualFunctions && !fd.isVirtual())
1153                     return 0;
1154                 if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod())
1155                     return 0;
1156 
1157                 auto fa = new FuncAliasDeclaration(fd.ident, fd, false);
1158                 fa.protection = fd.protection;
1159 
1160                 auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false)
1161                             : new DsymbolExp(Loc.initial, fa, false);
1162 
1163                 // if the parent is an interface declaration
1164                 // we must check for functions with the same signature
1165                 // in different inherited interfaces
1166                 if (sym && sym.isInterfaceDeclaration())
1167                     insertInterfaceInheritedFunction(fd, e);
1168                 else
1169                     exps.push(e);
1170                 return 0;
1171             }
1172 
1173             InterfaceDeclaration ifd = null;
1174             if (sym)
1175                 ifd = sym.isInterfaceDeclaration();
1176             // If the symbol passed as a parameter is an
1177             // interface that inherits other interfaces
1178             overloadApply(f, &dg);
1179             if (ifd && ifd.interfaces && f)
1180             {
1181                 // check the overloads of each inherited interface individually
1182                 foreach (bc; ifd.interfaces)
1183                 {
1184                     if (auto fd = bc.sym.search(e.loc, f.ident))
1185                         overloadApply(fd, &dg);
1186                 }
1187             }
1188 
1189             auto tup = new TupleExp(e.loc, exps);
1190             return tup.expressionSemantic(scx);
1191         }
1192         else
1193             assert(0);
1194     }
1195     if (e.ident == Id.classInstanceSize)
1196     {
1197         if (dim != 1)
1198             return dimError(1);
1199 
1200         auto o = (*e.args)[0];
1201         auto s = getDsymbol(o);
1202         auto cd = s ? s.isClassDeclaration() : null;
1203         if (!cd)
1204         {
1205             e.error("first argument is not a class");
1206             return ErrorExp.get();
1207         }
1208         if (cd.sizeok != Sizeok.done)
1209         {
1210             cd.size(e.loc);
1211         }
1212         if (cd.sizeok != Sizeok.done)
1213         {
1214             e.error("%s `%s` is forward referenced", cd.kind(), cd.toChars());
1215             return ErrorExp.get();
1216         }
1217 
1218         return new IntegerExp(e.loc, cd.structsize, Type.tsize_t);
1219     }
1220     if (e.ident == Id.getAliasThis)
1221     {
1222         if (dim != 1)
1223             return dimError(1);
1224 
1225         auto o = (*e.args)[0];
1226         auto s = getDsymbol(o);
1227         auto ad = s ? s.isAggregateDeclaration() : null;
1228 
1229         auto exps = new Expressions();
1230         if (ad && ad.aliasthis)
1231             exps.push(new StringExp(e.loc, ad.aliasthis.ident.toString()));
1232         Expression ex = new TupleExp(e.loc, exps);
1233         ex = ex.expressionSemantic(sc);
1234         return ex;
1235     }
1236     if (e.ident == Id.getAttributes)
1237     {
1238         /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
1239          * a symbol should not be folded to a constant.
1240          * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
1241          */
1242         if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3))
1243             return ErrorExp.get();
1244 
1245         if (dim != 1)
1246             return dimError(1);
1247 
1248         auto o = (*e.args)[0];
1249         auto po = isParameter(o);
1250         auto s = getDsymbolWithoutExpCtx(o);
1251         UserAttributeDeclaration udad = null;
1252         if (po)
1253         {
1254             udad = po.userAttribDecl;
1255         }
1256         else if (s)
1257         {
1258             if (s.isImport())
1259             {
1260                 s = s.isImport().mod;
1261             }
1262             //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope);
1263             udad = s.userAttribDecl;
1264         }
1265         else
1266         {
1267             version (none)
1268             {
1269                 Expression x = isExpression(o);
1270                 Type t = isType(o);
1271                 if (x)
1272                     printf("e = %s %s\n", Token.toChars(x.op), x.toChars());
1273                 if (t)
1274                     printf("t = %d %s\n", t.ty, t.toChars());
1275             }
1276             e.error("first argument is not a symbol");
1277             return ErrorExp.get();
1278         }
1279 
1280         auto exps = udad ? udad.getAttributes() : new Expressions();
1281         auto tup = new TupleExp(e.loc, exps);
1282         return tup.expressionSemantic(sc);
1283     }
1284     if (e.ident == Id.getFunctionAttributes)
1285     {
1286         /* Extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
1287          * https://dlang.org/spec/traits.html#getFunctionAttributes
1288          */
1289         if (dim != 1)
1290             return dimError(1);
1291 
1292         FuncDeclaration fd;
1293         TypeFunction tf = toTypeFunction((*e.args)[0], fd);
1294 
1295         if (!tf)
1296         {
1297             e.error("first argument is not a function");
1298             return ErrorExp.get();
1299         }
1300 
1301         auto mods = new Expressions();
1302 
1303         void addToMods(string str)
1304         {
1305             mods.push(new StringExp(Loc.initial, str));
1306         }
1307         tf.modifiersApply(&addToMods);
1308         tf.attributesApply(&addToMods, TRUSTformatSystem);
1309 
1310         auto tup = new TupleExp(e.loc, mods);
1311         return tup.expressionSemantic(sc);
1312     }
1313     if (e.ident == Id.isReturnOnStack)
1314     {
1315         /* Extract as a boolean if function return value is on the stack
1316          * https://dlang.org/spec/traits.html#isReturnOnStack
1317          */
1318         if (dim != 1)
1319             return dimError(1);
1320 
1321         RootObject o = (*e.args)[0];
1322         FuncDeclaration fd;
1323         TypeFunction tf = toTypeFunction(o, fd);
1324 
1325         if (!tf)
1326         {
1327             e.error("argument to `__traits(isReturnOnStack, %s)` is not a function", o.toChars());
1328             return ErrorExp.get();
1329         }
1330 
1331         bool value = target.isReturnOnStack(tf, fd && fd.needThis());
1332         return IntegerExp.createBool(value);
1333     }
1334     if (e.ident == Id.getFunctionVariadicStyle)
1335     {
1336         /* Accept a symbol or a type. Returns one of the following:
1337          *  "none"      not a variadic function
1338          *  "argptr"    extern(D) void dstyle(...), use `__argptr` and `__arguments`
1339          *  "stdarg"    extern(C) void cstyle(int, ...), use core.stdc.stdarg
1340          *  "typesafe"  void typesafe(T[] ...)
1341          */
1342         // get symbol linkage as a string
1343         if (dim != 1)
1344             return dimError(1);
1345 
1346         LINK link;
1347         VarArg varargs;
1348         auto o = (*e.args)[0];
1349 
1350         FuncDeclaration fd;
1351         TypeFunction tf = toTypeFunction(o, fd);
1352 
1353         if (tf)
1354         {
1355             link = tf.linkage;
1356             varargs = tf.parameterList.varargs;
1357         }
1358         else
1359         {
1360             if (!fd)
1361             {
1362                 e.error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o.toChars());
1363                 return ErrorExp.get();
1364             }
1365             link = fd.linkage;
1366             varargs = fd.getParameterList().varargs;
1367         }
1368         string style;
1369         final switch (varargs)
1370         {
1371             case VarArg.none:     style = "none";           break;
1372             case VarArg.variadic: style = (link == LINK.d)
1373                                              ? "argptr"
1374                                              : "stdarg";    break;
1375             case VarArg.typesafe: style = "typesafe";       break;
1376         }
1377         auto se = new StringExp(e.loc, style);
1378         return se.expressionSemantic(sc);
1379     }
1380     if (e.ident == Id.getParameterStorageClasses)
1381     {
1382         /* Accept a function symbol or a type, followed by a parameter index.
1383          * Returns a tuple of strings of the parameter's storage classes.
1384          */
1385         // get symbol linkage as a string
1386         if (dim != 2)
1387             return dimError(2);
1388 
1389         auto o = (*e.args)[0];
1390         auto o1 = (*e.args)[1];
1391 
1392         FuncDeclaration fd;
1393         TypeFunction tf = toTypeFunction(o, fd);
1394 
1395         ParameterList fparams;
1396         if (tf)
1397             fparams = tf.parameterList;
1398         else if (fd)
1399             fparams = fd.getParameterList();
1400         else
1401         {
1402             e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function",
1403                 o.toChars(), o1.toChars());
1404             return ErrorExp.get();
1405         }
1406 
1407         StorageClass stc;
1408 
1409         // Set stc to storage class of the ith parameter
1410         auto ex = isExpression((*e.args)[1]);
1411         if (!ex)
1412         {
1413             e.error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
1414                 o.toChars(), o1.toChars());
1415             return ErrorExp.get();
1416         }
1417         ex = ex.ctfeInterpret();
1418         auto ii = ex.toUInteger();
1419         if (ii >= fparams.length)
1420         {
1421             e.error("parameter index must be in range 0..%u not %s", cast(uint)fparams.length, ex.toChars());
1422             return ErrorExp.get();
1423         }
1424 
1425         uint n = cast(uint)ii;
1426         Parameter p = fparams[n];
1427         stc = p.storageClass;
1428 
1429         // This mirrors hdrgen.visit(Parameter p)
1430         if (p.type && p.type.mod & MODFlags.shared_)
1431             stc &= ~STC.shared_;
1432 
1433         auto exps = new Expressions;
1434 
1435         void push(string s)
1436         {
1437             exps.push(new StringExp(e.loc, s));
1438         }
1439 
1440         if (stc & STC.auto_)
1441             push("auto");
1442         if (stc & STC.return_)
1443             push("return");
1444 
1445         if (stc & STC.out_)
1446             push("out");
1447         else if (stc & STC.ref_)
1448             push("ref");
1449         else if (stc & STC.in_)
1450             push("in");
1451         else if (stc & STC.lazy_)
1452             push("lazy");
1453         else if (stc & STC.alias_)
1454             push("alias");
1455 
1456         if (stc & STC.const_)
1457             push("const");
1458         if (stc & STC.immutable_)
1459             push("immutable");
1460         if (stc & STC.wild)
1461             push("inout");
1462         if (stc & STC.shared_)
1463             push("shared");
1464         if (stc & STC.scope_ && !(stc & STC.scopeinferred))
1465             push("scope");
1466 
1467         auto tup = new TupleExp(e.loc, exps);
1468         return tup.expressionSemantic(sc);
1469     }
1470     if (e.ident == Id.getLinkage)
1471     {
1472         // get symbol linkage as a string
1473         if (dim != 1)
1474             return dimError(1);
1475 
1476         LINK link;
1477         auto o = (*e.args)[0];
1478 
1479         FuncDeclaration fd;
1480         TypeFunction tf = toTypeFunction(o, fd);
1481 
1482         if (tf)
1483             link = tf.linkage;
1484         else
1485         {
1486             auto s = getDsymbol(o);
1487             Declaration d;
1488             AggregateDeclaration agg;
1489             if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null))
1490             {
1491                 e.error("argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars());
1492                 return ErrorExp.get();
1493             }
1494 
1495             if (d !is null)
1496                 link = d.linkage;
1497             else
1498             {
1499                 // Resolves forward references
1500                 if (agg.sizeok != Sizeok.done)
1501                 {
1502                     agg.size(e.loc);
1503                     if (agg.sizeok != Sizeok.done)
1504                     {
1505                         e.error("%s `%s` is forward referenced", agg.kind(), agg.toChars());
1506                         return ErrorExp.get();
1507                     }
1508                 }
1509 
1510                 final switch (agg.classKind)
1511                 {
1512                     case ClassKind.d:
1513                         link = LINK.d;
1514                         break;
1515                     case ClassKind.cpp:
1516                         link = LINK.cpp;
1517                         break;
1518                     case ClassKind.objc:
1519                         link = LINK.objc;
1520                         break;
1521                 }
1522             }
1523         }
1524         auto linkage = linkageToChars(link);
1525         auto se = new StringExp(e.loc, linkage.toDString());
1526         return se.expressionSemantic(sc);
1527     }
1528     if (e.ident == Id.allMembers ||
1529         e.ident == Id.derivedMembers)
1530     {
1531         if (dim != 1)
1532             return dimError(1);
1533 
1534         auto o = (*e.args)[0];
1535         auto s = getDsymbol(o);
1536         if (!s)
1537         {
1538             e.error("In expression `%s` `%s` can't have members", e.toChars(), o.toChars());
1539             e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", o.toChars());
1540 
1541             return ErrorExp.get();
1542         }
1543         if (auto imp = s.isImport())
1544         {
1545             // https://issues.dlang.org/show_bug.cgi?id=9692
1546             s = imp.mod;
1547         }
1548 
1549         // https://issues.dlang.org/show_bug.cgi?id=16044
1550         if (auto p = s.isPackage())
1551         {
1552             if (auto pm = p.isPackageMod())
1553                 s = pm;
1554         }
1555 
1556         auto sds = s.isScopeDsymbol();
1557         if (!sds || sds.isTemplateDeclaration())
1558         {
1559             e.error("In expression `%s` %s `%s` has no members", e.toChars(), s.kind(), s.toChars());
1560             e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", s.toChars());
1561             return ErrorExp.get();
1562         }
1563 
1564         auto idents = new Identifiers();
1565 
1566         int pushIdentsDg(size_t n, Dsymbol sm)
1567         {
1568             if (!sm)
1569                 return 1;
1570 
1571             // skip local symbols, such as static foreach loop variables
1572             if (auto decl = sm.isDeclaration())
1573             {
1574                 if (decl.storage_class & STC.local)
1575                 {
1576                     return 0;
1577                 }
1578             }
1579 
1580             // https://issues.dlang.org/show_bug.cgi?id=20915
1581             // skip version and debug identifiers
1582             if (sm.isVersionSymbol() || sm.isDebugSymbol())
1583                 return 0;
1584 
1585             //printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
1586             if (sm.ident)
1587             {
1588                 // https://issues.dlang.org/show_bug.cgi?id=10096
1589                 // https://issues.dlang.org/show_bug.cgi?id=10100
1590                 // Skip over internal members in __traits(allMembers)
1591                 if ((sm.isCtorDeclaration() && sm.ident != Id.ctor) ||
1592                     (sm.isDtorDeclaration() && sm.ident != Id.dtor) ||
1593                     (sm.isPostBlitDeclaration() && sm.ident != Id.postblit) ||
1594                     sm.isInvariantDeclaration() ||
1595                     sm.isUnitTestDeclaration())
1596 
1597                 {
1598                     return 0;
1599                 }
1600                 if (sm.ident == Id.empty)
1601                 {
1602                     return 0;
1603                 }
1604                 if (sm.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177
1605                     return 0;
1606                 if ((!sds.isModule() && !sds.isPackage()) && sm.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057
1607                     return 0;
1608 
1609                 //printf("\t%s\n", sm.ident.toChars());
1610 
1611                 /* Skip if already present in idents[]
1612                  */
1613                 foreach (id; *idents)
1614                 {
1615                     if (id == sm.ident)
1616                         return 0;
1617 
1618                     // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
1619                     debug
1620                     {
1621                         import core.stdc.string : strcmp;
1622                         assert(strcmp(id.toChars(), sm.ident.toChars()) != 0);
1623                     }
1624                 }
1625                 idents.push(sm.ident);
1626             }
1627             else if (auto ed = sm.isEnumDeclaration())
1628             {
1629                 ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg);
1630             }
1631             return 0;
1632         }
1633 
1634         ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg);
1635         auto cd = sds.isClassDeclaration();
1636         if (cd && e.ident == Id.allMembers)
1637         {
1638             if (cd.semanticRun < PASS.semanticdone)
1639                 cd.dsymbolSemantic(null); // https://issues.dlang.org/show_bug.cgi?id=13668
1640                                    // Try to resolve forward reference
1641 
1642             void pushBaseMembersDg(ClassDeclaration cd)
1643             {
1644                 for (size_t i = 0; i < cd.baseclasses.dim; i++)
1645                 {
1646                     auto cb = (*cd.baseclasses)[i].sym;
1647                     assert(cb);
1648                     ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg);
1649                     if (cb.baseclasses.dim)
1650                         pushBaseMembersDg(cb);
1651                 }
1652             }
1653 
1654             pushBaseMembersDg(cd);
1655         }
1656 
1657         // Turn Identifiers into StringExps reusing the allocated array
1658         assert(Expressions.sizeof == Identifiers.sizeof);
1659         auto exps = cast(Expressions*)idents;
1660         foreach (i, id; *idents)
1661         {
1662             auto se = new StringExp(e.loc, id.toString());
1663             (*exps)[i] = se;
1664         }
1665 
1666         /* Making this a tuple is more flexible, as it can be statically unrolled.
1667          * To make an array literal, enclose __traits in [ ]:
1668          *   [ __traits(allMembers, ...) ]
1669          */
1670         Expression ex = new TupleExp(e.loc, exps);
1671         ex = ex.expressionSemantic(sc);
1672         return ex;
1673     }
1674     if (e.ident == Id.compiles)
1675     {
1676         /* Determine if all the objects - types, expressions, or symbols -
1677          * compile without error
1678          */
1679         if (!dim)
1680             return False();
1681 
1682         foreach (o; *e.args)
1683         {
1684             uint errors = global.startGagging();
1685             Scope* sc2 = sc.push();
1686             sc2.tinst = null;
1687             sc2.minst = null;
1688             sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst;
1689 
1690             bool err = false;
1691 
1692             auto t = isType(o);
1693             while (t)
1694             {
1695                 if (auto tm = t.isTypeMixin())
1696                 {
1697                     /* The mixin string could be a type or an expression.
1698                      * Have to try compiling it to see.
1699                      */
1700                     OutBuffer buf;
1701                     if (expressionsToString(buf, sc, tm.exps))
1702                     {
1703                         err = true;
1704                         break;
1705                     }
1706                     const olderrors = global.errors;
1707                     const len = buf.length;
1708                     buf.writeByte(0);
1709                     const str = buf.extractSlice()[0 .. len];
1710                     scope p = new Parser!ASTCodegen(e.loc, sc._module, str, false);
1711                     p.nextToken();
1712                     //printf("p.loc.linnum = %d\n", p.loc.linnum);
1713 
1714                     o = p.parseTypeOrAssignExp(TOK.endOfFile);
1715                     p.reportDiagnostics();
1716                     if (olderrors != global.errors || p.token.value != TOK.endOfFile)
1717                     {
1718                         err = true;
1719                         break;
1720                     }
1721                     t = o.isType();
1722                 }
1723                 else
1724                     break;
1725             }
1726 
1727             if (!err)
1728             {
1729                 auto ex = t ? t.typeToExpression() : isExpression(o);
1730                 if (!ex && t)
1731                 {
1732                     Dsymbol s;
1733                     t.resolve(e.loc, sc2, &ex, &t, &s);
1734                     if (t)
1735                     {
1736                         t.typeSemantic(e.loc, sc2);
1737                         if (t.ty == Terror)
1738                             err = true;
1739                     }
1740                     else if (s && s.errors)
1741                         err = true;
1742                 }
1743                 if (ex)
1744                 {
1745                     ex = ex.expressionSemantic(sc2);
1746                     ex = resolvePropertiesOnly(sc2, ex);
1747                     ex = ex.optimize(WANTvalue);
1748                     if (sc2.func && sc2.func.type.ty == Tfunction)
1749                     {
1750                         const tf = cast(TypeFunction)sc2.func.type;
1751                         err |= tf.isnothrow && canThrow(ex, sc2.func, false);
1752                     }
1753                     ex = checkGC(sc2, ex);
1754                     if (ex.op == TOK.error)
1755                         err = true;
1756                 }
1757             }
1758 
1759             // Carefully detach the scope from the parent and throw it away as
1760             // we only need it to evaluate the expression
1761             // https://issues.dlang.org/show_bug.cgi?id=15428
1762             sc2.detach();
1763 
1764             if (global.endGagging(errors) || err)
1765             {
1766                 return False();
1767             }
1768         }
1769         return True();
1770     }
1771     if (e.ident == Id.isSame)
1772     {
1773         /* Determine if two symbols are the same
1774          */
1775         if (dim != 2)
1776             return dimError(2);
1777 
1778         // https://issues.dlang.org/show_bug.cgi?id=20761
1779         // tiarg semantic may expand in place the list of arguments, for example:
1780         //
1781         //     before tiarg sema:  __traits(isSame, seq!(0,0), seq!(1,1))
1782         //     after            :  __traits(isSame, 0, 0, 1, 1)
1783         //
1784         // so we split in two lists
1785         Objects ob1;
1786         ob1.push((*e.args)[0]);
1787         Objects ob2;
1788         ob2.push((*e.args)[1]);
1789         if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0))
1790             return ErrorExp.get();
1791         if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0))
1792             return ErrorExp.get();
1793         if (ob1.dim != ob2.dim)
1794             return False();
1795         foreach (immutable i; 0 .. ob1.dim)
1796             if (!ob1[i].isSame(ob2[i], sc))
1797                 return False();
1798         return True();
1799     }
1800     if (e.ident == Id.getUnitTests)
1801     {
1802         if (dim != 1)
1803             return dimError(1);
1804 
1805         auto o = (*e.args)[0];
1806         auto s = getDsymbolWithoutExpCtx(o);
1807         if (!s)
1808         {
1809             e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate",
1810                 o.toChars());
1811             return ErrorExp.get();
1812         }
1813         if (auto imp = s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990
1814             s = imp.mod;
1815 
1816         auto sds = s.isScopeDsymbol();
1817         if (!sds)
1818         {
1819             e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate, not a %s",
1820                 s.toChars(), s.kind());
1821             return ErrorExp.get();
1822         }
1823 
1824         auto exps = new Expressions();
1825         if (global.params.useUnitTests)
1826         {
1827             bool[void*] uniqueUnitTests;
1828 
1829             void symbolDg(Dsymbol s)
1830             {
1831                 if (auto ad = s.isAttribDeclaration())
1832                 {
1833                     ad.include(null).foreachDsymbol(&symbolDg);
1834                 }
1835                 else if (auto ud = s.isUnitTestDeclaration())
1836                 {
1837                     if (cast(void*)ud in uniqueUnitTests)
1838                         return;
1839 
1840                     uniqueUnitTests[cast(void*)ud] = true;
1841 
1842                     auto ad = new FuncAliasDeclaration(ud.ident, ud, false);
1843                     ad.protection = ud.protection;
1844 
1845                     auto e = new DsymbolExp(Loc.initial, ad, false);
1846                     exps.push(e);
1847                 }
1848             }
1849 
1850             sds.members.foreachDsymbol(&symbolDg);
1851         }
1852         auto te = new TupleExp(e.loc, exps);
1853         return te.expressionSemantic(sc);
1854     }
1855     if (e.ident == Id.getVirtualIndex)
1856     {
1857         if (dim != 1)
1858             return dimError(1);
1859 
1860         auto o = (*e.args)[0];
1861         auto s = getDsymbolWithoutExpCtx(o);
1862 
1863         auto fd = s ? s.isFuncDeclaration() : null;
1864         if (!fd)
1865         {
1866             e.error("first argument to __traits(getVirtualIndex) must be a function");
1867             return ErrorExp.get();
1868         }
1869 
1870         fd = fd.toAliasFunc(); // Necessary to support multiple overloads.
1871         return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t);
1872     }
1873     if (e.ident == Id.getPointerBitmap)
1874     {
1875         return pointerBitmap(e);
1876     }
1877     if (e.ident == Id.isZeroInit)
1878     {
1879         if (dim != 1)
1880             return dimError(1);
1881 
1882         auto o = (*e.args)[0];
1883         Type t = isType(o);
1884         if (!t)
1885         {
1886             e.error("type expected as second argument of __traits `%s` instead of `%s`",
1887                 e.ident.toChars(), o.toChars());
1888             return ErrorExp.get();
1889         }
1890 
1891         Type tb = t.baseElemOf();
1892         return tb.isZeroInit(e.loc) ? True() : False();
1893     }
1894     if (e.ident == Id.getTargetInfo)
1895     {
1896         if (dim != 1)
1897             return dimError(1);
1898 
1899         auto ex = isExpression((*e.args)[0]);
1900         StringExp se = ex ? ex.ctfeInterpret().toStringExp() : null;
1901         if (!ex || !se || se.len == 0)
1902         {
1903             e.error("string expected as argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars());
1904             return ErrorExp.get();
1905         }
1906         se = se.toUTF8(sc);
1907 
1908         const slice = se.peekString();
1909         Expression r = target.getTargetInfo(slice.ptr, e.loc); // BUG: reliance on terminating 0
1910         if (!r)
1911         {
1912             e.error("`getTargetInfo` key `\"%.*s\"` not supported by this implementation",
1913                 cast(int)slice.length, slice.ptr);
1914             return ErrorExp.get();
1915         }
1916         return r.expressionSemantic(sc);
1917     }
1918     if (e.ident == Id.getLocation)
1919     {
1920         if (dim != 1)
1921             return dimError(1);
1922         auto arg0 = (*e.args)[0];
1923         Dsymbol s = getDsymbolWithoutExpCtx(arg0);
1924         if (!s || !s.loc.isValid())
1925         {
1926             e.error("can only get the location of a symbol, not `%s`", arg0.toChars());
1927             return ErrorExp.get();
1928         }
1929 
1930         const fd = s.isFuncDeclaration();
1931         // FIXME:td.overnext is always set, even when using an index on it
1932         //const td = s.isTemplateDeclaration();
1933         if ((fd && fd.overnext) /*|| (td && td.overnext)*/)
1934         {
1935             e.error("cannot get location of an overload set, " ~
1936                     "use `__traits(getOverloads, ..., \"%s\"%s)[N]` " ~
1937                     "to get the Nth overload",
1938                     arg0.toChars(), /*td ? ", true".ptr :*/ "".ptr);
1939             return ErrorExp.get();
1940         }
1941 
1942         auto exps = new Expressions(3);
1943         (*exps)[0] = new StringExp(e.loc, s.loc.filename.toDString());
1944         (*exps)[1] = new IntegerExp(e.loc, s.loc.linnum,Type.tint32);
1945         (*exps)[2] = new IntegerExp(e.loc, s.loc.charnum,Type.tint32);
1946         auto tup = new TupleExp(e.loc, exps);
1947         return tup.expressionSemantic(sc);
1948     }
1949 
1950     static const(char)[] trait_search_fp(const(char)[] seed, ref int cost)
1951     {
1952         //printf("trait_search_fp('%s')\n", seed);
1953         if (!seed.length)
1954             return null;
1955         cost = 0;
1956         const sv = traitsStringTable.lookup(seed);
1957         return sv ? sv.toString() : null;
1958     }
1959 
1960     if (auto sub = speller!trait_search_fp(e.ident.toString()))
1961         e.error("unrecognized trait `%s`, did you mean `%.*s`?", e.ident.toChars(), cast(int) sub.length, sub.ptr);
1962     else
1963         e.error("unrecognized trait `%s`", e.ident.toChars());
1964     return ErrorExp.get();
1965 }
1966 
1967 /// compare arguments of __traits(isSame)
1968 private bool isSame(RootObject o1, RootObject o2, Scope* sc)
1969 {
1970     static FuncLiteralDeclaration isLambda(RootObject oarg)
1971     {
1972         if (auto t = isDsymbol(oarg))
1973         {
1974             if (auto td = t.isTemplateDeclaration())
1975             {
1976                 if (td.members && td.members.dim == 1)
1977                 {
1978                     if (auto fd = (*td.members)[0].isFuncLiteralDeclaration())
1979                         return fd;
1980                 }
1981             }
1982         }
1983         else if (auto ea = isExpression(oarg))
1984         {
1985             if (ea.op == TOK.function_)
1986             {
1987                 if (auto fe = cast(FuncExp)ea)
1988                     return fe.fd;
1989             }
1990         }
1991         return null;
1992     }
1993 
1994     auto l1 = isLambda(o1);
1995     auto l2 = isLambda(o2);
1996 
1997     if (l1 && l2)
1998     {
1999         import dmd.lambdacomp : isSameFuncLiteral;
2000         if (isSameFuncLiteral(l1, l2, sc))
2001             return true;
2002     }
2003 
2004     // issue 12001, allow isSame, <BasicType>, <BasicType>
2005     Type t1 = isType(o1);
2006     Type t2 = isType(o2);
2007     if (t1 && t2 && t1.equals(t2))
2008         return true;
2009 
2010     auto s1 = getDsymbol(o1);
2011     auto s2 = getDsymbol(o2);
2012     //printf("isSame: %s, %s\n", o1.toChars(), o2.toChars());
2013     version (none)
2014     {
2015         printf("o1: %p\n", o1);
2016         printf("o2: %p\n", o2);
2017         if (!s1)
2018         {
2019             if (auto ea = isExpression(o1))
2020                 printf("%s\n", ea.toChars());
2021             if (auto ta = isType(o1))
2022                 printf("%s\n", ta.toChars());
2023             return false;
2024         }
2025         else
2026             printf("%s %s\n", s1.kind(), s1.toChars());
2027     }
2028     if (!s1 && !s2)
2029     {
2030         auto ea1 = isExpression(o1);
2031         auto ea2 = isExpression(o2);
2032         if (ea1 && ea2)
2033         {
2034             if (ea1.equals(ea2))
2035                 return true;
2036         }
2037     }
2038     if (!s1 || !s2)
2039         return false;
2040 
2041     s1 = s1.toAlias();
2042     s2 = s2.toAlias();
2043 
2044     if (auto fa1 = s1.isFuncAliasDeclaration())
2045         s1 = fa1.toAliasFunc();
2046     if (auto fa2 = s2.isFuncAliasDeclaration())
2047         s2 = fa2.toAliasFunc();
2048 
2049     // https://issues.dlang.org/show_bug.cgi?id=11259
2050     // compare import symbol to a package symbol
2051     static bool cmp(Dsymbol s1, Dsymbol s2)
2052     {
2053         auto imp = s1.isImport();
2054         return imp && imp.pkg && imp.pkg == s2.isPackage();
2055     }
2056 
2057     if (cmp(s1,s2) || cmp(s2,s1))
2058         return true;
2059 
2060     if (s1 == s2)
2061         return true;
2062 
2063     // https://issues.dlang.org/show_bug.cgi?id=18771
2064     // OverloadSets are equal if they contain the same functions
2065     auto overSet1 = s1.isOverloadSet();
2066     if (!overSet1)
2067         return false;
2068 
2069     auto overSet2 = s2.isOverloadSet();
2070     if (!overSet2)
2071         return false;
2072 
2073     if (overSet1.a.dim != overSet2.a.dim)
2074         return false;
2075 
2076     // OverloadSets contain array of Dsymbols => O(n*n)
2077     // to compare for equality as the order of overloads
2078     // might not be the same
2079 Lnext:
2080     foreach(overload1; overSet1.a)
2081     {
2082         foreach(overload2; overSet2.a)
2083         {
2084             if (overload1 == overload2)
2085                 continue Lnext;
2086         }
2087         return false;
2088     }
2089     return true;
2090 }