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