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