1 /// Original Phobos drop-in replacement traits, superseded by the new Tern traits system.
2 module tern.legacy.traits;
3 
4 public import std.traits : fullyQualifiedName, mangledName, moduleName, packageName,
5     isFunction, arity, functionAttributes, hasFunctionAttributes, functionLinkage, FunctionTypeOf, isSafe, isUnsafe,
6     isFinal, ParameterDefaults, SetFunctionAttributes, FunctionAttribute, variadicFunctionStyle, EnumMembers,
7     hasAliasing, hasElaborateAssign, hasElaborateCopyConstructor, hasElaborateDestructor, hasElaborateMove, hasIndirections,
8     hasMember, hasStaticMember, hasNested, hasUnsharedAliasing, isInnerClass, isNested, TemplateArgsOf, TemplateOf,
9     CommonType, AllImplicitConversionTargets, ImplicitConversionTargets, CopyTypeQualifiers, CopyConstness, isAssignable,
10     isCovariantWith, isImplicitlyConvertible, isQualifierConvertible, InoutOf, ConstOf, SharedOf, SharedInoutOf, SharedConstOf,
11     SharedConstInoutOf, ImmutableOf, QualifierOf, allSameType, isType, isAggregateType, isArray, isAssociativeArray, isAutodecodableString,
12     isBasicType, isBoolean, isBuiltinType, isCopyable, isDynamicArray, isEqualityComparable, isFloatingPoint, isIntegral, isNarrowString, 
13     isConvertibleToString, isNumeric, isOrderingComparable, isPointer, isScalarType, isSigned, isSIMDVector, isSomeChar, isSomeString,
14     isStaticArray, isUnsigned, isAbstractClass, isAbstractFunction, isDelegate, isExpressions, isFinalClass, isFinalFunction, isFunctionPointer,
15     isInstanceOf, isSomeFunction, isTypeTuple, Unconst, Unshared, Unqual, Signed, Unsigned, ValueType, Promoted, Select, select,
16     hasUDA, getUDAs, getSymbolsByUDA;
17 import std.traits;
18 import std.meta;
19 import std.string;
20 
21 /// Gets an alias to the package in which `A` is defined, undefined behavior for any alias that does not have a package (any intrinsic type.)
22 public alias getPackage(alias A) = mixin(fullyQualifiedName!A.indexOf(".") == -1 ? fullyQualifiedName!A : fullyQualifiedName!A[0..fullyQualifiedName!A.indexOf(".")]);
23 /// True if `T` is an indirection.
24 public enum isReferenceType(T) = is(T == class) || is(T == interface) || isPointer!T || isDynamicArray!T || isAssociativeArray!T;
25 /// True if `T` is not an indirection.
26 public enum isValueType(T) = is(T == struct) || is(T == enum) || is(T == union) || isBuiltinType!T || hasModifiers!T;
27 /// True if `A` is a template.
28 public enum isTemplate(alias A) = __traits(isTemplate, A);
29 /// True if `A` is a module.
30 public enum isModule(alias A) = __traits(isModule, A);
31 /// True if `A` is a package.
32 public enum isPackage(alias A) = __traits(isPackage, A);
33 /// True if `A` is a field.
34 public enum isField(alias A) = !isType!A && !isFunction!A && !isTemplate!A && !isModule!A && !isPackage!A;
35 /// True if `A` is a local.
36 public enum isLocal(alias A) = !isManifest!A && !__traits(compiles, { enum _ = A; }) && __traits(compiles, { auto _ = A; });
37 /// True if `T` is a basic type, built-in type, or array.
38 public enum isBuiltinType(T) = isBasicType!T || isBuiltinType!T || isArray!T;
39 /// True if `A` has any parents.
40 public enum hasParents(alias A) = (!isType!A || !isBuiltinType!A) && !isPackage!A;
41 /// True if `T` is an enum, array, or pointer.
42 public enum hasModifiers(T) = isArray!T || isPointer!T || !isAggregateType!T;
43 /// True if `A` has any children.
44 public enum hasChildren(alias A) = isModule!A || isPackage!A || (!isType!A || (!isBuiltinType!A && !hasModifiers!A));
45 /// True if `T` has any instance constructor ("__ctor").
46 public enum hasConstructor(alias A) = hasMember!(T, "__ctor");
47 /// True if `F` is a constructor;
48 public enum isConstructor(alias F) = isFunction!F && (__traits(identifier, F).startsWith("__ctor") || __traits(identifier, F).startsWith("_staticCtor"));
49 /// True if `F` is a destructor.
50 public enum isDestructor(alias F) = isFunction!F && (__traits(identifier, F).startsWith("__dtor") || __traits(identifier, F).startsWith("__xdtor") || __traits(identifier, F).startsWith("_staticDtor"));
51 /// True if `A` is a static field.
52 public enum isStatic(alias A) = !isManifest!A && ((isField!A && __traits(compiles, { auto _ = __traits(getMember, __traits(parent, A), __traits(identifier, A)); })) || (isLocal!A && __traits(compiles, { static auto _() { return A; } })));
53 /// True if `A` is an enum field or local.
54 public enum isManifest(alias A) = __traits(compiles, { enum _ = __traits(getMember, __traits(parent, A), __traits(identifier, A)); });
55 /// True if `A` is an implementation defined alias (ie: __ctor, std, rt, etc.)
56 public enum isDImplDefined(alias A) =
57 {
58     static if ((isModule!A || (isType!A && !isBuiltinType!A)) && (getPackage!A.stringof == "package std" || getPackage!A.stringof == "package rt" || getPackage!A.stringof == "package core"))
59         return true;
60     else static if (isPackage!A && (A.stringof == "package std" || A.stringof == "package rt" || A.stringof == "package core"))
61         return true;
62     else static if (isType!A && isBuiltinType!A)
63         return true;
64     else static if (isFunction!A && 
65     // Not exactly accurate but good enough
66         (__traits(identifier, A).startsWith("_d_") || __traits(identifier, A).startsWith("rt_") || 
67         __traits(identifier, A).startsWith("__") || __traits(identifier, A).startsWith("op")))
68         return true;
69     else static if (isConstructor!A || isDestructor!A || (isFunction!F && (__traits(identifier, F).startsWith("toHash") || __traits(identifier, F).startsWith("toString"))))
70         return true;
71     else
72         return false;
73 }();
74 /// True if `A` is not D implementation defined.
75 public enum isOrganic(alias A) = !isDImplDefined!A;
76 /// True if `A` is able to be indexed.
77 public enum isIndexable(T) = isDynamicArray!T || isStaticArray!T || __traits(compiles, { T t; auto _ = t[0]; });
78 /// True if `A` is able to be iterated upon forwards.
79 public enum isForward(T) = isDynamicArray!T || isStaticArray!T || __traits(compiles, { T t; foreach (u; t) { } });
80 /// True if `A` is able to be iterated upon forwards.
81 public enum isBackward(T) = isDynamicArray!T || isStaticArray!T || __traits(compiles, { T t; foreach_reverse (u; t) { } });
82 /// True if `B` is an element type of `A` (assignable as element.)
83 public enum isElement(A, B) = isAssignable!(B, ElementType!A);
84 /// True if `B` is able to be used as a range the same as `A`.
85 public enum isSimRange(A, B) = isAssignable!(ElementType!B, ElementType!A);
86 /// True if `F` is a lambda.
87 public enum isLambda(alias F) = __traits(identifier, F).startsWith("__lambda");
88 /// True if `F` is a dynamic lambda (templated, ie: `x => x + 1`)
89 public enum isTemplatedCallable(alias F) = std.traits.isCallable!(DefaultInstance!F);
90 /// True if `F` is a dynamic lambda (templated, ie: `x => x + 1`)
91 public enum isDynamicLambda(alias F) = isLambda!F && is(typeof(F) == void);
92 /// True if `F` is a function, lambda, or otherwise may be called using `(...)`
93 public enum isCallable(alias F) = std.traits.isCallable!F || isLambda!F || isTemplatedCallable!F;
94 /// True if `F` is a property of any kind.
95 public enum isProperty(alias F) = hasUDA!(F, property);
96 /// True if `F` is a pure callable.
97 public enum isPure(alias F) = isCallable!F && hasFunctionAttributes!(F, "pure");
98 /// True if `B` implements `A`.
99 public enum isImplement(A, B) = staticIndexOf!(A, Implements!B) != -1;
100 /// True if `A` is not mutable (const, immutable, enum, etc.).
101 public enum isMutable(alias A) =
102 {
103     static if (isType!A)
104         return std.traits.isMutable!A;
105     else static if (isField!A)
106         return std.traits.isMutable!(typeof(A)) || !isManifest!A;
107     else
108         return false;
109 }();
110 
111 /// Gets the alignment of `T` dynamically, returning the actual instance alignment.
112 public enum alignof(T) =
113 {
114     static if (is(T == class))
115         return __traits(classInstanceAlignment, T);
116     else
117         return T.alignof;
118 }();
119 
120 /// Gets the size of `T` dynamically, returning the actual instance size.
121 public enum sizeof(T) =
122 {
123     static if (is(T == class))
124         return __traits(classInstanceSize, T);
125     else
126         return T.sizeof;
127 }();
128 
129 /// Gets the type of member `MEMBER` in `A`  
130 /// This will return a function alias if `MEMBER` refers to a function, and do god knows what if `MEMBER` is a package or module.
131 public template TypeOf(alias A, string MEMBER)
132 {
133     static if (isType!(__traits(getMember, A, MEMBER)) || isTemplate!(__traits(getMember, A, MEMBER)) || isFunction!(__traits(getMember, A, MEMBER)))
134         alias TypeOf = __traits(getMember, A, MEMBER);
135     else
136         alias TypeOf = typeof(__traits(getMember, A, MEMBER));
137 }
138 
139 /// Gets the return type of a callable symbol.
140 public template ReturnType(alias F)
141     if (isCallable!F)
142 {
143     static if (isLambda!F && !__traits(compiles, { alias _ = std.traits.ReturnType!F; }))
144     {
145         typeof(toDelegate(F)) dg;
146         alias ReturnType = TypeOf!dg;
147     }  
148     else
149         alias ReturnType = std.traits.ReturnType!F;
150 }
151 
152 /// Gets the parameters of a callable symbol.
153 public template Parameters(alias F)
154     if (isCallable!F)
155 {
156     static if (isLambda!F && !__traits(compiles, { alias _ = std.traits.Parameters!F; }))
157     {
158         typeof(toDelegate(F)) dg;
159         alias Parameters = std.traits.Parameters!dg;
160     }  
161     else
162         alias Parameters = std.traits.Parameters!F;
163 }
164 
165 /// Gets the element type of `T`, if applicable.  
166 /// Returns the type of enum values if `T` is an enum.
167 public template ElementType(T) 
168 {
169     static if (is(T == U[], U) || is(T == U*, U) || is(T U == U[L], size_t L))
170         alias ElementType = ElementType!U;
171     else static if (isIndexable!T)
172     {
173         T temp;
174         alias ElementType = ElementType!(typeof(temp[0]));
175     }
176     else
177         alias ElementType = OriginalType!T;
178 }
179 
180 /// Gets the length of `T`, if applicable.
181 public template Length(T) 
182 {
183     static if (is(T U == U[L], size_t L))
184         enum Length = L;
185 }
186 
187 /// Gets an `AliasSeq` all types that `T` implements.
188 public template Implements(T)
189 {
190     template Flatten(H, T...)
191     {
192         static if (T.length)
193         {
194             alias Flatten = AliasSeq!(Flatten!H, Flatten!T);
195         }
196         else
197         {
198             static if (!is(H == Object) && (is(H == class) || is(H == interface)))
199                 alias Flatten = AliasSeq!(H, Implements!H);
200             else
201                 alias Flatten = Implements!H;
202         }
203     }
204 
205     static if (is(T S == super) && S.length)
206     {
207         static if (__traits(getAliasThis, T).length != 0)
208             alias Implements = AliasSeq!(TypeOf!(T, __traits(getAliasThis, T)), Implements!(TypeOf!(T, __traits(getAliasThis, T))), NoDuplicates!(Flatten!S));
209         else
210             alias Implements = NoDuplicates!(Flatten!S);
211     }
212     else
213     {
214         static if (__traits(getAliasThis, T).length != 0)
215             alias Implements = AliasSeq!(TypeOf!(T, __traits(getAliasThis, T)), Implements!(TypeOf!(T, __traits(getAliasThis, T))));
216         else
217             alias Implements = AliasSeq!();
218     }  
219 }
220 
221 /// Gets an `AliasSeq` of the names of all fields in `A`.
222 public template Fields(alias A)
223 {
224     alias Fields = AliasSeq!();
225 
226     static if (hasChildren!A)
227     static foreach (member; __traits(allMembers, A))
228     {
229         static if (isField!(__traits(getMember, A, member)))
230             Fields = AliasSeq!(Fields, member);
231     }
232 }
233 
234 /// Gets an `AliasSeq` of the names all functions in `A`.
235 public template Functions(alias A)
236 {
237     alias Functions = AliasSeq!();
238 
239     static if (hasChildren!A)
240     static foreach (member; __traits(allMembers, A))
241     {
242         static if (isFunction!(__traits(getMember, A, member)))
243             Functions = AliasSeq!(Functions, member);
244     }
245 }
246 
247 /// Gets an `AliasSeq` of the names of all types in `A`.
248 public template Types(alias A)
249 {
250     alias Types = AliasSeq!();
251 
252     static if (hasChildren!A)
253     static foreach (member; __traits(allMembers, A))
254     {
255         static if (isType!(__traits(getMember, A, member)))
256             Types = AliasSeq!(Types, member);
257     }
258 }
259 
260 /// Gets an `AliasSeq` of the names of all templates in `A`.
261 public template Templates(alias A)
262 {
263     alias Templates = AliasSeq!();
264 
265     static if (hasChildren!A)
266     static foreach (member; __traits(allMembers, A))
267     {
268         static if (isTemplate!(__traits(getMember, A, member)))
269             Templates = AliasSeq!(Templates, member);
270     }
271 }