d: Merge dmd. druntime e770945277, phobos 6d6e0b9b9
[official-gcc.git] / gcc / d / dmd / traits.d
blobbe7aa9923c03697483a84952ec1a773929937a46
1 /**
2 * Handle introspection functionality of the `__traits()` construct.
4 * Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits)
6 * Copyright: Copyright (C) 1999-2024 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
14 module dmd.traits;
16 import core.stdc.stdio;
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.dinterpret;
28 import dmd.dmangle;
29 import dmd.dmodule;
30 import dmd.dscope;
31 import dmd.dsymbol;
32 import dmd.dsymbolsem;
33 import dmd.dtemplate;
34 import dmd.errors;
35 import dmd.errorsink;
36 import dmd.expression;
37 import dmd.expressionsem;
38 import dmd.func;
39 import dmd.funcsem;
40 import dmd.globals;
41 import dmd.hdrgen;
42 import dmd.id;
43 import dmd.identifier;
44 import dmd.location;
45 import dmd.mtype;
46 import dmd.nogc;
47 import dmd.optimize;
48 import dmd.parse;
49 import dmd.root.array;
50 import dmd.root.speller;
51 import dmd.root.stringtable;
52 import dmd.target;
53 import dmd.tokens;
54 import dmd.typesem;
55 import dmd.visitor;
56 import dmd.rootobject;
57 import dmd.common.outbuffer;
58 import dmd.root.string;
60 enum LOGSEMANTIC = false;
62 /************************ TraitsExp ************************************/
64 /**************************************
65 * Convert `Expression` or `Type` to corresponding `Dsymbol`, additionally
66 * stripping off expression contexts.
68 * Some symbol related `__traits` ignore arguments expression contexts.
69 * For example:
70 * ----
71 * struct S { void f() {} }
72 * S s;
73 * pragma(msg, __traits(isNested, s.f));
74 * // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`.
75 * ----
77 * This is used for that common `__traits` behavior.
79 * Input:
80 * oarg object to get the symbol for
81 * Returns:
82 * Dsymbol the corresponding symbol for oarg
84 private Dsymbol getDsymbolWithoutExpCtx(RootObject oarg)
86 if (auto e = isExpression(oarg))
88 if (auto dve = e.isDotVarExp())
89 return dve.var;
90 if (auto dte = e.isDotTemplateExp())
91 return dte.td;
93 return getDsymbol(oarg);
96 /**
97 * Fill an array of target size_t values that indicate possible pointer words in memory
98 * if interpreted as the type given as argument.
99 * One bit in the array per pointer-sized piece of memory
100 * Params:
101 * loc = location for error messages
102 * t = type to generate pointer bitmap from
103 * data = array forming the bitmap
104 * eSink = error message sink
105 * Returns:
106 * size of the type `t` in bytes, ulong.max on error
108 ulong getTypePointerBitmap(Loc loc, Type t, ref Array!(ulong) data, ErrorSink eSink)
110 auto tc = t.isTypeClass();
111 const ulong sz = (tc && !tc.sym.isInterfaceDeclaration())
112 ? tc.sym.AggregateDeclaration.size(loc)
113 : t.size(loc);
114 if (sz == SIZE_INVALID)
115 return ulong.max;
117 const sz_size_t = Type.tsize_t.size(loc); // size of target's size_t
118 assert(sz_size_t <= ulong.sizeof);
119 if (sz > sz.max - sz_size_t)
121 eSink.error(loc, "size overflow for type `%s`", t.toChars());
122 return ulong.max;
125 const ulong bitsPerElement = sz_size_t * 8; // bits used in each array element
126 const ulong cntptr = (sz + sz_size_t - 1) / sz_size_t; // pointers have same size as sz_size_t
127 const ulong length = (cntptr + bitsPerElement - 1) / bitsPerElement; // a bit per pointer
129 data.setDim(cast(size_t)length);
130 data.zero();
132 ulong offset;
133 bool error; // sticky error indicator
135 void visit(Type t)
137 void setpointer(ulong off)
139 ulong ptroff = off / sz_size_t;
140 data[cast(size_t)(ptroff / bitsPerElement)] |= 1L << (ptroff % bitsPerElement);
143 void visitType(Type t)
145 Type tb = t.toBasetype();
146 if (tb != t)
147 visit(tb);
150 void visitError(TypeError t)
152 visitType(t);
155 void visitBasic(TypeBasic t)
157 if (t.ty == Tvoid)
158 setpointer(offset);
161 void visitVector(TypeVector t)
165 void visitSArray(TypeSArray t)
167 ulong arrayoff = offset;
168 ulong nextsize = t.next.size();
169 if (nextsize == SIZE_INVALID)
170 error = true;
171 ulong dim = t.dim.toInteger();
172 for (ulong i = 0; i < dim; i++)
174 offset = arrayoff + i * nextsize;
175 visit(t.next);
177 offset = arrayoff;
180 void visitDArray(TypeDArray t)
182 setpointer(offset + sz_size_t);
185 // dynamic array is {length,ptr}
186 void visitAArray(TypeAArray t)
188 setpointer(offset);
191 void visitPointer(TypePointer t)
193 if (t.nextOf().ty != Tfunction) // don't mark function pointers
194 setpointer(offset);
197 void visitReference(TypeReference t)
199 setpointer(offset);
202 void visitClass(TypeClass t)
204 setpointer(offset);
207 void visitFunction(TypeFunction t)
211 void visitDelegate(TypeDelegate t)
213 setpointer(offset);
216 void visitEnum(TypeEnum t)
218 visitType(t);
221 void visitTuple(TypeTuple t)
223 visitType(t);
226 void visitNull(TypeNull t)
228 // always a null pointer
231 void visitNoreturn(TypeNoreturn t)
235 void visitStruct(TypeStruct t)
237 ulong structoff = offset;
238 foreach (v; t.sym.fields)
240 offset = structoff + v.offset;
241 if (v.type.ty == Tclass)
242 setpointer(offset);
243 else
244 visit(v.type);
246 offset = structoff;
249 void visitDefaultCase(Type t)
251 //printf("ty = %d\n", t.ty);
252 assert(0);
255 mixin VisitType!void visit;
256 visit.VisitType(t);
259 if (auto tcx = t.isTypeClass())
261 // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
262 void visitTopLevelClass(TypeClass t)
264 ulong classoff = offset;
265 // skip vtable-ptr and monitor
266 if (t.sym.baseClass)
267 visitTopLevelClass(t.sym.baseClass.type.isTypeClass());
268 foreach (v; t.sym.fields)
270 offset = classoff + v.offset;
271 visit(v.type);
273 offset = classoff;
276 visitTopLevelClass(tcx);
278 else
279 visit(t);
280 return error ? ulong.max : sz;
284 * get an array of size_t values that indicate possible pointer words in memory
285 * if interpreted as the type given as argument
286 * the first array element is the size of the type for independent interpretation
287 * of the array
288 * following elements bits represent one word (4/8 bytes depending on the target
289 * architecture). If set the corresponding memory might contain a pointer/reference.
291 * Returns: [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
293 private Expression pointerBitmap(TraitsExp e, ErrorSink eSink)
295 if (!e.args || e.args.length != 1)
297 eSink.error(e.loc, "a single type expected for trait pointerBitmap");
298 return ErrorExp.get();
301 Type t = getType((*e.args)[0]);
302 if (!t)
304 eSink.error(e.loc, "`%s` is not a type", (*e.args)[0].toChars());
305 return ErrorExp.get();
308 Array!(ulong) data;
309 const ulong sz = getTypePointerBitmap(e.loc, t, data, eSink);
310 if (sz == ulong.max)
311 return ErrorExp.get();
313 auto exps = new Expressions(data.length + 1);
314 (*exps)[0] = new IntegerExp(e.loc, sz, Type.tsize_t); // [0] is size in bytes of t
315 foreach (size_t i; 1 .. exps.length)
316 (*exps)[i] = new IntegerExp(e.loc, data[cast(size_t) (i - 1)], Type.tsize_t);
318 auto ale = new ArrayLiteralExp(e.loc, Type.tsize_t.sarrayOf(data.length + 1), exps);
319 return ale;
322 Expression semanticTraits(TraitsExp e, Scope* sc)
324 static if (LOGSEMANTIC)
326 printf("TraitsExp::semantic() %s\n", e.toChars());
329 if (e.ident != Id.compiles &&
330 e.ident != Id.isSame &&
331 e.ident != Id.identifier &&
332 e.ident != Id.getProtection && e.ident != Id.getVisibility &&
333 e.ident != Id.getAttributes)
335 // Pretend we're in a deprecated scope so that deprecation messages
336 // aren't triggered when checking if a symbol is deprecated
337 const save = sc.stc;
338 if (e.ident == Id.isDeprecated)
339 sc.stc |= STC.deprecated_;
340 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1))
342 sc.stc = save;
343 return ErrorExp.get();
345 sc.stc = save;
347 size_t dim = e.args ? e.args.length : 0;
349 Expression dimError(int expected)
351 error(e.loc, "expected %d arguments for `%s` but had %d", expected, e.ident.toChars(), cast(int)dim);
352 return ErrorExp.get();
355 static IntegerExp True()
357 return IntegerExp.createBool(true);
360 static IntegerExp False()
362 return IntegerExp.createBool(false);
365 /********
366 * Gets the function type from a given AST node
367 * if the node is a function of some sort.
368 * Params:
369 * o = an AST node to check for a `TypeFunction`
370 * fdp = if `o` is a FuncDeclaration then fdp is set to that, otherwise `null`
371 * Returns:
372 * a type node if `o` is a declaration of
373 * a delegate, function, function-pointer or a variable of the former.
374 * Otherwise, `null`.
376 static TypeFunction toTypeFunction(RootObject o, out FuncDeclaration fdp)
378 Type t;
379 if (auto s = getDsymbolWithoutExpCtx(o))
381 if (auto fd = s.isFuncDeclaration())
383 t = fd.type;
384 fdp = fd;
386 else if (auto vd = s.isVarDeclaration())
387 t = vd.type;
388 else
389 t = isType(o);
391 else
392 t = isType(o);
394 if (t)
396 if (auto tf = t.isFunction_Delegate_PtrToFunction())
397 return tf;
400 return null;
403 IntegerExp isX(T)(bool delegate(T) fp)
405 if (!dim)
406 return False();
407 foreach (o; *e.args)
409 static if (is(T == Type))
410 auto y = getType(o);
412 static if (is(T : Dsymbol))
414 auto s = getDsymbolWithoutExpCtx(o);
415 if (!s)
416 return False();
418 static if (is(T == Dsymbol))
419 alias y = s;
420 static if (is(T == Declaration))
421 auto y = s.isDeclaration();
422 static if (is(T == FuncDeclaration))
423 auto y = s.isFuncDeclaration();
425 if (!y || !fp(y))
426 return False();
428 return True();
431 alias isTypeX = isX!Type;
432 alias isDsymX = isX!Dsymbol;
433 alias isDeclX = isX!Declaration;
434 alias isFuncX = isX!FuncDeclaration;
436 Expression isPkgX(bool function(Package) fp)
438 return isDsymX((Dsymbol sym) {
439 Package p = resolveIsPackage(sym);
440 return (p !is null) && fp(p);
444 if (e.ident == Id.isArithmetic)
446 return isTypeX(t => t.isintegral() || t.isfloating());
448 if (e.ident == Id.isFloating)
450 return isTypeX(t => t.isfloating());
452 if (e.ident == Id.isIntegral)
454 return isTypeX(t => t.isintegral());
456 if (e.ident == Id.isScalar)
458 return isTypeX(t => t.isscalar());
460 if (e.ident == Id.isUnsigned)
462 return isTypeX(t => t.isunsigned());
464 if (e.ident == Id.isAssociativeArray)
466 return isTypeX(t => t.toBasetype().ty == Taarray);
468 if (e.ident == Id.isDeprecated)
470 if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true))
471 return True();
472 return isDsymX(t => t.isDeprecated());
474 if (e.ident == Id.isFuture)
476 return isDeclX(t => t.isFuture());
478 if (e.ident == Id.isStaticArray)
480 return isTypeX(t => t.toBasetype().ty == Tsarray);
482 if (e.ident == Id.isAbstractClass)
484 return isTypeX(t => t.toBasetype().isTypeClass() &&
485 t.toBasetype().isTypeClass().sym.isAbstract());
487 if (e.ident == Id.isFinalClass)
489 return isTypeX(t => t.toBasetype().isTypeClass() &&
490 (t.toBasetype().isTypeClass().sym.storage_class & STC.final_) != 0);
492 if (e.ident == Id.isTemplate)
494 if (dim != 1)
495 return dimError(1);
497 return isDsymX((s)
499 if (!s.toAlias().isOverloadable())
500 return false;
501 return overloadApply(s,
502 sm => sm.isTemplateDeclaration() !is null) != 0;
505 if (e.ident == Id.isPOD)
507 if (dim != 1)
508 return dimError(1);
510 auto o = (*e.args)[0];
511 auto t = isType(o);
512 if (!t)
514 error(e.loc, "type expected as second argument of __traits `%s` instead of `%s`",
515 e.ident.toChars(), o.toChars());
516 return ErrorExp.get();
519 Type tb = t.baseElemOf();
520 auto ts = tb.isTypeStruct();
521 if (auto sd = ts ? ts.sym : null)
523 return sd.isPOD() ? True() : False();
525 return True();
527 if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit)
529 if (dim != 1)
530 return dimError(1);
532 auto o = (*e.args)[0];
533 auto t = isType(o);
534 if (!t)
536 error(e.loc, "type expected as second argument of __traits `%s` instead of `%s`",
537 e.ident.toChars(), o.toChars());
538 return ErrorExp.get();
541 Type tb = t.baseElemOf();
542 auto ts = tb.isTypeStruct();
543 if (auto sd = ts ? ts.sym : null)
545 return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False())
546 : (sd.hasCopyCtor ? True() : False());
548 return False();
550 if (e.ident == Id.isCopyable)
552 if (dim != 1)
553 return dimError(1);
555 auto o = (*e.args)[0];
556 auto t = isType(o);
557 if (!t)
559 error(e.loc, "type expected as second argument of __traits `%s` instead of `%s`",
560 e.ident.toChars(), o.toChars());
561 return ErrorExp.get();
564 t = t.toBasetype(); // get the base in case `t` is an `enum`
566 if (auto ts = t.isTypeStruct())
568 ts.sym.dsymbolSemantic(sc);
571 return isCopyable(t) ? True() : False();
574 if (e.ident == Id.isNested)
576 if (dim != 1)
577 return dimError(1);
579 auto o = (*e.args)[0];
580 auto s = getDsymbolWithoutExpCtx(o);
581 if (!s)
584 else if (auto ad = s.isAggregateDeclaration())
586 return ad.isNested() ? True() : False();
588 else if (auto fd = s.isFuncDeclaration())
590 return fd.isNested() ? True() : False();
593 error(e.loc, "aggregate or function expected instead of `%s`", o.toChars());
594 return ErrorExp.get();
596 if (e.ident == Id.isDisabled)
598 if (dim != 1)
599 return dimError(1);
601 return isDeclX(f => f.isDisabled());
603 if (e.ident == Id.isAbstractFunction)
605 if (dim != 1)
606 return dimError(1);
608 return isFuncX(f => f.isAbstract());
610 if (e.ident == Id.isVirtualFunction)
612 // @@@DEPRECATED2.121@@@
613 // Deprecated in 2.101 - Can be removed from 2.121
614 deprecation(e.loc, "`traits(isVirtualFunction)` is deprecated. Use `traits(isVirtualMethod)` instead");
616 if (dim != 1)
617 return dimError(1);
619 return isFuncX(f => f.isVirtual());
621 if (e.ident == Id.isVirtualMethod)
623 if (dim != 1)
624 return dimError(1);
626 return isFuncX(f => f.isVirtualMethod());
628 if (e.ident == Id.isFinalFunction)
630 if (dim != 1)
631 return dimError(1);
633 return isFuncX(f => f.isFinalFunc());
635 if (e.ident == Id.isOverrideFunction)
637 if (dim != 1)
638 return dimError(1);
640 return isFuncX(f => f.isOverride());
642 if (e.ident == Id.isStaticFunction)
644 if (dim != 1)
645 return dimError(1);
647 return isFuncX(f => !f.needThis() && !f.isNested());
649 if (e.ident == Id.isModule)
651 if (dim != 1)
652 return dimError(1);
654 return isPkgX(p => p.isModule() || p.isPackageMod());
656 if (e.ident == Id.isPackage)
658 if (dim != 1)
659 return dimError(1);
661 return isPkgX(p => p.isModule() is null);
663 if (e.ident == Id.isRef)
665 if (dim != 1)
666 return dimError(1);
668 return isDeclX(d => d.isRef());
670 if (e.ident == Id.isOut)
672 if (dim != 1)
673 return dimError(1);
675 return isDeclX(d => d.isOut());
677 if (e.ident == Id.isLazy)
679 if (dim != 1)
680 return dimError(1);
682 return isDeclX(d => (d.storage_class & STC.lazy_) != 0);
684 if (e.ident == Id.identifier)
686 // Get identifier for symbol as a string literal
687 /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
688 * a symbol should not be folded to a constant.
689 * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
691 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2))
692 return ErrorExp.get();
693 if (dim != 1)
694 return dimError(1);
696 auto o = (*e.args)[0];
697 Identifier id;
698 if (auto po = isParameter(o))
700 if (!po.ident)
702 error(e.loc, "argument `%s` has no identifier", po.type.toChars());
703 return ErrorExp.get();
705 id = po.ident;
707 else
709 Dsymbol s = getDsymbolWithoutExpCtx(o);
710 if (!s || !s.ident)
712 error(e.loc, "argument `%s` has no identifier", o.toChars());
713 return ErrorExp.get();
715 id = s.ident;
718 auto se = new StringExp(e.loc, id.toString());
719 return se.expressionSemantic(sc);
721 if (e.ident == Id.fullyQualifiedName) // https://dlang.org/spec/traits.html#fullyQualifiedName
723 if (dim != 1)
724 return dimError(1);
726 Scope* sc2 = sc.push();
727 sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
728 bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
729 sc2.pop();
730 if (!ok)
731 return ErrorExp.get();
733 const(char)[] fqn;
734 auto o = (*e.args)[0];
735 if (auto s = getDsymbolWithoutExpCtx(o))
737 if (s.semanticRun == PASS.initial)
738 s.dsymbolSemantic(null);
740 fqn = s.toPrettyChars().toDString();
742 else if (auto t = getType(o))
744 fqn = t.toPrettyChars(true).toDString();
746 else
748 if (!isError(o))
749 error(e.loc, "argument `%s` has no identifier", o.toChars());
750 return ErrorExp.get();
752 assert(fqn);
753 auto se = new StringExp(e.loc, fqn);
754 return se.expressionSemantic(sc);
757 if (e.ident == Id.getProtection || e.ident == Id.getVisibility)
759 if (dim != 1)
760 return dimError(1);
762 Scope* sc2 = sc.push();
763 sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
764 bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
765 sc2.pop();
766 if (!ok)
767 return ErrorExp.get();
769 auto o = (*e.args)[0];
770 auto s = getDsymbolWithoutExpCtx(o);
771 if (!s)
773 if (!isError(o))
774 error(e.loc, "argument `%s` has no visibility", o.toChars());
775 return ErrorExp.get();
777 if (s.semanticRun == PASS.initial)
778 s.dsymbolSemantic(null);
780 auto protName = visibilityToString(s.visible().kind); // TODO: How about package(names)
781 assert(protName);
782 auto se = new StringExp(e.loc, protName);
783 return se.expressionSemantic(sc);
785 if (e.ident == Id.parent)
787 if (dim != 1)
788 return dimError(1);
790 auto o = (*e.args)[0];
791 auto s = getDsymbolWithoutExpCtx(o);
792 if (s)
794 // https://issues.dlang.org/show_bug.cgi?id=12496
795 // Consider:
796 // class T1
797 // {
798 // class C(uint value) { }
799 // }
800 // __traits(parent, T1.C!2)
801 if (auto ad = s.isAggregateDeclaration()) // `s` is `C`
803 if (ad.isNested()) // `C` is nested
805 if (auto p = s.toParent()) // `C`'s parent is `C!2`, believe it or not
807 if (auto ti = p.isTemplateInstance()) // `C!2` is a template instance
809 s = p; // `C!2`'s parent is `T1`
810 auto td = ti.tempdecl;
811 if (td)
812 s = td; // get the declaration context just in case there's two contexts
818 if (auto fd = s.isFuncDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=8943
819 s = fd.toAliasFunc();
820 if (!s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=8922
821 s = s.toParent();
823 if (!s || s.isImport())
825 error(e.loc, "argument `%s` has no parent", o.toChars());
826 return ErrorExp.get();
829 if (auto f = s.isFuncDeclaration())
831 if (auto td = getFuncTemplateDecl(f))
833 if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
834 td = td.overroot; // then get the start
835 Expression ex = new TemplateExp(e.loc, td, f);
836 ex = ex.expressionSemantic(sc);
837 return ex;
839 if (auto fld = f.isFuncLiteralDeclaration())
841 // Directly translate to VarExp instead of FuncExp
842 Expression ex = new VarExp(e.loc, fld, true);
843 return ex.expressionSemantic(sc);
846 return symbolToExp(s, e.loc, sc, false);
848 if (e.ident == Id.child)
850 if (dim != 2)
851 return dimError(2);
853 Expression ex;
854 auto op = (*e.args)[0];
855 if (auto symp = getDsymbol(op))
856 ex = new DsymbolExp(e.loc, symp);
857 else if (auto exp = op.isExpression())
858 ex = exp;
859 else
861 error(e.loc, "symbol or expression expected as first argument of __traits `child` instead of `%s`", op.toChars());
862 return ErrorExp.get();
865 ex = ex.expressionSemantic(sc);
866 auto oc = (*e.args)[1];
867 auto symc = getDsymbol(oc);
868 if (!symc)
870 error(e.loc, "symbol expected as second argument of __traits `child` instead of `%s`", oc.toChars());
871 return ErrorExp.get();
874 if (auto d = symc.isDeclaration())
875 ex = new DotVarExp(e.loc, ex, d);
876 else if (auto td = symc.isTemplateDeclaration())
877 ex = new DotExp(e.loc, ex, new TemplateExp(e.loc, td));
878 else if (auto ti = symc.isScopeDsymbol())
879 ex = new DotExp(e.loc, ex, new ScopeExp(e.loc, ti));
880 else
881 assert(0);
883 ex = ex.expressionSemantic(sc);
884 return ex;
886 if (e.ident == Id.toType)
888 if (dim != 1)
889 return dimError(1);
891 auto ex = isExpression((*e.args)[0]);
892 if (!ex)
894 error(e.loc, "expression expected as second argument of __traits `%s`", e.ident.toChars());
895 return ErrorExp.get();
897 ex = ex.ctfeInterpret();
899 StringExp se = semanticString(sc, ex, "__traits(toType, string)");
900 if (!se)
902 return ErrorExp.get();
904 Type t = decoToType(se.toUTF8(sc).peekString());
905 if (!t)
907 error(e.loc, "cannot determine `%s`", e.toChars());
908 return ErrorExp.get();
910 return (new TypeExp(e.loc, t)).expressionSemantic(sc);
912 if (e.ident == Id.hasMember ||
913 e.ident == Id.getMember ||
914 e.ident == Id.getOverloads ||
915 e.ident == Id.getVirtualMethods ||
916 e.ident == Id.getVirtualFunctions)
918 if (dim != 2 && !(dim == 3 && e.ident == Id.getOverloads))
919 return dimError(2);
921 auto o = (*e.args)[0];
922 auto ex = isExpression((*e.args)[1]);
923 if (!ex)
925 error(e.loc, "expression expected as second argument of __traits `%s`", e.ident.toChars());
926 return ErrorExp.get();
928 ex = ex.ctfeInterpret();
930 bool includeTemplates = false;
931 if (dim == 3 && e.ident == Id.getOverloads)
933 auto b = isExpression((*e.args)[2]);
934 b = b.ctfeInterpret();
935 if (!b.type.equals(Type.tbool))
937 error(e.loc, "`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b.toChars(), b.type.toChars());
938 return ErrorExp.get();
940 includeTemplates = b.toBool().get();
943 StringExp se = ex.toStringExp();
944 if (!se || se.len == 0)
946 error(e.loc, "string expected as second argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars());
947 return ErrorExp.get();
949 se = se.toUTF8(sc);
951 if (se.sz != 1)
953 error(e.loc, "string must be chars");
954 return ErrorExp.get();
956 auto id = Identifier.idPool(se.peekString());
958 /* Prefer a Type, because getDsymbol(Type) can lose type modifiers.
959 Then a Dsymbol, because it might need some runtime contexts.
962 Dsymbol sym = getDsymbol(o);
964 if (sym && e.ident == Id.hasMember)
966 if (auto sm = sym.search(e.loc, id))
967 return True();
969 // https://issues.dlang.org/show_bug.cgi?id=23951
970 if (auto decl = sym.isDeclaration())
972 ex = typeDotIdExp(e.loc, decl.type, id);
973 goto doSemantic;
977 if (auto t = isType(o))
978 ex = typeDotIdExp(e.loc, t, id);
979 else if (sym)
981 ex = new DsymbolExp(e.loc, sym);
982 ex = new DotIdExp(e.loc, ex, id);
984 else if (auto ex2 = isExpression(o))
985 ex = new DotIdExp(e.loc, ex2, id);
986 else
988 error(e.loc, "invalid first argument");
989 return ErrorExp.get();
991 doSemantic:
992 // ignore symbol visibility and disable access checks for these traits
993 Scope* scx = sc.push();
994 scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck;
995 scope (exit) scx.pop();
997 if (e.ident == Id.hasMember)
999 /* Take any errors as meaning it wasn't found
1001 ex = ex.trySemantic(scx);
1002 return ex ? True() : False();
1004 else if (e.ident == Id.getMember)
1006 if (auto die = ex.isDotIdExp())
1007 // Prevent semantic() from replacing Symbol with its initializer
1008 die.wantsym = true;
1009 ex = ex.expressionSemantic(scx);
1010 return ex;
1012 else if (e.ident == Id.getVirtualFunctions ||
1013 e.ident == Id.getVirtualMethods ||
1014 e.ident == Id.getOverloads)
1016 uint errors = global.errors;
1017 Expression eorig = ex;
1018 ex = ex.expressionSemantic(scx);
1019 if (errors < global.errors)
1020 error(e.loc, "`%s` cannot be resolved", eorig.toChars());
1022 if (e.ident == Id.getVirtualFunctions)
1024 // @@@DEPRECATED2.121@@@
1025 // Deprecated in 2.101 - Can be removed from 2.121
1026 deprecation(e.loc, "`traits(getVirtualFunctions)` is deprecated. Use `traits(getVirtualMethods)` instead");
1029 /* Create tuple of functions of ex
1031 auto exps = new Expressions();
1032 Dsymbol f;
1033 if (auto ve = ex.isVarExp)
1035 if (ve.var.isFuncDeclaration() || ve.var.isOverDeclaration())
1036 f = ve.var;
1037 ex = null;
1039 else if (auto dve = ex.isDotVarExp)
1041 if (dve.var.isFuncDeclaration() || dve.var.isOverDeclaration())
1042 f = dve.var;
1043 if (dve.e1.op == EXP.dotType || dve.e1.op == EXP.this_)
1044 ex = null;
1045 else
1046 ex = dve.e1;
1048 else if (auto te = ex.isTemplateExp)
1050 auto td = te.td;
1051 f = td;
1052 if (td && td.funcroot)
1053 f = td.funcroot;
1054 ex = null;
1056 else if (auto dte = ex.isDotTemplateExp)
1058 auto td = dte.td;
1059 f = td;
1060 if (td && td.funcroot)
1061 f = td.funcroot;
1062 ex = null;
1063 if (dte.e1.op != EXP.dotType && dte.e1.op != EXP.this_)
1064 ex = dte.e1;
1066 bool[string] funcTypeHash;
1068 /* Compute the function signature and insert it in the
1069 * hashtable, if not present. This is needed so that
1070 * traits(getOverlods, F3, "visit") does not count `int visit(int)`
1071 * twice in the following example:
1073 * =============================================
1074 * interface F1 { int visit(int);}
1075 * interface F2 { int visit(int); void visit(); }
1076 * interface F3 : F2, F1 {}
1077 *==============================================
1079 void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e)
1081 auto signature = fd.type.toString();
1082 //printf("%s - %s\n", fd.toChars, signature);
1083 if (signature !in funcTypeHash)
1085 funcTypeHash[signature] = true;
1086 exps.push(e);
1090 int dg(Dsymbol s)
1092 auto fd = s.isFuncDeclaration();
1093 if (!fd)
1095 if (includeTemplates)
1097 if (auto td = s.isTemplateDeclaration())
1099 // if td is part of an overload set we must take a copy
1100 // which shares the same `instances` cache but without
1101 // `overroot` and `overnext` set to avoid overload
1102 // behaviour in the result.
1103 if (td.overnext !is null)
1105 if (td.instances is null)
1107 // create an empty AA just to copy it
1108 scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
1109 auto tib = TemplateInstanceBox(ti);
1110 td.instances[tib] = null;
1111 td.instances.clear();
1113 td = td.syntaxCopy(null);
1114 import core.stdc.string : memcpy;
1115 memcpy(cast(void*) td, cast(void*) s,
1116 __traits(classInstanceSize, TemplateDeclaration));
1117 td.overroot = null;
1118 td.overnext = null;
1121 auto e = ex ? new DotTemplateExp(Loc.initial, ex, td)
1122 : new DsymbolExp(Loc.initial, td);
1123 exps.push(e);
1126 return 0;
1128 if (e.ident == Id.getVirtualFunctions && !fd.isVirtual())
1129 return 0;
1130 if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod())
1131 return 0;
1133 auto fa = new FuncAliasDeclaration(fd.ident, fd, false);
1134 fa.visibility = fd.visibility;
1136 auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false)
1137 : new DsymbolExp(Loc.initial, fa, false);
1139 // if the parent is an interface declaration
1140 // we must check for functions with the same signature
1141 // in different inherited interfaces
1142 if (sym && sym.isInterfaceDeclaration())
1143 insertInterfaceInheritedFunction(fd, e);
1144 else
1145 exps.push(e);
1146 return 0;
1149 InterfaceDeclaration ifd = null;
1150 if (sym)
1151 ifd = sym.isInterfaceDeclaration();
1152 // If the symbol passed as a parameter is an
1153 // interface that inherits other interfaces
1154 overloadApply(f, &dg);
1155 if (ifd && ifd.interfaces && f)
1157 // check the overloads of each inherited interface individually
1158 foreach (bc; ifd.interfaces)
1160 if (auto fd = bc.sym.search(e.loc, f.ident))
1161 overloadApply(fd, &dg);
1165 auto tup = new TupleExp(e.loc, exps);
1166 return tup.expressionSemantic(scx);
1168 else
1169 assert(0);
1171 if (e.ident == Id.classInstanceSize || e.ident == Id.classInstanceAlignment)
1173 if (dim != 1)
1174 return dimError(1);
1176 auto o = (*e.args)[0];
1177 auto s = getDsymbol(o);
1178 auto cd = s ? s.isClassDeclaration() : null;
1179 if (!cd)
1181 error(e.loc, "first argument is not a class");
1182 return ErrorExp.get();
1184 if (cd.sizeok != Sizeok.done)
1186 cd.size(e.loc);
1188 if (cd.sizeok != Sizeok.done)
1190 error(e.loc, "%s `%s` is forward referenced", cd.kind(), cd.toChars());
1191 return ErrorExp.get();
1194 return new IntegerExp(e.loc, e.ident == Id.classInstanceSize ? cd.structsize : cd.alignsize, Type.tsize_t);
1196 if (e.ident == Id.getAliasThis)
1198 if (dim != 1)
1199 return dimError(1);
1201 auto o = (*e.args)[0];
1202 auto s = getDsymbol(o);
1203 auto ad = s ? s.isAggregateDeclaration() : null;
1205 auto exps = new Expressions();
1206 if (ad && ad.aliasthis)
1207 exps.push(new StringExp(e.loc, ad.aliasthis.ident.toString()));
1208 Expression ex = new TupleExp(e.loc, exps);
1209 ex = ex.expressionSemantic(sc);
1210 return ex;
1212 if (e.ident == Id.getAttributes)
1214 /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
1215 * a symbol should not be folded to a constant.
1216 * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
1218 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3))
1219 return ErrorExp.get();
1221 if (dim != 1)
1222 return dimError(1);
1224 auto o = (*e.args)[0];
1225 auto po = isParameter(o);
1226 auto s = getDsymbolWithoutExpCtx(o);
1227 auto typeOfArg = isType(o);
1228 UserAttributeDeclaration udad = null;
1229 if (po)
1231 udad = po.userAttribDecl;
1233 else if (s)
1235 // @@@DEPRECATION 2.100.2
1236 if (auto fd = s.isFuncDeclaration())
1238 if (fd.overnext)
1240 deprecation(e.loc, "`__traits(getAttributes)` may only be used for individual functions, not the overload set `%s`", fd.toChars());
1241 deprecationSupplemental(e.loc, "the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from");
1245 // @@@DEPRECATION 2.100.2
1246 if (auto td = s.isTemplateDeclaration())
1248 if (td.overnext || td.overroot)
1250 deprecation(e.loc, "`__traits(getAttributes)` may only be used for individual functions, not the overload set `%s`", td.ident.toChars());
1251 deprecationSupplemental(e.loc, "the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from");
1254 if (s.isImport())
1256 s = s.isImport().mod;
1258 //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s._scope);
1259 udad = s.userAttribDecl;
1261 else if (typeOfArg)
1263 // If there is a type but no symbol, do nothing rather than erroring.
1265 else
1267 version (none)
1269 Expression x = isExpression(o);
1270 Type t = isType(o);
1271 if (x)
1272 printf("e = %s %s\n", EXPtoString(x.op).ptr, x.toChars());
1273 if (t)
1274 printf("t = %d %s\n", t.ty, t.toChars());
1276 error(e.loc, "first argument is not a symbol");
1277 return ErrorExp.get();
1280 auto exps = udad ? udad.getAttributes() : new Expressions();
1281 auto tup = new TupleExp(e.loc, exps);
1282 return tup.expressionSemantic(sc);
1284 if (e.ident == Id.getFunctionAttributes)
1286 /* Extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
1287 * https://dlang.org/spec/traits.html#getFunctionAttributes
1289 if (dim != 1)
1290 return dimError(1);
1292 FuncDeclaration fd;
1293 TypeFunction tf = toTypeFunction((*e.args)[0], fd);
1295 if (!tf)
1297 error(e.loc, "first argument is not a function");
1298 return ErrorExp.get();
1301 // https://issues.dlang.org/show_bug.cgi?id=19706
1302 // When getting the attributes of the instance of a
1303 // templated member function semantic tiargs does
1304 // not perform semantic3 on the instance.
1305 // For more information see FuncDeclaration.functionSemantic.
1306 // For getFunctionAttributes it is mandatory to do
1307 // attribute inference.
1308 if (fd && fd.parent && fd.parent.isTemplateInstance)
1310 functionSemantic3(fd);
1311 tf = fd.type.isTypeFunction();
1314 auto mods = new Expressions();
1316 void addToMods(string str)
1318 mods.push(new StringExp(Loc.initial, str));
1320 tf.modifiersApply(&addToMods);
1321 tf.attributesApply(&addToMods, TRUSTformatSystem);
1323 auto tup = new TupleExp(e.loc, mods);
1324 return tup.expressionSemantic(sc);
1326 if (e.ident == Id.isReturnOnStack)
1328 /* Extract as a boolean if function return value is on the stack
1329 * https://dlang.org/spec/traits.html#isReturnOnStack
1331 if (dim != 1)
1332 return dimError(1);
1334 RootObject o = (*e.args)[0];
1335 FuncDeclaration fd;
1336 TypeFunction tf = toTypeFunction(o, fd);
1338 if (!tf)
1340 error(e.loc, "argument to `__traits(isReturnOnStack, %s)` is not a function", o.toChars());
1341 return ErrorExp.get();
1344 bool value = target.isReturnOnStack(tf, fd && fd.needThis());
1345 return IntegerExp.createBool(value);
1347 if (e.ident == Id.getFunctionVariadicStyle)
1349 /* Accept a symbol or a type. Returns one of the following:
1350 * "none" not a variadic function
1351 * "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments`
1352 * "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg
1353 * "typesafe" void typesafe(T[] ...)
1354 * "KR" old K+R style
1356 // get symbol linkage as a string
1357 if (dim != 1)
1358 return dimError(1);
1360 LINK link;
1361 VarArg varargs;
1362 auto o = (*e.args)[0];
1364 FuncDeclaration fd;
1365 TypeFunction tf = toTypeFunction(o, fd);
1367 if (tf)
1369 link = tf.linkage;
1370 varargs = tf.parameterList.varargs;
1372 else
1374 if (!fd)
1376 error(e.loc, "argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o.toChars());
1377 return ErrorExp.get();
1379 link = fd._linkage;
1380 varargs = fd.getParameterList().varargs;
1382 string style;
1383 final switch (varargs)
1385 case VarArg.none: style = "none"; break;
1386 case VarArg.variadic: style = (link == LINK.d)
1387 ? "argptr"
1388 : "stdarg"; break;
1389 case VarArg.KRvariadic: style = "KR"; break;
1390 case VarArg.typesafe: style = "typesafe"; break;
1392 auto se = new StringExp(e.loc, style);
1393 return se.expressionSemantic(sc);
1395 if (e.ident == Id.getParameterStorageClasses)
1397 /* Accept a function symbol or a type, followed by a parameter index.
1398 * Returns a tuple of strings of the parameter's storage classes.
1400 // get symbol linkage as a string
1401 if (dim != 2)
1402 return dimError(2);
1404 auto o = (*e.args)[0];
1405 auto o1 = (*e.args)[1];
1407 ParameterList fparams;
1409 CallExp ce;
1410 if (auto exp = isExpression(o))
1411 ce = exp.isCallExp();
1413 if (ce)
1415 fparams = ce.f.getParameterList();
1417 else
1419 FuncDeclaration fd;
1420 auto tf = toTypeFunction(o, fd);
1421 if (tf)
1422 fparams = tf.parameterList;
1423 else if (fd)
1424 fparams = fd.getParameterList();
1425 else
1427 error(e.loc, "first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function or a function call",
1428 o.toChars(), o1.toChars());
1429 return ErrorExp.get();
1433 // Avoid further analysis for invalid functions leading to misleading error messages
1434 if (!fparams.parameters)
1435 return ErrorExp.get();
1437 StorageClass stc;
1439 // Set stc to storage class of the ith parameter
1440 auto ex = isExpression((*e.args)[1]);
1441 if (!ex)
1443 error(e.loc, "expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
1444 o.toChars(), o1.toChars());
1445 return ErrorExp.get();
1447 ex = ex.ctfeInterpret();
1448 auto ii = ex.toUInteger();
1449 if (ii >= fparams.length)
1451 error(e.loc, "parameter index must be in range 0..%u not %s", cast(uint)fparams.length, ex.toChars());
1452 return ErrorExp.get();
1455 uint n = cast(uint)ii;
1456 Parameter p = fparams[n];
1457 stc = p.storageClass;
1459 // This mirrors hdrgen.visit(Parameter p)
1460 if (p.type && p.type.mod & MODFlags.shared_)
1461 stc &= ~STC.shared_;
1463 auto exps = new Expressions;
1465 void push(string s)
1467 exps.push(new StringExp(e.loc, s));
1470 if (stc & STC.auto_)
1471 push("auto");
1472 if (stc & STC.return_)
1473 push("return");
1475 if (stc & STC.out_)
1476 push("out");
1477 else if (stc & STC.in_)
1478 push("in");
1479 else if (stc & STC.ref_)
1480 push("ref");
1481 else if (stc & STC.lazy_)
1482 push("lazy");
1483 else if (stc & STC.alias_)
1484 push("alias");
1486 if (stc & STC.const_)
1487 push("const");
1488 if (stc & STC.immutable_)
1489 push("immutable");
1490 if (stc & STC.wild)
1491 push("inout");
1492 if (stc & STC.shared_)
1493 push("shared");
1494 if (stc & STC.scope_ && !(stc & STC.scopeinferred))
1495 push("scope");
1497 auto tup = new TupleExp(e.loc, exps);
1498 return tup.expressionSemantic(sc);
1500 if (e.ident == Id.getLinkage)
1502 // get symbol linkage as a string
1503 if (dim != 1)
1504 return dimError(1);
1506 LINK link;
1507 auto o = (*e.args)[0];
1509 FuncDeclaration fd;
1510 TypeFunction tf = toTypeFunction(o, fd);
1512 if (tf)
1514 link = fd ? fd.toAliasFunc()._linkage : tf.linkage;
1516 else
1518 auto s = getDsymbol(o);
1519 Declaration d;
1520 AggregateDeclaration agg;
1521 if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null))
1523 error(e.loc, "argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars());
1524 return ErrorExp.get();
1527 if (d !is null)
1528 link = d._linkage;
1529 else
1531 // Resolves forward references
1532 if (agg.sizeok != Sizeok.done)
1534 agg.size(e.loc);
1535 if (agg.sizeok != Sizeok.done)
1537 error(e.loc, "%s `%s` is forward referenced", agg.kind(), agg.toChars());
1538 return ErrorExp.get();
1542 final switch (agg.classKind)
1544 case ClassKind.d:
1545 link = LINK.d;
1546 break;
1547 case ClassKind.cpp:
1548 link = LINK.cpp;
1549 break;
1550 case ClassKind.objc:
1551 link = LINK.objc;
1552 break;
1553 case ClassKind.c:
1554 link = LINK.c;
1555 break;
1559 auto linkage = linkageToChars(link);
1560 auto se = new StringExp(e.loc, linkage.toDString());
1561 return se.expressionSemantic(sc);
1563 if (e.ident == Id.allMembers ||
1564 e.ident == Id.derivedMembers)
1566 if (dim != 1)
1567 return dimError(1);
1569 auto o = (*e.args)[0];
1570 auto s = getDsymbol(o);
1571 if (!s)
1573 error(e.loc, "in expression `%s` `%s` can't have members", e.toChars(), o.toChars());
1574 errorSupplemental(e.loc, "`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", o.toChars());
1576 return ErrorExp.get();
1578 if (auto imp = s.isImport())
1580 // https://issues.dlang.org/show_bug.cgi?id=9692
1581 // https://issues.dlang.org/show_bug.cgi?id=20008
1582 if (imp.pkg)
1583 s = imp.pkg;
1586 // https://issues.dlang.org/show_bug.cgi?id=16044
1587 if (auto p = s.isPackage())
1589 if (auto pm = p.isPackageMod())
1590 s = pm;
1593 auto sds = s.isScopeDsymbol();
1594 if (!sds || sds.isTemplateDeclaration())
1596 error(e.loc, "in expression `%s` %s `%s` has no members", e.toChars(), s.kind(), s.toChars());
1597 errorSupplemental(e.loc, "`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", s.toChars());
1598 return ErrorExp.get();
1601 auto idents = new Identifiers();
1603 int pushIdentsDg(size_t n, Dsymbol sm)
1605 if (!sm)
1606 return 1;
1608 // skip local symbols, such as static foreach loop variables
1609 if (auto decl = sm.isDeclaration())
1611 if (decl.storage_class & STC.local)
1613 return 0;
1615 // skip 'this' context pointers
1616 else if (decl.isThisDeclaration())
1617 return 0;
1620 // https://issues.dlang.org/show_bug.cgi?id=20915
1621 // skip version and debug identifiers
1622 if (sm.isVersionSymbol() || sm.isDebugSymbol())
1623 return 0;
1625 //printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
1626 if (sm.ident)
1628 // https://issues.dlang.org/show_bug.cgi?id=10096
1629 // https://issues.dlang.org/show_bug.cgi?id=10100
1630 // Skip over internal members in __traits(allMembers)
1631 if ((sm.isCtorDeclaration() && sm.ident != Id.ctor) ||
1632 (sm.isDtorDeclaration() && sm.ident != Id.dtor) ||
1633 (sm.isPostBlitDeclaration() && sm.ident != Id.postblit) ||
1634 sm.isInvariantDeclaration() ||
1635 sm.isUnitTestDeclaration())
1638 return 0;
1640 if (sm.ident == Id.empty)
1642 return 0;
1644 if (sm.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177
1645 return 0;
1646 if ((!sds.isModule() && !sds.isPackage()) && sm.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057
1647 return 0;
1649 //printf("\t%s\n", sm.ident.toChars());
1651 /* Skip if already present in idents[]
1653 foreach (id; *idents)
1655 if (id == sm.ident)
1656 return 0;
1658 // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
1659 debug
1661 import core.stdc.string : strcmp;
1662 assert(strcmp(id.toChars(), sm.ident.toChars()) != 0);
1665 idents.push(sm.ident);
1667 else if (auto ed = sm.isEnumDeclaration())
1669 _foreach(null, ed.members, &pushIdentsDg);
1671 return 0;
1674 _foreach(sc, sds.members, &pushIdentsDg);
1675 auto cd = sds.isClassDeclaration();
1676 if (cd && e.ident == Id.allMembers)
1678 if (cd.semanticRun < PASS.semanticdone)
1679 cd.dsymbolSemantic(null); // https://issues.dlang.org/show_bug.cgi?id=13668
1680 // Try to resolve forward reference
1682 void pushBaseMembersDg(ClassDeclaration cd)
1684 for (size_t i = 0; i < cd.baseclasses.length; i++)
1686 auto cb = (*cd.baseclasses)[i].sym;
1687 assert(cb);
1688 _foreach(null, cb.members, &pushIdentsDg);
1689 if (cb.baseclasses.length)
1690 pushBaseMembersDg(cb);
1694 pushBaseMembersDg(cd);
1697 // Turn Identifiers into StringExps reusing the allocated array
1698 assert(Expressions.sizeof == Identifiers.sizeof);
1699 auto exps = cast(Expressions*)idents;
1700 foreach (i, id; *idents)
1702 auto se = new StringExp(e.loc, id.toString());
1703 (*exps)[i] = se;
1706 /* Making this a tuple is more flexible, as it can be statically unrolled.
1707 * To make an array literal, enclose __traits in [ ]:
1708 * [ __traits(allMembers, ...) ]
1710 Expression ex = new TupleExp(e.loc, exps);
1711 ex = ex.expressionSemantic(sc);
1712 return ex;
1714 if (e.ident == Id.compiles)
1716 /* Determine if all the objects - types, expressions, or symbols -
1717 * compile without error
1719 if (!dim)
1720 return False();
1722 foreach (o; *e.args)
1724 uint errors = global.startGagging();
1725 Scope* sc2 = sc.push();
1726 sc2.tinst = null;
1727 sc2.minst = null; // this is why code for these are not emitted to object file
1728 sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst;
1730 bool err = false;
1732 auto t = isType(o);
1733 auto ex = isExpression(o);
1734 if (t)
1736 Dsymbol s;
1737 t.resolve(e.loc, sc2, ex, t, s);
1738 if (t)
1740 t.typeSemantic(e.loc, sc2);
1741 if (t.ty == Terror)
1742 err = true;
1744 else if (s && s.errors)
1745 err = true;
1747 if (ex)
1749 ex = ex.expressionSemantic(sc2);
1750 ex = resolvePropertiesOnly(sc2, ex);
1751 ex = ex.optimize(WANTvalue);
1752 if (sc2.func && sc2.func.type.isTypeFunction())
1754 const tf = sc2.func.type.isTypeFunction();
1755 err |= tf.isnothrow && canThrow(ex, sc2.func, null);
1757 ex = checkGC(sc2, ex);
1758 if (ex.op == EXP.error)
1759 err = true;
1762 // Carefully detach the scope from the parent and throw it away as
1763 // we only need it to evaluate the expression
1764 // https://issues.dlang.org/show_bug.cgi?id=15428
1765 sc2.detach();
1767 if (global.endGagging(errors) || err)
1769 return False();
1772 return True();
1774 if (e.ident == Id.isSame)
1776 /* Determine if two symbols are the same
1778 if (dim != 2)
1779 return dimError(2);
1781 // https://issues.dlang.org/show_bug.cgi?id=20761
1782 // tiarg semantic may expand in place the list of arguments, for example:
1784 // before tiarg sema: __traits(isSame, seq!(0,0), seq!(1,1))
1785 // after : __traits(isSame, 0, 0, 1, 1)
1787 // so we split in two lists
1788 Objects ob1;
1789 ob1.push((*e.args)[0]);
1790 Objects ob2;
1791 ob2.push((*e.args)[1]);
1792 if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0))
1793 return ErrorExp.get();
1794 if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0))
1795 return ErrorExp.get();
1796 if (ob1.length != ob2.length)
1797 return False();
1798 foreach (immutable i; 0 .. ob1.length)
1799 if (!ob1[i].isSame(ob2[i], sc))
1800 return False();
1801 return True();
1803 if (e.ident == Id.getUnitTests)
1805 if (dim != 1)
1806 return dimError(1);
1808 auto o = (*e.args)[0];
1809 auto s = getDsymbolWithoutExpCtx(o);
1810 if (!s)
1812 error(e.loc, "argument `%s` to __traits(getUnitTests) must be a module or aggregate",
1813 o.toChars());
1814 return ErrorExp.get();
1816 if (auto imp = s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990
1817 s = imp.mod;
1819 auto sds = s.isScopeDsymbol();
1820 if (!sds || sds.isTemplateDeclaration())
1822 error(e.loc, "argument `%s` to __traits(getUnitTests) must be a module or aggregate, not a %s",
1823 s.toChars(), s.kind());
1824 return ErrorExp.get();
1827 auto exps = new Expressions();
1828 if (global.params.useUnitTests)
1830 bool[void*] uniqueUnitTests;
1832 void symbolDg(Dsymbol s)
1834 if (auto ad = s.isAttribDeclaration())
1836 ad.include(null).foreachDsymbol(&symbolDg);
1838 else if (auto tm = s.isTemplateMixin())
1840 tm.members.foreachDsymbol(&symbolDg);
1842 else if (auto ud = s.isUnitTestDeclaration())
1844 if (cast(void*)ud in uniqueUnitTests)
1845 return;
1847 uniqueUnitTests[cast(void*)ud] = true;
1849 auto ad = new FuncAliasDeclaration(ud.ident, ud, false);
1850 ad.visibility = ud.visibility;
1852 auto e = new DsymbolExp(Loc.initial, ad, false);
1853 exps.push(e);
1857 sds.members.foreachDsymbol(&symbolDg);
1859 auto te = new TupleExp(e.loc, exps);
1860 return te.expressionSemantic(sc);
1862 if (e.ident == Id.getVirtualIndex)
1864 if (dim != 1)
1865 return dimError(1);
1867 auto o = (*e.args)[0];
1868 auto s = getDsymbolWithoutExpCtx(o);
1870 auto fd = s ? s.isFuncDeclaration() : null;
1871 if (!fd)
1873 error(e.loc, "first argument to __traits(getVirtualIndex) must be a function");
1874 return ErrorExp.get();
1877 fd = fd.toAliasFunc(); // Necessary to support multiple overloads.
1878 return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t);
1880 if (e.ident == Id.getPointerBitmap)
1882 return pointerBitmap(e, global.errorSink);
1884 if (e.ident == Id.initSymbol)
1886 if (dim != 1)
1887 return dimError(1);
1889 auto o = (*e.args)[0];
1891 ErrorExp badArgument()
1893 error(e.loc, "struct / class type expected as argument to __traits(initSymbol) instead of `%s`", o.toChars());
1894 return ErrorExp.get();
1897 Type t = isType(o);
1899 if (!t || t.isTypeEnum())
1900 return badArgument();
1902 AggregateDeclaration ad = isAggregate(t);
1904 // Interfaces don't have an init symbol and hence cause linker errors
1905 if (!ad || ad.isInterfaceDeclaration())
1906 return badArgument();
1908 Declaration d = new SymbolDeclaration(ad.loc, ad);
1909 d.type = Type.tvoid.arrayOf().constOf();
1910 d.storage_class |= STC.rvalue;
1911 return new VarExp(e.loc, d);
1913 if (e.ident == Id.isZeroInit)
1915 if (dim != 1)
1916 return dimError(1);
1918 auto o = (*e.args)[0];
1919 Type t = isType(o);
1920 if (!t)
1922 error(e.loc, "type expected as second argument of __traits `%s` instead of `%s`",
1923 e.ident.toChars(), o.toChars());
1924 return ErrorExp.get();
1927 // https://issues.dlang.org/show_bug.cgi?id=23534
1929 // For enums, we need to get the enum initializer
1930 // (the first enum member), not the initializer of the
1931 // type of the enum members.
1932 Type tb = t.isTypeEnum ? t : t.baseElemOf();
1933 return tb.isZeroInit(e.loc) ? True() : False();
1935 if (e.ident == Id.getTargetInfo)
1937 if (dim != 1)
1938 return dimError(1);
1940 auto ex = isExpression((*e.args)[0]);
1941 StringExp se = ex ? ex.ctfeInterpret().toStringExp() : null;
1942 if (!ex || !se || se.len == 0)
1944 error(e.loc, "string expected as argument of __traits `%s` instead of `%s`", e.ident.toChars(), (*e.args)[0].toChars());
1945 return ErrorExp.get();
1947 se = se.toUTF8(sc);
1949 const slice = se.peekString();
1950 Expression r = target.getTargetInfo(slice.ptr, e.loc); // BUG: reliance on terminating 0
1951 if (!r)
1953 error(e.loc, "`getTargetInfo` key `\"%.*s\"` not supported by this implementation",
1954 cast(int)slice.length, slice.ptr);
1955 return ErrorExp.get();
1957 return r.expressionSemantic(sc);
1959 if (e.ident == Id.getLocation)
1961 if (dim != 1)
1962 return dimError(1);
1963 auto arg0 = (*e.args)[0];
1964 Dsymbol s = getDsymbolWithoutExpCtx(arg0);
1965 if (!s || !s.loc.isValid())
1967 error(e.loc, "can only get the location of a symbol, not `%s`", arg0.toChars());
1968 return ErrorExp.get();
1971 const fd = s.isFuncDeclaration();
1972 // FIXME:td.overnext is always set, even when using an index on it
1973 //const td = s.isTemplateDeclaration();
1974 if ((fd && fd.overnext) /*|| (td && td.overnext)*/)
1976 error(e.loc, "cannot get location of an overload set, " ~
1977 "use `__traits(getOverloads, ..., \"%s\"%s)[N]` " ~
1978 "to get the Nth overload",
1979 arg0.toChars(), /*td ? ", true".ptr :*/ "".ptr);
1980 return ErrorExp.get();
1983 auto exps = new Expressions(3);
1984 (*exps)[0] = new StringExp(e.loc, s.loc.filename.toDString());
1985 (*exps)[1] = new IntegerExp(e.loc, s.loc.linnum,Type.tint32);
1986 (*exps)[2] = new IntegerExp(e.loc, s.loc.charnum,Type.tint32);
1987 auto tup = new TupleExp(e.loc, exps);
1988 return tup.expressionSemantic(sc);
1990 if (e.ident == Id.getCppNamespaces)
1992 auto o = (*e.args)[0];
1993 auto s = getDsymbolWithoutExpCtx(o);
1994 auto exps = new Expressions(0);
1995 if (auto d = s.isDeclaration())
1997 if (d.inuse)
1999 .error(d.loc, "%s `%s` circular reference in `__traits(GetCppNamespaces,...)`", d.kind, d.toPrettyChars);
2000 return ErrorExp.get();
2002 d.inuse = 1;
2006 Prepend the namespaces in the linked list `ns` to `es`.
2008 Returns: true if `ns` contains an `ErrorExp`.
2010 bool prependNamespaces(Expressions* es, CPPNamespaceDeclaration ns)
2012 // Semantic processing will convert `extern(C++, "a", "b", "c")`
2013 // into `extern(C++, "a") extern(C++, "b") extern(C++, "c")`,
2014 // creating a linked list what `a`'s `cppnamespace` points to `b`,
2015 // and `b`'s points to `c`. Our entry point is `a`.
2016 for (; ns !is null; ns = ns.cppnamespace)
2018 ns.dsymbolSemantic(sc);
2020 if (ns.exp.isErrorExp())
2021 return true;
2023 auto se = ns.exp.toStringExp();
2024 // extern(C++, (emptyTuple))
2025 // struct D {}
2026 // will produce a blank ident
2027 if (!se.len)
2028 continue;
2029 es.insert(0, se);
2031 return false;
2033 for (auto p = s; !p.isModule(); p = p.toParent())
2035 p.dsymbolSemantic(sc);
2036 auto pp = p.toParent();
2037 if (pp.isTemplateInstance())
2039 if (!p.cppnamespace)
2040 continue;
2041 //if (!p.toParent().cppnamespace)
2042 // continue;
2043 auto inner = new Expressions(0);
2044 auto outer = new Expressions(0);
2045 if (prependNamespaces(inner, p.cppnamespace)) return ErrorExp.get();
2046 if (prependNamespaces(outer, pp.cppnamespace)) return ErrorExp.get();
2048 size_t i = 0;
2049 while(i < outer.length && ((*inner)[i]) == (*outer)[i])
2050 i++;
2052 foreach_reverse (ns; (*inner)[][i .. $])
2053 exps.insert(0, ns);
2054 continue;
2057 if (p.isNspace())
2058 exps.insert(0, new StringExp(p.loc, p.ident.toString()));
2060 if (prependNamespaces(exps, p.cppnamespace))
2061 return ErrorExp.get();
2063 if (auto d = s.isDeclaration())
2064 d.inuse = 0;
2065 auto tup = new TupleExp(e.loc, exps);
2066 return tup.expressionSemantic(sc);
2068 //https://issues.dlang.org/show_bug.cgi?id=22291
2069 if (e.ident == Id.parameters)
2071 //No args are valid
2072 if (e.args)
2074 char[] contents = cast(char[]) e.args.toString();
2075 contents = contents[1..$];
2076 contents[$-1] = '\0';
2077 error(e.loc, "`__traits(parameters)` cannot have arguments, but `%s` was supplied", contents.ptr);
2078 return ErrorExp.get();
2081 auto fd = sc.getEnclosingFunction();
2082 if (!fd)
2084 error(e.loc, "`__traits(parameters)` may only be used inside a function");
2085 return ErrorExp.get();
2088 auto tf = fd.type.isTypeFunction();
2089 assert(tf);
2090 auto exps = new Expressions(0);
2091 int addParameterDG(size_t idx, Parameter x)
2093 assert(x.ident);
2094 exps.push(new IdentifierExp(e.loc, x.ident));
2095 return 0;
2098 This is required since not all "parameters" actually have a name
2099 until they (tuples) are expanded e.g. an anonymous tuple parameter's
2100 contents get given names but not the tuple itself.
2102 Parameter._foreach(tf.parameterList.parameters, &addParameterDG);
2103 auto tup = new TupleExp(e.loc, exps);
2104 return tup.expressionSemantic(sc);
2107 /* Can't find the identifier. Try a spell check for a better error message
2109 traitNotFound(e);
2111 return ErrorExp.get();
2114 /// compare arguments of __traits(isSame)
2115 private bool isSame(RootObject o1, RootObject o2, Scope* sc)
2117 static FuncLiteralDeclaration isLambda(RootObject oarg)
2119 if (auto t = isDsymbol(oarg))
2121 if (auto td = t.isTemplateDeclaration())
2123 if (td.members && td.members.length == 1)
2125 if (auto fd = (*td.members)[0].isFuncLiteralDeclaration())
2126 return fd;
2130 else if (auto ea = isExpression(oarg))
2132 if (ea.op == EXP.function_)
2134 if (auto fe = ea.isFuncExp())
2135 return fe.fd;
2138 return null;
2141 auto l1 = isLambda(o1);
2142 auto l2 = isLambda(o2);
2144 if (l1 && l2)
2146 import dmd.lambdacomp : isSameFuncLiteral;
2147 if (isSameFuncLiteral(l1, l2, sc))
2148 return true;
2151 // https://issues.dlang.org/show_bug.cgi?id=12001, allow isSame, <BasicType>, <BasicType>
2152 Type t1 = isType(o1);
2153 Type t2 = isType(o2);
2154 if (t1 && t2 && t1.equals(t2))
2155 return true;
2157 auto s1 = getDsymbol(o1);
2158 auto s2 = getDsymbol(o2);
2159 //printf("isSame: %s, %s\n", o1.toChars(), o2.toChars());
2160 version (none)
2162 printf("o1: %p\n", o1);
2163 printf("o2: %p\n", o2);
2164 if (!s1)
2166 if (auto ea = isExpression(o1))
2167 printf("%s\n", ea.toChars());
2168 if (auto ta = isType(o1))
2169 printf("%s\n", ta.toChars());
2170 return false;
2172 else
2173 printf("%s %s\n", s1.kind(), s1.toChars());
2175 if (!s1 && !s2)
2177 auto ea1 = isExpression(o1);
2178 auto ea2 = isExpression(o2);
2179 if (ea1 && ea2)
2181 if (ea1.equals(ea2))
2182 return true;
2185 if (!s1 || !s2)
2186 return false;
2188 s1 = s1.toAlias();
2189 s2 = s2.toAlias();
2191 if (auto fa1 = s1.isFuncAliasDeclaration())
2192 s1 = fa1.toAliasFunc();
2193 if (auto fa2 = s2.isFuncAliasDeclaration())
2194 s2 = fa2.toAliasFunc();
2196 // https://issues.dlang.org/show_bug.cgi?id=11259
2197 // compare import symbol to a package symbol
2198 static bool cmp(Dsymbol s1, Dsymbol s2)
2200 auto imp = s1.isImport();
2201 return imp && imp.pkg && imp.pkg == s2.isPackage();
2204 if (cmp(s1,s2) || cmp(s2,s1))
2205 return true;
2207 if (s1 == s2)
2208 return true;
2210 // https://issues.dlang.org/show_bug.cgi?id=18771
2211 // OverloadSets are equal if they contain the same functions
2212 auto overSet1 = s1.isOverloadSet();
2213 if (!overSet1)
2214 return false;
2216 auto overSet2 = s2.isOverloadSet();
2217 if (!overSet2)
2218 return false;
2220 if (overSet1.a.length != overSet2.a.length)
2221 return false;
2223 // OverloadSets contain array of Dsymbols => O(n*n)
2224 // to compare for equality as the order of overloads
2225 // might not be the same
2226 Lnext:
2227 foreach(overload1; overSet1.a)
2229 foreach(overload2; overSet2.a)
2231 if (overload1 == overload2)
2232 continue Lnext;
2234 return false;
2236 return true;
2240 /***********************************
2241 * A trait was not found. Give a decent error message
2242 * by trying a spell check.
2243 * Params:
2244 * e = the offending trait
2246 private void traitNotFound(TraitsExp e)
2248 __gshared const StringTable!bool traitsStringTable;
2249 __gshared bool initialized;
2251 if (!initialized)
2253 initialized = true; // lazy initialization
2255 // All possible traits
2256 __gshared Identifier*[59] idents =
2258 &Id.allMembers,
2259 &Id.child,
2260 &Id.classInstanceAlignment,
2261 &Id.classInstanceSize,
2262 &Id.compiles,
2263 &Id.derivedMembers,
2264 &Id.fullyQualifiedName,
2265 &Id.getAliasThis,
2266 &Id.getAttributes,
2267 &Id.getFunctionAttributes,
2268 &Id.getFunctionVariadicStyle,
2269 &Id.getLinkage,
2270 &Id.getLocation,
2271 &Id.getMember,
2272 &Id.getOverloads,
2273 &Id.getParameterStorageClasses,
2274 &Id.getPointerBitmap,
2275 &Id.getProtection,
2276 &Id.getTargetInfo,
2277 &Id.getUnitTests,
2278 &Id.getVirtualFunctions,
2279 &Id.getVirtualIndex,
2280 &Id.getVirtualMethods,
2281 &Id.getVisibility,
2282 &Id.hasCopyConstructor,
2283 &Id.hasMember,
2284 &Id.hasPostblit,
2285 &Id.identifier,
2286 &Id.isAbstractClass,
2287 &Id.isAbstractFunction,
2288 &Id.isArithmetic,
2289 &Id.isAssociativeArray,
2290 &Id.isCopyable,
2291 &Id.isDeprecated,
2292 &Id.isDisabled,
2293 &Id.isFinalClass,
2294 &Id.isFinalFunction,
2295 &Id.isFloating,
2296 &Id.isFuture,
2297 &Id.isIntegral,
2298 &Id.isLazy,
2299 &Id.isModule,
2300 &Id.isNested,
2301 &Id.isOut,
2302 &Id.isOverrideFunction,
2303 &Id.isPackage,
2304 &Id.isPOD,
2305 &Id.isRef,
2306 &Id.isReturnOnStack,
2307 &Id.isSame,
2308 &Id.isScalar,
2309 &Id.isStaticArray,
2310 &Id.isStaticFunction,
2311 &Id.isUnsigned,
2312 &Id.isVirtualFunction,
2313 &Id.isVirtualMethod,
2314 &Id.isZeroInit,
2315 &Id.parameters,
2316 &Id.parent,
2319 StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable;
2320 stringTable._init(idents.length);
2322 foreach (id; idents)
2324 auto sv = stringTable.insert((*id).toString(), true);
2325 assert(sv);
2329 static const(char)[] trait_search_fp(const(char)[] seed, out int cost)
2331 //printf("trait_search_fp('%s')\n", seed);
2332 if (!seed.length)
2333 return null;
2334 cost = 0; // all the same cost
2335 const sv = traitsStringTable.lookup(seed);
2336 return sv ? sv.toString() : null;
2339 if (auto sub = speller!trait_search_fp(e.ident.toString()))
2340 error(e.loc, "unrecognized trait `%s`, did you mean `%.*s`?", e.ident.toChars(), cast(int) sub.length, sub.ptr);
2341 else
2342 error(e.loc, "unrecognized trait `%s`", e.ident.toChars());