1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _traits.d)
9  */
10 
11 module ddmd.traits;
12 
13 import core.stdc.stdio;
14 import core.stdc..string;
15 import ddmd.aggregate;
16 import ddmd.arraytypes;
17 import ddmd.canthrow;
18 import ddmd.dclass;
19 import ddmd.declaration;
20 import ddmd.dscope;
21 import ddmd.dsymbol;
22 import ddmd.dtemplate;
23 import ddmd.errors;
24 import ddmd.expression;
25 import ddmd.func;
26 import ddmd.globals;
27 import ddmd.hdrgen;
28 import ddmd.id;
29 import ddmd.identifier;
30 import ddmd.mtype;
31 import ddmd.nogc;
32 import ddmd.root.array;
33 import ddmd.root.speller;
34 import ddmd.root.stringtable;
35 import ddmd.tokens;
36 import ddmd.visitor;
37 
38 enum LOGSEMANTIC = false;
39 
40 /************************ TraitsExp ************************************/
41 
42 // callback for TypeFunction::attributesApply
43 struct PushAttributes
44 {
45     Expressions* mods;
46 
47     extern (C++) static int fp(void* param, const(char)* str)
48     {
49         PushAttributes* p = cast(PushAttributes*)param;
50         p.mods.push(new StringExp(Loc(), cast(char*)str));
51         return 0;
52     }
53 }
54 
55 extern (C++) __gshared StringTable traitsStringTable;
56 
57 static this()
58 {
59     static immutable string[] names =
60     [
61         "isAbstractClass",
62         "isArithmetic",
63         "isAssociativeArray",
64         "isFinalClass",
65         "isPOD",
66         "isNested",
67         "isFloating",
68         "isIntegral",
69         "isScalar",
70         "isStaticArray",
71         "isUnsigned",
72         "isVirtualFunction",
73         "isVirtualMethod",
74         "isAbstractFunction",
75         "isFinalFunction",
76         "isOverrideFunction",
77         "isStaticFunction",
78         "isRef",
79         "isOut",
80         "isLazy",
81         "hasMember",
82         "identifier",
83         "getProtection",
84         "parent",
85         "getMember",
86         "getOverloads",
87         "getVirtualFunctions",
88         "getVirtualMethods",
89         "classInstanceSize",
90         "allMembers",
91         "derivedMembers",
92         "isSame",
93         "compiles",
94         "parameters",
95         "getAliasThis",
96         "getAttributes",
97         "getFunctionAttributes",
98         "getUnitTests",
99         "getVirtualIndex",
100         "getPointerBitmap",
101     ];
102 
103     traitsStringTable._init(40);
104 
105     foreach (s; names)
106     {
107         auto sv = traitsStringTable.insert(s.ptr, s.length, cast(void*)s.ptr);
108         assert(sv);
109     }
110 }
111 
112 /**
113  * get an array of size_t values that indicate possible pointer words in memory
114  *  if interpreted as the type given as argument
115  * the first array element is the size of the type for independent interpretation
116  *  of the array
117  * following elements bits represent one word (4/8 bytes depending on the target
118  *  architecture). If set the corresponding memory might contain a pointer/reference.
119  *
120  *  [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
121  */
122 extern (C++) Expression pointerBitmap(TraitsExp e)
123 {
124     if (!e.args || e.args.dim != 1)
125     {
126         error(e.loc, "a single type expected for trait pointerBitmap");
127         return new ErrorExp();
128     }
129 
130     Type t = getType((*e.args)[0]);
131     if (!t)
132     {
133         error(e.loc, "%s is not a type", (*e.args)[0].toChars());
134         return new ErrorExp();
135     }
136 
137     d_uns64 sz;
138     if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration())
139         sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(e.loc);
140     else
141         sz = t.size(e.loc);
142     if (sz == SIZE_INVALID)
143         return new ErrorExp();
144 
145     const sz_size_t = Type.tsize_t.size(e.loc);
146     if (sz > sz.max - sz_size_t)
147     {
148         error(e.loc, "size overflow for type %s", t.toChars());
149         return new ErrorExp();
150     }
151 
152     d_uns64 bitsPerWord = sz_size_t * 8;
153     d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t;
154     d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord;
155 
156     Array!(d_uns64) data;
157     data.setDim(cast(size_t)cntdata);
158     data.zero();
159 
160     extern (C++) final class PointerBitmapVisitor : Visitor
161     {
162         alias visit = super.visit;
163     public:
164         extern (D) this(Array!(d_uns64)* _data, d_uns64 _sz_size_t)
165         {
166             this.data = _data;
167             this.sz_size_t = _sz_size_t;
168         }
169 
170         void setpointer(d_uns64 off)
171         {
172             d_uns64 ptroff = off / sz_size_t;
173             (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t));
174         }
175 
176         override void visit(Type t)
177         {
178             Type tb = t.toBasetype();
179             if (tb != t)
180                 tb.accept(this);
181         }
182 
183         override void visit(TypeError t)
184         {
185             visit(cast(Type)t);
186         }
187 
188         override void visit(TypeNext t)
189         {
190             assert(0);
191         }
192 
193         override void visit(TypeBasic t)
194         {
195             if (t.ty == Tvoid)
196                 setpointer(offset);
197         }
198 
199         override void visit(TypeVector t)
200         {
201         }
202 
203         override void visit(TypeArray t)
204         {
205             assert(0);
206         }
207 
208         override void visit(TypeSArray t)
209         {
210             d_uns64 arrayoff = offset;
211             d_uns64 nextsize = t.next.size();
212             if (nextsize == SIZE_INVALID)
213                 error = true;
214             d_uns64 dim = t.dim.toInteger();
215             for (d_uns64 i = 0; i < dim; i++)
216             {
217                 offset = arrayoff + i * nextsize;
218                 t.next.accept(this);
219             }
220             offset = arrayoff;
221         }
222 
223         override void visit(TypeDArray t)
224         {
225             setpointer(offset + sz_size_t);
226         }
227 
228         // dynamic array is {length,ptr}
229         override void visit(TypeAArray t)
230         {
231             setpointer(offset);
232         }
233 
234         override void visit(TypePointer t)
235         {
236             if (t.nextOf().ty != Tfunction) // don't mark function pointers
237                 setpointer(offset);
238         }
239 
240         override void visit(TypeReference t)
241         {
242             setpointer(offset);
243         }
244 
245         override void visit(TypeClass t)
246         {
247             setpointer(offset);
248         }
249 
250         override void visit(TypeFunction t)
251         {
252         }
253 
254         override void visit(TypeDelegate t)
255         {
256             setpointer(offset);
257         }
258 
259         // delegate is {context, function}
260         override void visit(TypeQualified t)
261         {
262             assert(0);
263         }
264 
265         // assume resolved
266         override void visit(TypeIdentifier t)
267         {
268             assert(0);
269         }
270 
271         override void visit(TypeInstance t)
272         {
273             assert(0);
274         }
275 
276         override void visit(TypeTypeof t)
277         {
278             assert(0);
279         }
280 
281         override void visit(TypeReturn t)
282         {
283             assert(0);
284         }
285 
286         override void visit(TypeEnum t)
287         {
288             visit(cast(Type)t);
289         }
290 
291         override void visit(TypeTuple t)
292         {
293             visit(cast(Type)t);
294         }
295 
296         override void visit(TypeSlice t)
297         {
298             assert(0);
299         }
300 
301         override void visit(TypeNull t)
302         {
303             // always a null pointer
304         }
305 
306         override void visit(TypeStruct t)
307         {
308             d_uns64 structoff = offset;
309             foreach (v; t.sym.fields)
310             {
311                 offset = structoff + v.offset;
312                 if (v.type.ty == Tclass)
313                     setpointer(offset);
314                 else
315                     v.type.accept(this);
316             }
317             offset = structoff;
318         }
319 
320         // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
321         void visitClass(TypeClass t)
322         {
323             d_uns64 classoff = offset;
324             // skip vtable-ptr and monitor
325             if (t.sym.baseClass)
326                 visitClass(cast(TypeClass)t.sym.baseClass.type);
327             foreach (v; t.sym.fields)
328             {
329                 offset = classoff + v.offset;
330                 v.type.accept(this);
331             }
332             offset = classoff;
333         }
334 
335         Array!(d_uns64)* data;
336         d_uns64 offset;
337         d_uns64 sz_size_t;
338         bool error;
339     }
340 
341     scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(&data, sz_size_t);
342     if (t.ty == Tclass)
343         pbv.visitClass(cast(TypeClass)t);
344     else
345         t.accept(pbv);
346     if (pbv.error)
347         return new ErrorExp();
348 
349     auto exps = new Expressions();
350     exps.push(new IntegerExp(e.loc, sz, Type.tsize_t));
351     foreach (d_uns64 i; 0 .. cntdata)
352         exps.push(new IntegerExp(e.loc, data[cast(size_t)i], Type.tsize_t));
353 
354     auto ale = new ArrayLiteralExp(e.loc, exps);
355     ale.type = Type.tsize_t.sarrayOf(cntdata + 1);
356     return ale;
357 }
358 
359 extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
360 {
361     static if (LOGSEMANTIC)
362     {
363         printf("TraitsExp::semantic() %s\n", e.toChars());
364     }
365 
366     if (e.ident != Id.compiles &&
367         e.ident != Id.isSame &&
368         e.ident != Id.identifier &&
369         e.ident != Id.getProtection)
370     {
371         if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1))
372             return new ErrorExp();
373     }
374     size_t dim = e.args ? e.args.dim : 0;
375 
376     Expression dimError(int expected)
377     {
378         e.error("expected %d arguments for %s but had %d", expected, e.ident.toChars(), cast(int)dim);
379         return new ErrorExp();
380     }
381 
382     Expression isX(T)(bool function(T) fp)
383     {
384         int result = 0;
385         if (!dim)
386             goto Lfalse;
387         foreach (o; *e.args)
388         {
389             static if (is(T == Type))
390                 auto y = getType(o);
391 
392             static if (is(T : Dsymbol))
393             {
394                 auto s = getDsymbol(o);
395                 if (!s)
396                     goto Lfalse;
397             }
398             static if (is(T == Dsymbol))
399                 alias y = s;
400             static if (is(T == Declaration))
401                 auto y = s.isDeclaration();
402             static if (is(T == FuncDeclaration))
403                 auto y = s.isFuncDeclaration();
404 
405             if (!y || !fp(y))
406                 goto Lfalse;
407         }
408         result = 1;
409 
410     Lfalse:
411         return new IntegerExp(e.loc, result, Type.tbool);
412     }
413 
414     alias isTypeX = isX!Type;
415     alias isDsymX = isX!Dsymbol;
416     alias isDeclX = isX!Declaration;
417     alias isFuncX = isX!FuncDeclaration;
418 
419     if (e.ident == Id.isArithmetic)
420     {
421         return isTypeX(t => t.isintegral() || t.isfloating());
422     }
423     if (e.ident == Id.isFloating)
424     {
425         return isTypeX(t => t.isfloating());
426     }
427     if (e.ident == Id.isIntegral)
428     {
429         return isTypeX(t => t.isintegral());
430     }
431     if (e.ident == Id.isScalar)
432     {
433         return isTypeX(t => t.isscalar());
434     }
435     if (e.ident == Id.isUnsigned)
436     {
437         return isTypeX(t => t.isunsigned());
438     }
439     if (e.ident == Id.isAssociativeArray)
440     {
441         return isTypeX(t => t.toBasetype().ty == Taarray);
442     }
443     if (e.ident == Id.isStaticArray)
444     {
445         return isTypeX(t => t.toBasetype().ty == Tsarray);
446     }
447     if (e.ident == Id.isAbstractClass)
448     {
449         return isTypeX(t => t.toBasetype().ty == Tclass &&
450                             (cast(TypeClass)t.toBasetype()).sym.isAbstract());
451     }
452     if (e.ident == Id.isFinalClass)
453     {
454         return isTypeX(t => t.toBasetype().ty == Tclass &&
455                             ((cast(TypeClass)t.toBasetype()).sym.storage_class & STCfinal) != 0);
456     }
457     if (e.ident == Id.isTemplate)
458     {
459         return isDsymX((s)
460         {
461             if (!s.toAlias().isOverloadable())
462                 return false;
463             return overloadApply(s,
464                 sm => sm.isTemplateDeclaration() !is null) != 0;
465         });
466     }
467     if (e.ident == Id.isPOD)
468     {
469         if (dim != 1)
470             return dimError(1);
471 
472         auto o = (*e.args)[0];
473         auto t = isType(o);
474         if (!t)
475         {
476             e.error("type expected as second argument of __traits %s instead of %s",
477                 e.ident.toChars(), o.toChars());
478             return new ErrorExp();
479         }
480 
481         Type tb = t.baseElemOf();
482         if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
483         {
484             if (sd.isPOD())
485                 goto Ltrue;
486             else
487                 goto Lfalse;
488         }
489         goto Ltrue;
490     }
491     if (e.ident == Id.isNested)
492     {
493         if (dim != 1)
494             return dimError(1);
495 
496         auto o = (*e.args)[0];
497         auto s = getDsymbol(o);
498         if (!s)
499         {
500         }
501         else if (auto ad = s.isAggregateDeclaration())
502         {
503             if (ad.isNested())
504                 goto Ltrue;
505             else
506                 goto Lfalse;
507         }
508         else if (auto fd = s.isFuncDeclaration())
509         {
510             if (fd.isNested())
511                 goto Ltrue;
512             else
513                 goto Lfalse;
514         }
515 
516         e.error("aggregate or function expected instead of '%s'", o.toChars());
517         return new ErrorExp();
518     }
519     if (e.ident == Id.isAbstractFunction)
520     {
521         return isFuncX(f => f.isAbstract());
522     }
523     if (e.ident == Id.isVirtualFunction)
524     {
525         return isFuncX(f => f.isVirtual());
526     }
527     if (e.ident == Id.isVirtualMethod)
528     {
529         return isFuncX(f => f.isVirtualMethod());
530     }
531     if (e.ident == Id.isFinalFunction)
532     {
533         return isFuncX(f => f.isFinalFunc());
534     }
535     if (e.ident == Id.isOverrideFunction)
536     {
537         return isFuncX(f => f.isOverride());
538     }
539     if (e.ident == Id.isStaticFunction)
540     {
541         return isFuncX(f => !f.needThis() && !f.isNested());
542     }
543     if (e.ident == Id.isRef)
544     {
545         return isDeclX(d => d.isRef());
546     }
547     if (e.ident == Id.isOut)
548     {
549         return isDeclX(d => d.isOut());
550     }
551     if (e.ident == Id.isLazy)
552     {
553         return isDeclX(d => (d.storage_class & STClazy) != 0);
554     }
555     if (e.ident == Id.identifier)
556     {
557         // Get identifier for symbol as a string literal
558         /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
559          * a symbol should not be folded to a constant.
560          * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
561          */
562         if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2))
563             return new ErrorExp();
564         if (dim != 1)
565             return dimError(1);
566 
567         auto o = (*e.args)[0];
568         Identifier id;
569         if (auto po = isParameter(o))
570         {
571             id = po.ident;
572             assert(id);
573         }
574         else
575         {
576             Dsymbol s = getDsymbol(o);
577             if (!s || !s.ident)
578             {
579                 e.error("argument %s has no identifier", o.toChars());
580                 return new ErrorExp();
581             }
582             id = s.ident;
583         }
584 
585         auto se = new StringExp(e.loc, cast(char*)id.toChars());
586         return se.semantic(sc);
587     }
588     if (e.ident == Id.getProtection)
589     {
590         if (dim != 1)
591             return dimError(1);
592 
593         Scope* sc2 = sc.push();
594         sc2.flags = sc.flags | SCOPEnoaccesscheck;
595         bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
596         sc2.pop();
597         if (!ok)
598             return new ErrorExp();
599 
600         auto o = (*e.args)[0];
601         auto s = getDsymbol(o);
602         if (!s)
603         {
604             if (!isError(o))
605                 e.error("argument %s has no protection", o.toChars());
606             return new ErrorExp();
607         }
608         if (s._scope)
609             s.semantic(s._scope);
610 
611         auto protName = protectionToChars(s.prot().kind); // TODO: How about package(names)
612         assert(protName);
613         auto se = new StringExp(e.loc, cast(char*)protName);
614         return se.semantic(sc);
615     }
616     if (e.ident == Id.parent)
617     {
618         if (dim != 1)
619             return dimError(1);
620 
621         auto o = (*e.args)[0];
622         auto s = getDsymbol(o);
623         if (s)
624         {
625             if (auto fd = s.isFuncDeclaration()) // Bugzilla 8943
626                 s = fd.toAliasFunc();
627             if (!s.isImport()) // Bugzilla 8922
628                 s = s.toParent();
629         }
630         if (!s || s.isImport())
631         {
632             e.error("argument %s has no parent", o.toChars());
633             return new ErrorExp();
634         }
635 
636         if (auto f = s.isFuncDeclaration())
637         {
638             if (auto td = getFuncTemplateDecl(f))
639             {
640                 if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
641                     td = td.overroot; // then get the start
642                 Expression ex = new TemplateExp(e.loc, td, f);
643                 ex = ex.semantic(sc);
644                 return ex;
645             }
646             if (auto fld = f.isFuncLiteralDeclaration())
647             {
648                 // Directly translate to VarExp instead of FuncExp
649                 Expression ex = new VarExp(e.loc, fld, true);
650                 return ex.semantic(sc);
651             }
652         }
653         return DsymbolExp.resolve(e.loc, sc, s, false);
654     }
655     if (e.ident == Id.hasMember ||
656         e.ident == Id.getMember ||
657         e.ident == Id.getOverloads ||
658         e.ident == Id.getVirtualMethods ||
659         e.ident == Id.getVirtualFunctions)
660     {
661         if (dim != 2)
662             return dimError(2);
663 
664         auto o = (*e.args)[0];
665         auto ex = isExpression((*e.args)[1]);
666         if (!ex)
667         {
668             e.error("expression expected as second argument of __traits %s", e.ident.toChars());
669             return new ErrorExp();
670         }
671         ex = ex.ctfeInterpret();
672 
673         StringExp se = ex.toStringExp();
674         if (!se || se.len == 0)
675         {
676             e.error("string expected as second argument of __traits %s instead of %s", e.ident.toChars(), ex.toChars());
677             return new ErrorExp();
678         }
679         se = se.toUTF8(sc);
680 
681         if (se.sz != 1)
682         {
683             e.error("string must be chars");
684             return new ErrorExp();
685         }
686         auto id = Identifier.idPool(se.peekSlice());
687 
688         /* Prefer dsymbol, because it might need some runtime contexts.
689          */
690         Dsymbol sym = getDsymbol(o);
691         if (sym)
692         {
693             ex = new DsymbolExp(e.loc, sym);
694             ex = new DotIdExp(e.loc, ex, id);
695         }
696         else if (auto t = isType(o))
697             ex = typeDotIdExp(e.loc, t, id);
698         else if (auto ex2 = isExpression(o))
699             ex = new DotIdExp(e.loc, ex2, id);
700         else
701         {
702             e.error("invalid first argument");
703             return new ErrorExp();
704         }
705         if (e.ident == Id.hasMember)
706         {
707             if (sym)
708             {
709                 if (auto sm = sym.search(e.loc, id))
710                     goto Ltrue;
711             }
712 
713             /* Take any errors as meaning it wasn't found
714              */
715             Scope* sc2 = sc.push();
716             ex = ex.trySemantic(sc2);
717             sc2.pop();
718             if (!ex)
719                 goto Lfalse;
720             else
721                 goto Ltrue;
722         }
723         else if (e.ident == Id.getMember)
724         {
725             ex = ex.semantic(sc);
726             return ex;
727         }
728         else if (e.ident == Id.getVirtualFunctions ||
729                  e.ident == Id.getVirtualMethods ||
730                  e.ident == Id.getOverloads)
731         {
732             uint errors = global.errors;
733             Expression eorig = ex;
734             ex = ex.semantic(sc);
735             if (errors < global.errors)
736                 e.error("%s cannot be resolved", eorig.toChars());
737             //ex->print();
738 
739             /* Create tuple of functions of ex
740              */
741             auto exps = new Expressions();
742             FuncDeclaration f;
743             if (ex.op == TOKvar)
744             {
745                 VarExp ve = cast(VarExp)ex;
746                 f = ve.var.isFuncDeclaration();
747                 ex = null;
748             }
749             else if (ex.op == TOKdotvar)
750             {
751                 DotVarExp dve = cast(DotVarExp)ex;
752                 f = dve.var.isFuncDeclaration();
753                 if (dve.e1.op == TOKdottype || dve.e1.op == TOKthis)
754                     ex = null;
755                 else
756                     ex = dve.e1;
757             }
758 
759             overloadApply(f, (Dsymbol s)
760             {
761                 auto fd = s.isFuncDeclaration();
762                 if (!fd)
763                     return 0;
764                 if (e.ident == Id.getVirtualFunctions && !fd.isVirtual())
765                     return 0;
766                 if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod())
767                     return 0;
768 
769                 auto fa = new FuncAliasDeclaration(fd.ident, fd, false);
770                 fa.protection = fd.protection;
771 
772                 auto e = ex ? new DotVarExp(Loc(), ex, fa, false)
773                             : new DsymbolExp(Loc(), fa, false);
774 
775                 exps.push(e);
776                 return 0;
777             });
778 
779             auto tup = new TupleExp(e.loc, exps);
780             return tup.semantic(sc);
781         }
782         else
783             assert(0);
784     }
785     if (e.ident == Id.classInstanceSize)
786     {
787         if (dim != 1)
788             return dimError(1);
789 
790         auto o = (*e.args)[0];
791         auto s = getDsymbol(o);
792         auto cd = s ? s.isClassDeclaration() : null;
793         if (!cd)
794         {
795             e.error("first argument is not a class");
796             return new ErrorExp();
797         }
798         if (cd.sizeok != SIZEOKdone)
799         {
800             cd.size(e.loc);
801         }
802         if (cd.sizeok != SIZEOKdone)
803         {
804             e.error("%s %s is forward referenced", cd.kind(), cd.toChars());
805             return new ErrorExp();
806         }
807 
808         return new IntegerExp(e.loc, cd.structsize, Type.tsize_t);
809     }
810     if (e.ident == Id.getAliasThis)
811     {
812         if (dim != 1)
813             return dimError(1);
814 
815         auto o = (*e.args)[0];
816         auto s = getDsymbol(o);
817         auto ad = s ? s.isAggregateDeclaration() : null;
818         if (!ad)
819         {
820             e.error("argument is not an aggregate type");
821             return new ErrorExp();
822         }
823 
824         auto exps = new Expressions();
825         if (ad.aliasthis)
826             exps.push(new StringExp(e.loc, cast(char*)ad.aliasthis.ident.toChars()));
827         Expression ex = new TupleExp(e.loc, exps);
828         ex = ex.semantic(sc);
829         return ex;
830     }
831     if (e.ident == Id.getAttributes)
832     {
833         if (dim != 1)
834             return dimError(1);
835 
836         auto o = (*e.args)[0];
837         auto s = getDsymbol(o);
838         if (!s)
839         {
840             version (none)
841             {
842                 Expression x = isExpression(o);
843                 Type t = isType(o);
844                 if (x)
845                     printf("e = %s %s\n", Token.toChars(x.op), x.toChars());
846                 if (t)
847                     printf("t = %d %s\n", t.ty, t.toChars());
848             }
849             e.error("first argument is not a symbol");
850             return new ErrorExp();
851         }
852         if (auto imp = s.isImport())
853         {
854             s = imp.mod;
855         }
856 
857         //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttribDecl, s->scope);
858         auto udad = s.userAttribDecl;
859         auto exps = udad ? udad.getAttributes() : new Expressions();
860         auto tup = new TupleExp(e.loc, exps);
861         return tup.semantic(sc);
862     }
863     if (e.ident == Id.getFunctionAttributes)
864     {
865         // extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
866         if (dim != 1)
867             return dimError(1);
868 
869         auto o = (*e.args)[0];
870         auto s = getDsymbol(o);
871         auto t = isType(o);
872         TypeFunction tf = null;
873         if (s)
874         {
875             if (auto fd = s.isFuncDeclaration())
876                 t = fd.type;
877             else if (auto vd = s.isVarDeclaration())
878                 t = vd.type;
879         }
880         if (t)
881         {
882             if (t.ty == Tfunction)
883                 tf = cast(TypeFunction)t;
884             else if (t.ty == Tdelegate)
885                 tf = cast(TypeFunction)t.nextOf();
886             else if (t.ty == Tpointer && t.nextOf().ty == Tfunction)
887                 tf = cast(TypeFunction)t.nextOf();
888         }
889         if (!tf)
890         {
891             e.error("first argument is not a function");
892             return new ErrorExp();
893         }
894 
895         auto mods = new Expressions();
896         PushAttributes pa;
897         pa.mods = mods;
898         tf.modifiersApply(&pa, &PushAttributes.fp);
899         tf.attributesApply(&pa, &PushAttributes.fp, TRUSTformatSystem);
900 
901         auto tup = new TupleExp(e.loc, mods);
902         return tup.semantic(sc);
903     }
904     if (e.ident == Id.allMembers ||
905         e.ident == Id.derivedMembers)
906     {
907         if (dim != 1)
908             return dimError(1);
909 
910         auto o = (*e.args)[0];
911         auto s = getDsymbol(o);
912         if (!s)
913         {
914             e.error("argument has no members");
915             return new ErrorExp();
916         }
917         if (auto imp = s.isImport())
918         {
919             // Bugzilla 9692
920             s = imp.mod;
921         }
922 
923         auto sds = s.isScopeDsymbol();
924         if (!sds || sds.isTemplateDeclaration())
925         {
926             e.error("%s %s has no members", s.kind(), s.toChars());
927             return new ErrorExp();
928         }
929 
930         auto idents = new Identifiers();
931 
932         int pushIdentsDg(size_t n, Dsymbol sm)
933         {
934             if (!sm)
935                 return 1;
936             //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars());
937             if (sm.ident)
938             {
939                 const idx = sm.ident.toChars();
940                 if (idx[0] == '_' &&
941                     idx[1] == '_' &&
942                     sm.ident != Id.ctor &&
943                     sm.ident != Id.dtor &&
944                     sm.ident != Id.__xdtor &&
945                     sm.ident != Id.postblit &&
946                     sm.ident != Id.__xpostblit)
947                 {
948                     return 0;
949                 }
950                 if (sm.ident == Id.empty)
951                 {
952                     return 0;
953                 }
954                 if (sm.isTypeInfoDeclaration()) // Bugzilla 15177
955                     return 0;
956 
957                 //printf("\t%s\n", sm->ident->toChars());
958 
959                 /* Skip if already present in idents[]
960                  */
961                 foreach (id; *idents)
962                 {
963                     if (id == sm.ident)
964                         return 0;
965 
966                     // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
967                     debug assert(strcmp(id.toChars(), sm.ident.toChars()) != 0);
968                 }
969                 idents.push(sm.ident);
970             }
971             else if (auto ed = sm.isEnumDeclaration())
972             {
973                 ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg);
974             }
975             return 0;
976         }
977 
978         ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg);
979         auto cd = sds.isClassDeclaration();
980         if (cd && e.ident == Id.allMembers)
981         {
982             if (cd._scope)
983                 cd.semantic(null); // Bugzilla 13668: Try to resolve forward reference
984 
985             void pushBaseMembersDg(ClassDeclaration cd)
986             {
987                 for (size_t i = 0; i < cd.baseclasses.dim; i++)
988                 {
989                     auto cb = (*cd.baseclasses)[i].sym;
990                     assert(cb);
991                     ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg);
992                     if (cb.baseclasses.dim)
993                         pushBaseMembersDg(cb);
994                 }
995             }
996 
997             pushBaseMembersDg(cd);
998         }
999 
1000         // Turn Identifiers into StringExps reusing the allocated array
1001         assert(Expressions.sizeof == Identifiers.sizeof);
1002         auto exps = cast(Expressions*)idents;
1003         foreach (i, id; *idents)
1004         {
1005             auto se = new StringExp(e.loc, cast(char*)id.toChars());
1006             (*exps)[i] = se;
1007         }
1008 
1009         /* Making this a tuple is more flexible, as it can be statically unrolled.
1010          * To make an array literal, enclose __traits in [ ]:
1011          *   [ __traits(allMembers, ...) ]
1012          */
1013         Expression ex = new TupleExp(e.loc, exps);
1014         ex = ex.semantic(sc);
1015         return ex;
1016     }
1017     if (e.ident == Id.compiles)
1018     {
1019         /* Determine if all the objects - types, expressions, or symbols -
1020          * compile without error
1021          */
1022         if (!dim)
1023             goto Lfalse;
1024 
1025         foreach (o; *e.args)
1026         {
1027             uint errors = global.startGagging();
1028             Scope* sc2 = sc.push();
1029             sc2.tinst = null;
1030             sc2.minst = null;
1031             sc2.flags = (sc.flags & ~(SCOPEctfe | SCOPEcondition)) | SCOPEcompile | SCOPEfullinst;
1032 
1033             bool err = false;
1034 
1035             auto t = isType(o);
1036             auto ex = t ? t.toExpression() : isExpression(o);
1037             if (!ex && t)
1038             {
1039                 Dsymbol s;
1040                 t.resolve(e.loc, sc2, &ex, &t, &s);
1041                 if (t)
1042                 {
1043                     t.semantic(e.loc, sc2);
1044                     if (t.ty == Terror)
1045                         err = true;
1046                 }
1047                 else if (s && s.errors)
1048                     err = true;
1049             }
1050             if (ex)
1051             {
1052                 ex = ex.semantic(sc2);
1053                 ex = resolvePropertiesOnly(sc2, ex);
1054                 ex = ex.optimize(WANTvalue);
1055                 if (sc2.func && sc2.func.type.ty == Tfunction)
1056                 {
1057                     auto tf = cast(TypeFunction)sc2.func.type;
1058                     canThrow(ex, sc2.func, tf.isnothrow);
1059                 }
1060                 ex = checkGC(sc2, ex);
1061                 if (ex.op == TOKerror)
1062                     err = true;
1063             }
1064 
1065             sc2.pop();
1066 
1067             if (global.endGagging(errors) || err)
1068             {
1069                 goto Lfalse;
1070             }
1071         }
1072         goto Ltrue;
1073     }
1074     if (e.ident == Id.isSame)
1075     {
1076         /* Determine if two symbols are the same
1077          */
1078         if (dim != 2)
1079             return dimError(2);
1080 
1081         if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 0))
1082             return new ErrorExp();
1083 
1084         auto o1 = (*e.args)[0];
1085         auto o2 = (*e.args)[1];
1086         auto s1 = getDsymbol(o1);
1087         auto s2 = getDsymbol(o2);
1088         //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars());
1089         version (none)
1090         {
1091             printf("o1: %p\n", o1);
1092             printf("o2: %p\n", o2);
1093             if (!s1)
1094             {
1095                 if (auto ea = isExpression(o1))
1096                     printf("%s\n", ea.toChars());
1097                 if (auto ta = isType(o1))
1098                     printf("%s\n", ta.toChars());
1099                 goto Lfalse;
1100             }
1101             else
1102                 printf("%s %s\n", s1.kind(), s1.toChars());
1103         }
1104         if (!s1 && !s2)
1105         {
1106             auto ea1 = isExpression(o1);
1107             auto ea2 = isExpression(o2);
1108             if (ea1 && ea2)
1109             {
1110                 if (ea1.equals(ea2))
1111                     goto Ltrue;
1112             }
1113         }
1114         if (!s1 || !s2)
1115             goto Lfalse;
1116         s1 = s1.toAlias();
1117         s2 = s2.toAlias();
1118 
1119         if (auto fa1 = s1.isFuncAliasDeclaration())
1120             s1 = fa1.toAliasFunc();
1121         if (auto fa2 = s2.isFuncAliasDeclaration())
1122             s2 = fa2.toAliasFunc();
1123 
1124         if (s1 == s2)
1125             goto Ltrue;
1126         else
1127             goto Lfalse;
1128     }
1129     if (e.ident == Id.getUnitTests)
1130     {
1131         if (dim != 1)
1132             return dimError(1);
1133 
1134         auto o = (*e.args)[0];
1135         auto s = getDsymbol(o);
1136         if (!s)
1137         {
1138             e.error("argument %s to __traits(getUnitTests) must be a module or aggregate",
1139                 o.toChars());
1140             return new ErrorExp();
1141         }
1142         if (auto imp = s.isImport()) // Bugzilla 10990
1143             s = imp.mod;
1144 
1145         auto sds = s.isScopeDsymbol();
1146         if (!sds)
1147         {
1148             e.error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s",
1149                 s.toChars(), s.kind());
1150             return new ErrorExp();
1151         }
1152 
1153         auto exps = new Expressions();
1154         if (global.params.useUnitTests)
1155         {
1156             bool[void*] uniqueUnitTests;
1157 
1158             void collectUnitTests(Dsymbols* a)
1159             {
1160                 if (!a)
1161                     return;
1162                 foreach (s; *a)
1163                 {
1164                     if (auto atd = s.isAttribDeclaration())
1165                     {
1166                         collectUnitTests(atd.include(null, null));
1167                         continue;
1168                     }
1169                     if (auto ud = s.isUnitTestDeclaration())
1170                     {
1171                         if (cast(void*)ud in uniqueUnitTests)
1172                             continue;
1173 
1174                         auto ad = new FuncAliasDeclaration(ud.ident, ud, false);
1175                         ad.protection = ud.protection;
1176 
1177                         auto e = new DsymbolExp(Loc(), ad, false);
1178                         exps.push(e);
1179 
1180                         uniqueUnitTests[cast(void*)ud] = true;
1181                     }
1182                 }
1183             }
1184 
1185             collectUnitTests(sds.members);
1186         }
1187         auto te = new TupleExp(e.loc, exps);
1188         return te.semantic(sc);
1189     }
1190     if (e.ident == Id.getVirtualIndex)
1191     {
1192         if (dim != 1)
1193             return dimError(1);
1194 
1195         auto o = (*e.args)[0];
1196         auto s = getDsymbol(o);
1197 
1198         auto fd = s ? s.isFuncDeclaration() : null;
1199         if (!fd)
1200         {
1201             e.error("first argument to __traits(getVirtualIndex) must be a function");
1202             return new ErrorExp();
1203         }
1204 
1205         fd = fd.toAliasFunc(); // Neccessary to support multiple overloads.
1206         return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t);
1207     }
1208     if (e.ident == Id.getPointerBitmap)
1209     {
1210         return pointerBitmap(e);
1211     }
1212 
1213     extern (D) void* trait_search_fp(const(char)* seed, ref int cost)
1214     {
1215         //printf("trait_search_fp('%s')\n", seed);
1216         size_t len = strlen(seed);
1217         if (!len)
1218             return null;
1219         cost = 0;
1220         StringValue* sv = traitsStringTable.lookup(seed, len);
1221         return sv ? sv.ptrvalue : null;
1222     }
1223 
1224     if (auto sub = cast(const(char)*)speller(e.ident.toChars(), &trait_search_fp, idchars))
1225         e.error("unrecognized trait '%s', did you mean '%s'?", e.ident.toChars(), sub);
1226     else
1227         e.error("unrecognized trait '%s'", e.ident.toChars());
1228     return new ErrorExp();
1229 
1230 Lfalse:
1231     return new IntegerExp(e.loc, 0, Type.tbool);
1232 
1233 Ltrue:
1234     return new IntegerExp(e.loc, 1, Type.tbool);
1235 }