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