1 /**
2     Numem Traits
3     
4     Copyright:
5         Copyright © 2005-2009, The D Language Foundation.
6         Copyright © 2023-2025, Kitsunebi Games
7         Copyright © 2023-2025, Inochi2D Project
8     
9     License:    $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10     Authors:    
11         $(HTTP digitalmars.com, Walter Bright),
12         Tomasz Stachowiak (isExpressions),
13         $(HTTP erdani.org, Andrei Alexandrescu),
14         Shin Fujishiro,
15         $(HTTP octarineparrot.com, Robert Clipsham),
16         $(HTTP klickverbot.at, David Nadlinger),
17         Kenji Hara,
18         Shoichi Kato
19         Luna Nielsen
20 */
21 module numem.core.traits;
22 import numem.core.meta;
23 
24 /**
25     Gets a sequence over all of the fields in type $(D T).
26 
27     If $(D T) is a type with no fields, returns a sequence containing the input.
28 */
29 template Fields(T) {
30     static if(is(T == struct) || is(T == union))
31         alias Fields = typeof(T.tupleof[0..$-__traits(isNested, T)]);
32     else static if (is(T == class) || is(T == interface))
33         alias Fields = typeof(T.tupleof);
34     else
35         alias Fields = AliasSeq!T;
36 }
37 
38 /**
39     Gets the base element type of type $(D T).
40 */
41 template BaseElemOf(T) {
42     static if(is(OriginalType!T == E[N], E, size_t N))
43         alias BaseElemOf = BaseElemOf!E;
44     else
45         alias BaseElemOf = T;
46 }
47 
48 /**
49     Gets the original type of $(D T).
50 */
51 template OriginalType(T) {
52     template Impl(T) {
53         static if(is(T U == enum)) alias Impl = OriginalType!U;
54         else                       alias Impl = T;
55     }
56 
57     alias OriginalType = ModifyTypePreservingTQ!(Impl, T);
58 }
59 
60 /**
61     Modifies type $(D T) to follow the predicate specified by $(D Modifier).
62 */
63 template ModifyTypePreservingTQ(alias Modifier, T) {
64          static if (is(T U ==          immutable U)) alias ModifyTypePreservingTQ =          immutable Modifier!U;
65     else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U;
66     else static if (is(T U == shared inout       U)) alias ModifyTypePreservingTQ = shared inout       Modifier!U;
67     else static if (is(T U == shared       const U)) alias ModifyTypePreservingTQ = shared       const Modifier!U;
68     else static if (is(T U == shared             U)) alias ModifyTypePreservingTQ = shared             Modifier!U;
69     else static if (is(T U ==        inout const U)) alias ModifyTypePreservingTQ =        inout const Modifier!U;
70     else static if (is(T U ==        inout       U)) alias ModifyTypePreservingTQ =              inout Modifier!U;
71     else static if (is(T U ==              const U)) alias ModifyTypePreservingTQ =              const Modifier!U;
72     else                                             alias ModifyTypePreservingTQ =                    Modifier!T;
73 }
74 
75 /**
76     Removes const type qualifiers from $(D T).
77 */
78 alias Unconst(T : const U, U) = U;
79 
80 /**
81     Removes shared type qualifiers from $(D T).
82 */
83 alias Unshared(T : shared U, U) = U;
84 
85 /**
86     Removes all qualifiers from type T.
87 */
88 template Unqual(T : const U, U) {
89     static if(is(U == shared V, V))
90         alias Unqual = V;
91     else
92         alias Unqual = U;
93 }
94 
95 /**
96     Gets the reference type version of type $(D T).
97 */
98 template Ref(T) {
99     static if (is(T == class) || isHeapAllocated!T)
100         alias Ref = T;
101     else
102         alias Ref = T*;
103 }
104 
105 /**
106     Gets the value type version of type $(D T).
107 */
108 template Unref(T) {
109     static if (!isClasslike!T && isHeapAllocated!T)
110         alias Unref = typeof(*T.init);
111     else
112         alias Unref = T;
113 }
114 
115 /**
116     Gets the amount of bytes needed to allocate an instance of type $(D T).
117 */
118 template AllocSize(T) {
119     static if (is(T == class) || is(T == interface))
120         enum AllocSize = __traits(classInstanceSize, T);
121     else 
122         enum AllocSize = T.sizeof;
123 }
124 
125 /**
126     Gets the alignment of type $(D T) in bytes.
127 */
128 template AllocAlign(T) {
129     static if(is(T == class))
130         enum AllocAlign = __traits(classInstanceAlignment, T);
131     else
132         enum AllocAlign = T.alignof;
133 }
134 
135 private struct __DummyStruct { }
136 
137 /**
138     Returns the rvalue equivalent of T.
139 */
140 @property T rvalueOf(T)(T val) { return val; }
141 
142 /**
143     Returns the rvalue equivalent of $(D T).
144 
145     Can only be used at compile time for type checking.
146 */
147 @property T rvalueOf(T)(inout __DummyStruct = __DummyStruct.init);
148 
149 /**
150     Returns the lvalue equivalent of $(D T).
151 
152     Can only be used at compile time for type checking.
153 */
154 @property ref T lvalueOf(T)(inout __DummyStruct = __DummyStruct.init);
155 
156 /**
157     Gets whether $(D T) supports moving.
158 */
159 enum isMovable(T) =
160     (is(T == struct) || is(T == union)) ||
161     (__traits(isStaticArray, T) && hasElaborateMove!(T.init[0]));
162 
163 /**
164     Gets whether $(D T) is an aggregate type (i.e. a type which contains other types)
165 */
166 enum isAggregateType(T) =
167     is(T == class) || is(T == interface) ||
168     is(T == struct) || is(T == union);
169 
170 /**
171     Gets whether $(D T) is a class-like (i.e. class or interface)
172 */
173 enum isClasslike(T) =
174     is(T == class) || is(T == interface);
175 
176 /**
177     Gets whether $(D T) is a struct-like (i.e. struct or union)
178 */
179 enum isStructLike(T) =
180     is(T == struct) || is(T == union);
181 
182 /**
183     Gets whether $(D T) is a type.
184 */
185 enum isType(T) = !is(typeof(T)) || is(T == void);
186 
187 /**
188     Gets whether the provided type is a scalar type.
189 */
190 enum isScalarType(T) = __traits(isScalar, T) && is(T : real);
191 
192 /**
193     Gets whether the provided type is a basic type.
194 */
195 enum isBasicType(T) = isScalarType!T || is(immutable T == immutable void);
196 
197 /**
198     Gets whether $(D T) is a pointer type.
199 */
200 enum isPointer(T) =
201     is(T == U*, U) && !isClasslike!T;
202 
203 /**
204     Gets whether $(D T) is heap allocated.
205 */
206 enum isHeapAllocated(T) =
207     is(T == class) || is(T == U*, U);
208 
209 /**
210     Gets whether type $(D T) is an array.
211 */
212 enum isArray(T) = is(T == E[n], E, size_t n);
213 
214 /**
215     Gets whether type T is a floating point type.
216 */
217 enum isFloatingPoint(T) = __traits(isFloating, T);
218 
219 /**
220     Gets whether type T is a integral point type.
221 */
222 enum isIntegral(T) = __traits(isIntegral, T);
223 
224 /**
225     Gets whether type T is a numeric type.
226 */
227 enum isNumeric(T) = 
228     __traits(isFloating, T) && 
229     __traits(isIntegral, T);
230 
231 template FtoI(T) {
232     static if (is(T == double))
233         alias FtoI = ulong;
234     else static if (is(T == float))
235         alias FtoI = uint;
236     else
237         alias FtoI = size_t;
238 }
239 
240 /**
241     Gets whether $(D Lhs) can be assigned to $(D Rhs).
242 */
243 template isAssignable(Lhs, Rhs = Lhs) {
244     enum isAssignable = 
245         __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs) && 
246         __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs);
247 }
248 
249 /**
250     Gets whether $(D Lhs) can be assigned to $(D Rhs) or $(D Rhs) can be assigned to $(D Lhs).
251 */
252 enum isAnyAssignable(Lhs, Rhs = Lhs) =
253     isAssignable!(Lhs, Rhs) || isAssignable!(Rhs, Lhs);
254 
255 /**
256     Gets whether the unqualified versions of $(D Lhs) and $(D Rhs) are in
257     any way compatible in any direction.
258 */
259 enum isAnyCompatible(Lhs, Rhs) =
260     is(Unqual!Lhs : Unqual!Rhs) || is(Unqual!Rhs : Unqual!Lhs);
261 
262 /**
263     Gets whether unqualified type $(D TValue) is compatible with the unqualified 
264     range element type of $(D TRange).
265 */
266 enum isAnyCompatibleRange(TRange, TValue) = is(TRange == U[], U) && isAnyCompatible!(typeof(TRange.init[0]), TValue);
267 
268 /**
269     Gets whether $(D symbol) has the user defined attribute $(D attrib).
270 */
271 template hasUDA(alias symbol, alias attrib) {
272     enum hasUDA = anySatisfy!(isDesiredAttr!attrib, __traits(getAttributes, symbol));
273 }
274 
275 /**
276     Gets a sequence of all of the attributes within attrib.
277 */
278 template getUDAs(alias symbol, alias attrib) {
279     alias getUDAs = Filter!(isDesiredAttr!attrib, __traits(getAttributes, symbol));
280 }
281 
282 private
283 template isDesiredAttr(alias attribute) {
284     // Taken from phobos.
285 
286     template isDesiredAttr(alias toCheck) {
287         static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) {
288             static if (__traits(compiles, toCheck == attribute))
289                 enum isDesiredAttr = toCheck == attribute;
290             else
291                 enum isDesiredAttr = false;
292         } else static if (is(typeof(toCheck))) {
293             static if (__traits(isTemplate, attribute))
294                 enum isDesiredAttr = isInstanceOf!(attribute, typeof(toCheck));
295             else
296                 enum isDesiredAttr = is(typeof(toCheck) == attribute);
297         } else static if (__traits(isTemplate, attribute))
298             enum isDesiredAttr = isInstanceOf!(attribute, toCheck);
299         else
300             enum isDesiredAttr = is(toCheck == attribute);
301     }
302 }
303 
304 /**
305     Gets whether $(D T) is an instance of template $(D S).
306 
307     Returns:
308         $(D true) if $(D T) is an instance of template $(D S),
309         $(D false) otherwise.
310 */
311 enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...);
312 template isInstanceOf(alias S, alias T) {
313     enum impl(alias T : S!Args, Args...) = true;
314     enum impl(alias T) = false;
315     enum isInstanceOf = impl!T;
316 } /// ditto
317 
318 /**
319     Gets whether $(D T) is a C++ aggregate type
320 */
321 enum isCPP(T) =
322     isAggregateType!(Unref!T) && __traits(getLinkage, Unref!T) == "C++";
323 
324 /**
325     Gets whether $(D T) is an Objective-C class or protocol.
326 
327     Additionally, said class or protocol needs to have the
328     $(D retain) and $(D release) methods.
329 */
330 enum isObjectiveC(T) =
331     isClasslike!T && __traits(getLinkage, T) == "Objective-C";
332 
333 /**
334     Gets whether $(D T) is a *valid* NSObject derived
335     Objective-C class or protocol.
336 
337     Said class or protocol needs to have the
338     $(D retain), $(D release) and $(D autorelease) methods.
339 
340     See_Also:
341         $(LINK2 https://github.com/Inochi2D/objective-d, Objective-D)
342 */
343 enum isValidObjectiveC(T) =
344     isClasslike!T && __traits(getLinkage, T) == "Objective-C" &&
345     is(typeof(T.retain)) && is(typeof(T.release));
346 
347 
348 /**
349     Gets whether $(D T) is an COM class or interface.
350 
351     Additionally, said class or interface needs to have the
352     $(D retain) and $(D release) methods.
353 */
354 enum isCOMClass(T) =
355     (is(T == class) || is(T == interface)) &&
356     __traits(isCOMClass, T);
357 
358 /**
359     Valid function names for `retain` type functions.
360 */
361 enum rcRetainNames = AliasSeq!("retain", "Retain", "addRef", "AddRef");
362 
363 /**
364     Gets whether $(D T) has a `retain` style function.
365 
366     Params:
367         T = The type to check
368     
369     Returns:
370         $(D true) if $(D T) is a type with a retain function,
371         $(D false) otherwise.
372 
373     See_Also:
374         $(D rcRetainNames)
375 */
376 template hasRCRetainFunction(T) {
377     static if (isCOMClass!T || isValidObjectiveC!T) {
378         enum hasRCRetainFunction = true;
379     } else {
380         enum hasRCFunc(string name) = __traits(hasMember, T, name) && is(typeof((ref T obj) { mixin("obj.", name, "();"); }));
381         enum hasRCRetainFunction = anySatisfy!(hasRCFunc, rcRetainNames);
382     }
383 }
384 
385 /**
386     Valid function names for `release` type functions.
387 */
388 enum rcReleaseNames = AliasSeq!("release", "Release");
389 
390 /**
391     Gets whether $(D T) has a `retain` style function.
392 
393     Params:
394         T = The type to check
395     
396     Returns:
397         $(D true) if $(D T) is a type with a retain function,
398         $(D false) otherwise.
399 
400     See_Also:
401         $(D rcRetainNames)
402 */
403 template hasRCReleaseFunction(T) {
404     static if (isCOMClass!T || isValidObjectiveC!T) {
405         enum hasRCReleaseFunction = true;
406     } else {
407         enum hasRCFunc(string name) = __traits(hasMember, T, name) && is(typeof((ref T obj) { __traits(getMember, obj, name)(); }));
408         enum hasRCReleaseFunction = anySatisfy!(hasRCFunc, rcReleaseNames);
409     }
410 }
411 
412 /**
413     Gets whether $(D T) is refcounted.
414 
415     Params:
416         T = The type to query.
417 
418     Returns:
419         $(D true) if $(D T) is a refcounted type,
420         $(D false) otherwise.
421 */
422 template isRefcounted(T) {
423     static if (isCOMClass!T || isValidObjectiveC!T) {
424         enum isRefcounted = true;
425     } else {
426         enum isRefcounted = hasRCRetainFunction!T && hasRCReleaseFunction!T;
427     }
428 }
429 
430 /**
431     Gets whether T is an inner class in a nested class layout.
432 */
433 template isInnerClass(T) if(is(T == class)) {
434     static if (is(typeof(T.outer))) {
435         template hasOuterMember(T...) {
436             static if (T.length == 0)
437                 enum hasOuterMember = false;
438             else
439                 enum hasOuterMember = T[0] == "outer" || hasOuterMember!(T[1..$]);
440         }
441 
442         enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) && !hasOuterMember!(__traits(allMembers, T));
443     } else enum isInnerClass = false;
444 }
445 
446 /**
447     Gets whether $(D T) or any of its children has an elaborate move.
448 */
449 template hasElaborateMove(T) {
450     static if (isObjectiveC!T)
451         enum bool hasElaborateMove = false;
452     else static if (__traits(isStaticArray, T)) 
453         enum bool hasElaborateMove = T.sizeof && hasElaborateMove!(BaseElemOf!T);
454     else static if (is(T == struct))
455         enum hasElaborateMove = (is(typeof(S.init.opPostMove(lvalueOf!T))) &&
456                                 !is(typeof(S.init.opPostMove(rvalueOf!T)))) ||
457                                 anySatisfy!(.hasElaborateMove, Fields!T);
458     else
459         enum hasElaborateMove = false;
460 
461 }
462 
463 /**
464     Gets whether type $(D T) has elaborate assign semantics
465     (i.e. is $(D opAssign) declared for the type)
466 */
467 template hasElaborateAssign(T) {
468     static if (isObjectiveC!T)
469         enum bool hasElaborateAssign = false;
470     else static if (__traits(isStaticArray, T)) 
471         enum bool hasElaborateAssign = T.sizeof && hasElaborateAssign!(BaseElemOf!T);
472     else static if (is(T == struct))
473         enum hasElaborateAssign = (is(typeof(S.init.opPostMove(opAssign!T))) &&
474                                 !is(typeof(S.init.opPostMove(opAssign!T)))) ||
475                                 anySatisfy!(.hasElaborateAssign, Fields!T);
476     else
477         enum hasElaborateAssign = false;
478 
479 }
480 
481 /**
482     Gets whether type $(D T) has elaborate copy constructor semantics
483     (i.e. is a copy constructor or postblit constructor declared.)
484 */
485 template hasElaborateCopyConstructor(T) {
486     static if (isObjectiveC!T)
487         enum bool hasElaborateCopyConstructor = false;
488     else static if (__traits(isStaticArray, T)) 
489         enum bool hasElaborateCopyConstructor = T.sizeof && hasElaborateCopyConstructor!(BaseElemOf!T);
490     else static if (is(T == struct))
491         enum hasElaborateCopyConstructor = __traits(hasCopyConstructor, T) || __traits(hasPostblit, T);
492     else
493         enum hasElaborateCopyConstructor = false;
494 }
495 
496 /**
497     Gets whether type $(D T) has elaborate destructor semantics (is ~this() declared).
498 */
499 template hasElaborateDestructor(T) {
500     static if (__traits(isStaticArray, T)) 
501         enum bool hasElaborateDestructor = T.sizeof && hasElaborateDestructor!(BaseElemOf!T);
502     else static if (is(T == struct))
503         enum bool hasElaborateDestructor = is(typeof(() { T.init.__xdtor(); }));
504     else
505         enum bool hasElaborateDestructor = false;
506 }
507 
508 /**
509     Gets whether type $(D T) has any destructor semantics, whether explicit
510     or implicit.
511 */
512 template hasAnyDestructor(T) {
513 
514     // NOTE: To handle self-referential structs we need this layer of indirection.
515     template isNotSelf(U) {
516         enum isNotSelf = !is(Unref!U == Unref!T);
517     }
518 
519     static if (isObjectiveC!T)
520         enum bool hasAnyDestructor = false;
521     else static if (__traits(isStaticArray, T)) 
522         enum bool hasAnyDestructor = T.sizeof && hasAnyDestructor!(BaseElemOf!T);
523     else static if (is(T == class))             // Classes have implicit destructors 
524         enum bool hasAnyDestructor = true; 
525     else static if (is(T == interface))         // Interfaces have implicit destructors 
526         enum bool hasAnyDestructor = true;
527     else static if (is(Unref!T == struct)) {
528         static if (is(typeof(() { Unref!T.init.__xdtor(); })))
529             enum bool hasAnyDestructor = true;
530         else enum bool hasAnyDestructor = anySatisfy!(.hasAnyDestructor, Filter!(isNotSelf, Fields!(Unref!T)));
531     } else
532         enum bool hasAnyDestructor = false;
533 }
534 
535 /**
536     Detect whether symbol or type $(D T) is a function, a function pointer or a delegate.
537 
538     Params:
539         T = The type to check
540     Returns:
541         A $(D_KEYWORD bool)
542  */
543 enum bool isSomeFunction(alias T) =
544     is(T == return) ||
545     is(typeof(T) == return) ||
546     is(typeof(&T) == return); // @property
547 
548 /**
549     Detect whether $(D T) is a callable object, which can be called with the
550     function call operator `$(LPAREN)...$(RPAREN)`.
551 */
552 template isCallable(alias callable) {
553     static if (is(typeof(&callable.opCall) == delegate))
554         // T is a object which has a member function opCall().
555         enum bool isCallable = true;
556     else static if (is(typeof(&callable.opCall) V : V*) && is(V == function))
557         // T is a type which has a static member function opCall().
558         enum bool isCallable = true;
559     else static if (is(typeof(&callable.opCall!()) TemplateInstanceType))
560     {
561         enum bool isCallable = isCallable!TemplateInstanceType;
562     }
563     else static if (is(typeof(&callable!()) TemplateInstanceType))
564     {
565         enum bool isCallable = isCallable!TemplateInstanceType;
566     }
567     else
568     {
569         enum bool isCallable = isSomeFunction!callable;
570     }
571 }
572 
573 /**
574     Get the function type from a callable object $(D func), or from a function pointer/delegate type.
575 
576     Using builtin $(D typeof) on a property function yields the types of the
577     property value, not of the property function itself.  Still,
578     $(D FunctionTypeOf) is able to obtain function types of properties.
579 
580     Note:
581         Do not confuse function types with function pointer types; function types are
582         usually used for compile-time reflection purposes.
583 */
584 template FunctionTypeOf(alias func)
585 if (isCallable!func) {
586     static if ((is(typeof(& func) Fsym : Fsym*) && is(Fsym == function)) || is(typeof(& func) Fsym == delegate))
587     {
588         alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol
589     }
590     else static if (is(typeof(& func.opCall) Fobj == delegate) || is(typeof(& func.opCall!()) Fobj == delegate))
591     {
592         alias FunctionTypeOf = Fobj; // HIT: callable object
593     }
594     else static if (
595             (is(typeof(& func.opCall) Ftyp : Ftyp*) && is(Ftyp == function)) ||
596             (is(typeof(& func.opCall!()) Ftyp : Ftyp*) && is(Ftyp == function))
597         )
598     {
599         alias FunctionTypeOf = Ftyp; // HIT: callable type
600     }
601     else static if (is(func T) || is(typeof(func) T))
602     {
603         static if (is(T == function))
604             alias FunctionTypeOf = T;    // HIT: function
605         else static if (is(T Fptr : Fptr*) && is(Fptr == function))
606             alias FunctionTypeOf = Fptr; // HIT: function pointer
607         else static if (is(T Fdlg == delegate))
608             alias FunctionTypeOf = Fdlg; // HIT: delegate
609         else
610             static assert(0);
611     }
612     else
613         static assert(0);
614 }
615 
616 /**
617     Get the type of the return value from a function,
618     a pointer to function, a delegate, a struct
619     with an opCall, a pointer to a struct with an opCall,
620     or a class with an $(D opCall). Please note that $(D_KEYWORD ref)
621     is not part of a type, but the attribute of the function.
622 
623     Note:
624         To reduce template instantiations, consider instead using
625         $(D_INLINECODE typeof(() { return func(args); } ())) if the argument types are known or
626         $(D_INLINECODE static if (is(typeof(func) Ret == return))) if only that basic test is needed.
627 */
628 template ReturnType(alias func)
629 if (isCallable!func) {
630     static if (is(FunctionTypeOf!func R == return))
631         alias ReturnType = R;
632     else
633         static assert(0, "argument has no return type");
634 }
635 
636 /**
637     Get, as a tuple, the types of the parameters to a function, a pointer
638     to function, a delegate, a struct with an `opCall`, a pointer to a
639     struct with an `opCall`, or a class with an `opCall`.
640 */
641 template Parameters(alias func)
642 if (isCallable!func) {
643     static if (is(FunctionTypeOf!func P == function))
644         alias Parameters = P;
645     else
646         static assert(0, "argument has no parameters");
647 }