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
16 import core
.stdc
.stdio
;
19 import dmd
.arraytypes
;
20 import dmd
.astcodegen
;
25 import dmd
.declaration
;
27 import dmd
.dinterpret
;
32 import dmd
.dsymbolsem
;
36 import dmd
.expression
;
37 import dmd
.expressionsem
;
43 import dmd
.identifier
;
49 import dmd
.root
.array
;
50 import dmd
.root
.speller
;
51 import dmd
.root
.stringtable
;
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.
71 * struct S { void f() {} }
73 * pragma(msg, __traits(isNested, s.f));
74 * // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`.
77 * This is used for that common `__traits` behavior.
80 * oarg object to get the symbol for
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())
90 if (auto dte
= e
.isDotTemplateExp())
93 return getDsymbol(oarg
);
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
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
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
)
114 if (sz
== SIZE_INVALID
)
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());
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
);
133 bool error
; // sticky error indicator
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();
150 void visitError(TypeError t
)
155 void visitBasic(TypeBasic t
)
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
)
171 ulong dim
= t
.dim
.toInteger();
172 for (ulong i
= 0; i
< dim
; i
++)
174 offset
= arrayoff
+ i
* nextsize
;
180 void visitDArray(TypeDArray t
)
182 setpointer(offset
+ sz_size_t
);
185 // dynamic array is {length,ptr}
186 void visitAArray(TypeAArray t
)
191 void visitPointer(TypePointer t
)
193 if (t
.nextOf().ty
!= Tfunction
) // don't mark function pointers
197 void visitReference(TypeReference t
)
202 void visitClass(TypeClass t
)
207 void visitFunction(TypeFunction t
)
211 void visitDelegate(TypeDelegate t
)
216 void visitEnum(TypeEnum t
)
221 void visitTuple(TypeTuple 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
)
249 void visitDefaultCase(Type t
)
251 //printf("ty = %d\n", t.ty);
255 mixin VisitType
!void visit
;
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
267 visitTopLevelClass(t
.sym
.baseClass
.type
.isTypeClass());
268 foreach (v
; t
.sym
.fields
)
270 offset
= classoff
+ v
.offset
;
276 visitTopLevelClass(tcx
);
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
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]);
304 eSink
.error(e
.loc
, "`%s` is not a type", (*e
.args
)[0].toChars());
305 return ErrorExp
.get();
309 const ulong sz
= getTypePointerBitmap(e
.loc
, t
, data
, eSink
);
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
);
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
338 if (e
.ident
== Id
.isDeprecated
)
339 sc
.stc |
= STC
.deprecated_
;
340 if (!TemplateInstance
.semanticTiargs(e
.loc
, sc
, e
.args
, 1))
343 return ErrorExp
.get();
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);
366 * Gets the function type from a given AST node
367 * if the node is a function of some sort.
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`
372 * a type node if `o` is a declaration of
373 * a delegate, function, function-pointer or a variable of the former.
376 static TypeFunction
toTypeFunction(RootObject o
, out FuncDeclaration fdp
)
379 if (auto s
= getDsymbolWithoutExpCtx(o
))
381 if (auto fd
= s
.isFuncDeclaration())
386 else if (auto vd
= s
.isVarDeclaration())
396 if (auto tf
= t
.isFunction_Delegate_PtrToFunction())
403 IntegerExp
isX(T
)(bool delegate(T
) fp
)
409 static if (is(T
== Type
))
412 static if (is(T
: Dsymbol
))
414 auto s
= getDsymbolWithoutExpCtx(o
);
418 static if (is(T
== Dsymbol
))
420 static if (is(T
== Declaration
))
421 auto y
= s
.isDeclaration();
422 static if (is(T
== FuncDeclaration
))
423 auto y
= s
.isFuncDeclaration();
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))
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
)
499 if (!s
.toAlias().isOverloadable())
501 return overloadApply(s
,
502 sm
=> sm
.isTemplateDeclaration() !is null) != 0;
505 if (e
.ident
== Id
.isPOD
)
510 auto o
= (*e
.args
)[0];
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();
527 if (e
.ident
== Id
.hasCopyConstructor || e
.ident
== Id
.hasPostblit
)
532 auto o
= (*e
.args
)[0];
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());
550 if (e
.ident
== Id
.isCopyable
)
555 auto o
= (*e
.args
)[0];
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
)
579 auto o
= (*e
.args
)[0];
580 auto s
= getDsymbolWithoutExpCtx(o
);
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
)
601 return isDeclX(f
=> f
.isDisabled());
603 if (e
.ident
== Id
.isAbstractFunction
)
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");
619 return isFuncX(f
=> f
.isVirtual());
621 if (e
.ident
== Id
.isVirtualMethod
)
626 return isFuncX(f
=> f
.isVirtualMethod());
628 if (e
.ident
== Id
.isFinalFunction
)
633 return isFuncX(f
=> f
.isFinalFunc());
635 if (e
.ident
== Id
.isOverrideFunction
)
640 return isFuncX(f
=> f
.isOverride());
642 if (e
.ident
== Id
.isStaticFunction
)
647 return isFuncX(f
=> !f
.needThis() && !f
.isNested());
649 if (e
.ident
== Id
.isModule
)
654 return isPkgX(p
=> p
.isModule() || p
.isPackageMod());
656 if (e
.ident
== Id
.isPackage
)
661 return isPkgX(p
=> p
.isModule() is null);
663 if (e
.ident
== Id
.isRef
)
668 return isDeclX(d
=> d
.isRef());
670 if (e
.ident
== Id
.isOut
)
675 return isDeclX(d
=> d
.isOut());
677 if (e
.ident
== Id
.isLazy
)
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();
696 auto o
= (*e
.args
)[0];
698 if (auto po
= isParameter(o
))
702 error(e
.loc
, "argument `%s` has no identifier", po
.type
.toChars());
703 return ErrorExp
.get();
709 Dsymbol s
= getDsymbolWithoutExpCtx(o
);
712 error(e
.loc
, "argument `%s` has no identifier", o
.toChars());
713 return ErrorExp
.get();
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
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);
731 return ErrorExp
.get();
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();
749 error(e
.loc
, "argument `%s` has no identifier", o
.toChars());
750 return ErrorExp
.get();
753 auto se
= new StringExp(e
.loc
, fqn
);
754 return se
.expressionSemantic(sc
);
757 if (e
.ident
== Id
.getProtection || e
.ident
== Id
.getVisibility
)
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);
767 return ErrorExp
.get();
769 auto o
= (*e
.args
)[0];
770 auto s
= getDsymbolWithoutExpCtx(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)
782 auto se
= new StringExp(e
.loc
, protName
);
783 return se
.expressionSemantic(sc
);
785 if (e
.ident
== Id
.parent
)
790 auto o
= (*e
.args
)[0];
791 auto s
= getDsymbolWithoutExpCtx(o
);
794 // https://issues.dlang.org/show_bug.cgi?id=12496
798 // class C(uint value) { }
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
;
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
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
);
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
)
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())
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
);
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
));
883 ex
= ex
.expressionSemantic(sc
);
886 if (e
.ident
== Id
.toType
)
891 auto ex
= isExpression((*e
.args
)[0]);
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)");
902 return ErrorExp
.get();
904 Type t
= decoToType(se
.toUTF8(sc
).peekString());
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
))
921 auto o
= (*e
.args
)[0];
922 auto ex
= isExpression((*e
.args
)[1]);
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();
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
))
969 // https://issues.dlang.org/show_bug.cgi?id=23951
970 if (auto decl
= sym
.isDeclaration())
972 ex
= typeDotIdExp(e
.loc
, decl
.type
, id
);
977 if (auto t
= isType(o
))
978 ex
= typeDotIdExp(e
.loc
, t
, id
);
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
);
988 error(e
.loc
, "invalid first argument");
989 return ErrorExp
.get();
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
1009 ex
= ex
.expressionSemantic(scx
);
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();
1033 if (auto ve
= ex
.isVarExp
)
1035 if (ve
.var
.isFuncDeclaration() || ve
.var
.isOverDeclaration())
1039 else if (auto dve
= ex
.isDotVarExp
)
1041 if (dve
.var
.isFuncDeclaration() || dve
.var
.isOverDeclaration())
1043 if (dve
.e1
.op
== EXP
.dotType || dve
.e1
.op
== EXP
.this_
)
1048 else if (auto te
= ex
.isTemplateExp
)
1052 if (td
&& td
.funcroot
)
1056 else if (auto dte
= ex
.isDotTemplateExp
)
1060 if (td
&& td
.funcroot
)
1063 if (dte
.e1
.op
!= EXP
.dotType
&& dte
.e1
.op
!= EXP
.this_
)
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;
1092 auto fd
= s
.isFuncDeclaration();
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
));
1121 auto e
= ex ?
new DotTemplateExp(Loc
.initial
, ex
, td
)
1122 : new DsymbolExp(Loc
.initial
, td
);
1128 if (e
.ident
== Id
.getVirtualFunctions
&& !fd
.isVirtual())
1130 if (e
.ident
== Id
.getVirtualMethods
&& !fd
.isVirtualMethod())
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
);
1149 InterfaceDeclaration ifd
= null;
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
);
1171 if (e
.ident
== Id
.classInstanceSize || e
.ident
== Id
.classInstanceAlignment
)
1176 auto o
= (*e
.args
)[0];
1177 auto s
= getDsymbol(o
);
1178 auto cd
= s ? s
.isClassDeclaration() : null;
1181 error(e
.loc
, "first argument is not a class");
1182 return ErrorExp
.get();
1184 if (cd
.sizeok
!= Sizeok
.done
)
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
)
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
);
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();
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;
1231 udad
= po
.userAttribDecl
;
1235 // @@@DEPRECATION 2.100.2
1236 if (auto fd
= s
.isFuncDeclaration())
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");
1256 s
= s
.isImport().mod
;
1258 //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s._scope);
1259 udad
= s
.userAttribDecl
;
1263 // If there is a type but no symbol, do nothing rather than erroring.
1269 Expression x
= isExpression(o
);
1272 printf("e = %s %s\n", EXPtoString(x
.op
).ptr
, x
.toChars());
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
1293 TypeFunction tf
= toTypeFunction((*e
.args
)[0], fd
);
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
1334 RootObject o
= (*e
.args
)[0];
1336 TypeFunction tf
= toTypeFunction(o
, fd
);
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
1362 auto o
= (*e
.args
)[0];
1365 TypeFunction tf
= toTypeFunction(o
, fd
);
1370 varargs
= tf
.parameterList
.varargs
;
1376 error(e
.loc
, "argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o
.toChars());
1377 return ErrorExp
.get();
1380 varargs
= fd
.getParameterList().varargs
;
1383 final switch (varargs
)
1385 case VarArg
.none
: style
= "none"; break;
1386 case VarArg
.variadic
: style
= (link
== LINK
.d
)
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
1404 auto o
= (*e
.args
)[0];
1405 auto o1
= (*e
.args
)[1];
1407 ParameterList fparams
;
1410 if (auto exp
= isExpression(o
))
1411 ce
= exp
.isCallExp();
1415 fparams
= ce
.f
.getParameterList();
1420 auto tf
= toTypeFunction(o
, fd
);
1422 fparams
= tf
.parameterList
;
1424 fparams
= fd
.getParameterList();
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();
1439 // Set stc to storage class of the ith parameter
1440 auto ex
= isExpression((*e
.args
)[1]);
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
;
1467 exps
.push(new StringExp(e
.loc
, s
));
1470 if (stc & STC
.auto_
)
1472 if (stc & STC
.return_
)
1477 else if (stc & STC
.in_
)
1479 else if (stc & STC
.ref_
)
1481 else if (stc & STC
.lazy_
)
1483 else if (stc & STC
.alias_
)
1486 if (stc & STC
.const_
)
1488 if (stc & STC
.immutable_
)
1492 if (stc & STC
.shared_
)
1494 if (stc & STC
.scope_
&& !(stc & STC
.scopeinferred
))
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
1507 auto o
= (*e
.args
)[0];
1510 TypeFunction tf
= toTypeFunction(o
, fd
);
1514 link
= fd ? fd
.toAliasFunc()._linkage
: tf
.linkage
;
1518 auto s
= getDsymbol(o
);
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();
1531 // Resolves forward references
1532 if (agg
.sizeok
!= Sizeok
.done
)
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
)
1550 case ClassKind
.objc
:
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
)
1569 auto o
= (*e
.args
)[0];
1570 auto s
= getDsymbol(o
);
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
1586 // https://issues.dlang.org/show_bug.cgi?id=16044
1587 if (auto p
= s
.isPackage())
1589 if (auto pm
= p
.isPackageMod())
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
)
1608 // skip local symbols, such as static foreach loop variables
1609 if (auto decl
= sm
.isDeclaration())
1611 if (decl
.storage_class
& STC
.local
)
1615 // skip 'this' context pointers
1616 else if (decl
.isThisDeclaration())
1620 // https://issues.dlang.org/show_bug.cgi?id=20915
1621 // skip version and debug identifiers
1622 if (sm
.isVersionSymbol() || sm
.isDebugSymbol())
1625 //printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
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())
1640 if (sm
.ident
== Id
.empty
)
1644 if (sm
.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177
1646 if ((!sds
.isModule() && !sds
.isPackage()) && sm
.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057
1649 //printf("\t%s\n", sm.ident.toChars());
1651 /* Skip if already present in idents[]
1653 foreach (id
; *idents
)
1658 // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
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
);
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
;
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());
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
);
1714 if (e
.ident
== Id
.compiles
)
1716 /* Determine if all the objects - types, expressions, or symbols -
1717 * compile without error
1722 foreach (o
; *e
.args
)
1724 uint errors
= global
.startGagging();
1725 Scope
* sc2
= sc
.push();
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
;
1733 auto ex
= isExpression(o
);
1737 t
.resolve(e
.loc
, sc2
, ex
, t
, s
);
1740 t
.typeSemantic(e
.loc
, sc2
);
1744 else if (s
&& s
.errors
)
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
)
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
1767 if (global
.endGagging(errors
) || err
)
1774 if (e
.ident
== Id
.isSame
)
1776 /* Determine if two symbols are the same
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
1789 ob1
.push((*e
.args
)[0]);
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
)
1798 foreach (immutable i
; 0 .. ob1
.length
)
1799 if (!ob1
[i
].isSame(ob2
[i
], sc
))
1803 if (e
.ident
== Id
.getUnitTests
)
1808 auto o
= (*e
.args
)[0];
1809 auto s
= getDsymbolWithoutExpCtx(o
);
1812 error(e
.loc
, "argument `%s` to __traits(getUnitTests) must be a module or aggregate",
1814 return ErrorExp
.get();
1816 if (auto imp
= s
.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990
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
)
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);
1857 sds
.members
.foreachDsymbol(&symbolDg
);
1859 auto te
= new TupleExp(e
.loc
, exps
);
1860 return te
.expressionSemantic(sc
);
1862 if (e
.ident
== Id
.getVirtualIndex
)
1867 auto o
= (*e
.args
)[0];
1868 auto s
= getDsymbolWithoutExpCtx(o
);
1870 auto fd
= s ? s
.isFuncDeclaration() : null;
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
)
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();
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
)
1918 auto o
= (*e
.args
)[0];
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
)
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();
1949 const slice
= se
.peekString();
1950 Expression r
= target
.getTargetInfo(slice
.ptr
, e
.loc
); // BUG: reliance on terminating 0
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
)
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())
1999 .error(d
.loc
, "%s `%s` circular reference in `__traits(GetCppNamespaces,...)`", d
.kind
, d
.toPrettyChars
);
2000 return ErrorExp
.get();
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())
2023 auto se
= ns
.exp
.toStringExp();
2024 // extern(C++, (emptyTuple))
2026 // will produce a blank ident
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
)
2041 //if (!p.toParent().cppnamespace)
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();
2049 while(i
< outer
.length
&& ((*inner
)[i
]) == (*outer
)[i
])
2052 foreach_reverse (ns
; (*inner
)[][i
.. $])
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())
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
)
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();
2084 error(e
.loc
, "`__traits(parameters)` may only be used inside a function");
2085 return ErrorExp
.get();
2088 auto tf
= fd
.type
.isTypeFunction();
2090 auto exps
= new Expressions(0);
2091 int addParameterDG(size_t idx
, Parameter x
)
2094 exps
.push(new IdentifierExp(e
.loc
, x
.ident
));
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
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())
2130 else if (auto ea
= isExpression(oarg
))
2132 if (ea
.op
== EXP
.function_
)
2134 if (auto fe
= ea
.isFuncExp())
2141 auto l1
= isLambda(o1
);
2142 auto l2
= isLambda(o2
);
2146 import dmd
.lambdacomp
: isSameFuncLiteral
;
2147 if (isSameFuncLiteral(l1
, l2
, sc
))
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
))
2157 auto s1
= getDsymbol(o1
);
2158 auto s2
= getDsymbol(o2
);
2159 //printf("isSame: %s, %s\n", o1.toChars(), o2.toChars());
2162 printf("o1: %p\n", o1
);
2163 printf("o2: %p\n", o2
);
2166 if (auto ea
= isExpression(o1
))
2167 printf("%s\n", ea
.toChars());
2168 if (auto ta
= isType(o1
))
2169 printf("%s\n", ta
.toChars());
2173 printf("%s %s\n", s1
.kind(), s1
.toChars());
2177 auto ea1
= isExpression(o1
);
2178 auto ea2
= isExpression(o2
);
2181 if (ea1
.equals(ea2
))
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
))
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();
2216 auto overSet2
= s2
.isOverloadSet();
2220 if (overSet1
.a
.length
!= overSet2
.a
.length
)
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
2227 foreach(overload1
; overSet1
.a
)
2229 foreach(overload2
; overSet2
.a
)
2231 if (overload1
== overload2
)
2240 /***********************************
2241 * A trait was not found. Give a decent error message
2242 * by trying a spell check.
2244 * e = the offending trait
2246 private void traitNotFound(TraitsExp e
)
2248 __gshared
const StringTable
!bool traitsStringTable
;
2249 __gshared
bool initialized
;
2253 initialized
= true; // lazy initialization
2255 // All possible traits
2256 __gshared Identifier
*[59] idents
=
2260 &Id
.classInstanceAlignment
,
2261 &Id
.classInstanceSize
,
2264 &Id
.fullyQualifiedName
,
2267 &Id
.getFunctionAttributes
,
2268 &Id
.getFunctionVariadicStyle
,
2273 &Id
.getParameterStorageClasses
,
2274 &Id
.getPointerBitmap
,
2278 &Id
.getVirtualFunctions
,
2279 &Id
.getVirtualIndex
,
2280 &Id
.getVirtualMethods
,
2282 &Id
.hasCopyConstructor
,
2286 &Id
.isAbstractClass
,
2287 &Id
.isAbstractFunction
,
2289 &Id
.isAssociativeArray
,
2294 &Id
.isFinalFunction
,
2302 &Id
.isOverrideFunction
,
2306 &Id
.isReturnOnStack
,
2310 &Id
.isStaticFunction
,
2312 &Id
.isVirtualFunction
,
2313 &Id
.isVirtualMethod
,
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);
2329 static const(char)[] trait_search_fp(const(char)[] seed
, out int cost
)
2331 //printf("trait_search_fp('%s')\n", seed);
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
);
2342 error(e
.loc
, "unrecognized trait `%s`", e
.ident
.toChars());