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