1 /** Various extensions to std.traits.
2 
3     Copyright: Per Nordlöw 2022-.
4     License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
5     Authors: $(WEB Per Nordlöw)
6 
7     See_Also: http://forum.dlang.org/thread/jbyixfbuefvdlttnyclu@forum.dlang.org#post-mailman.2199.1353742037.5162.digitalmars-d-learn:40puremagic.com
8     See_Also: http://forum.dlang.org/post/rjrdgmijsmvvsbpinidz@forum.dlang.org
9 */
10 module nxt.traits_ex;
11 
12 import std.traits: isArray, ParameterTypeTuple, isDynamicArray, isSomeChar, isSomeString, isExpressions, isIntegral, isSigned, isUnsigned, isAssignable, isIterable;
13 import std.meta : allSatisfy;
14 import std.range: ElementType, isForwardRange, isRandomAccessRange, isInputRange, isBidirectionalRange, isOutputRange;
15 
16 /** Returns: `true` iff $(D ptr) is handled by D's garbage collector (GC).
17  */
18 bool isGCPointer(T)(const T* ptr)
19     @trusted nothrow @nogc
20 {
21     import core.memory : GC;
22     return cast(bool)GC.addrOf(ptr);
23 }
24 
25 ///
26 @system nothrow unittest
27 {
28     int s;
29     int* sp = &s;
30     assert(!sp.isGCPointer);
31     int* ip = new int;
32     assert(ip.isGCPointer);
33 }
34 
35 /** Returns: `true` iff all values `V` are the same.
36  *
37  * See_Also: https://forum.dlang.org/post/lnsreapgttmdeuscsupp@forum.dlang.org
38  */
39 template allSameIterative(V...)
40 {
41     static if (V.length <= 1)
42         enum allSameIterative = true;
43     else
44     {
45         static foreach (Vi; V[1 .. $])
46             static if (is(typeof(allSameIterative) == void) && // not yet defined
47                        !isSame!(V[0], Vi))
48                 enum allSameIterative = false;
49         static if (is(typeof(allSameIterative) == void)) // if not yet defined
50             enum allSameIterative = true;
51     }
52 }
53 
54 ///
55 @safe pure nothrow @nogc unittest
56 {
57     static assert( allSameIterative!());
58     static assert( allSameIterative!(42));
59     static assert( allSameIterative!(42, 42));
60     static assert( allSameIterative!(42, 42, 42));
61     static assert(!allSameIterative!(42, 43, 42));
62 
63     static assert( allSameIterative!(int));
64     static assert( allSameIterative!(int, int));
65     static assert( allSameIterative!(int, int, int));
66     static assert(!allSameIterative!(int, byte, int));
67 }
68 
69 alias allSame = allSameIterative; // default to iterative variant for now
70 alias isHomogeneousType = allSame;
71 enum isHomogeneousTuple(T) = isHomogeneousType!(T.Types);
72 
73 ///
74 @safe pure nothrow @nogc unittest
75 {
76     static assert(isHomogeneousTuple!(Tuple!(int, int, int)));
77     static assert(isHomogeneousTuple!(Tuple!(float, float, float)));
78     static assert(!isHomogeneousTuple!(Tuple!(int, float, double)));
79 }
80 
81 enum isHomogeneousTupleOf(T, E) = (isHomogeneousType!(T) &&
82                                    is(T.Types[0] == E));
83 
84 ///
85 @safe pure nothrow @nogc unittest
86 {
87     static assert(isHomogeneousTupleOf!(Tuple!(int, int, int), int));
88     static assert(isHomogeneousTupleOf!(Tuple!(float, float, float), float));
89     static assert(!isHomogeneousTupleOf!(Tuple!(float, float, float), int));
90 }
91 
92 /**
93    Returns $(D true) if at least one type in the $(D Tuple T)
94    is not the same as the others.
95 */
96 enum isHeterogeneous(T) = !isHomogeneousType!T;
97 
98 template allSameTypeIterative(V...)
99 // TODO: restrict `V` to types only
100 {
101     static if (V.length >= 2)
102         static foreach (Vi; V[1 .. $])
103             static if (is(typeof(allSameTypeIterative) == void) && // not yet defined
104                        !is(V[0] == Vi)) // 10% faster than `!isSame(V[0], Vi)`
105                 enum allSameTypeIterative = false;
106     static if (is(typeof(allSameTypeIterative) == void)) // if not yet defined
107         enum allSameTypeIterative = true;
108 }
109 alias allSameType = allSameTypeIterative;
110 
111 ///
112 @safe pure nothrow @nogc unittest
113 {
114     static assert( allSameTypeIterative!(int));
115     static assert( allSameTypeIterative!(int, int));
116 
117     static assert( allSameTypeIterative!(int, int, int));
118     static assert(!allSameTypeIterative!(int, byte, int));
119 
120     static assert( allSameTypeIterative!(int, int, int, int));
121     static assert(!allSameTypeIterative!(int, byte, int, byte));
122 
123     static assert(!allSameTypeIterative!(int, const(int)));
124     static assert(!allSameTypeIterative!(byte, const(int)));
125 }
126 
127 /** Returns: `true` iff all values `V` are the same.
128 
129     Same as NoDuplicates!V.length == 1
130 
131     See_Also: https://forum.dlang.org/post/ptnzlhnkuetijhgrgumd@forum.dlang.org
132     See_Also: http://forum.dlang.org/post/iflpslqgrixdjwrlqqvn@forum.dlang.org
133     See_Also: http://forum.dlang.org/post/mheumktihihfsxxxapff@forum.dlang.org
134 */
135 template allSameRecursive(V...)
136 if (isExpressions!V)
137 {
138     static if (V.length <= 1)
139         enum allSameRecursive = true;
140     else static if (V.length & 1) // odd count
141         enum allSameRecursive = (V[0] == V[$ - 1] && // first equals last
142                                  V[0 .. $/2] == V[$/2 .. $-1] && // (first half) equals (second half minus last element)
143                                  allSameRecursive!(V[0 .. $/2]));
144     else                        // event count
145         enum allSameRecursive = (V[0 .. $/2] == V[$/2 .. $] && // (first half) equals (second half)
146                                  allSameRecursive!(V[0 .. $/2]));
147 }
148 
149 ///
150 @safe pure nothrow @nogc unittest
151 {
152     static assert( allSameRecursive!());
153     static assert( allSameRecursive!(42));
154     static assert( allSameRecursive!(42, 42));
155     static assert( allSameRecursive!(42, 42, 42));
156     static assert(!allSameRecursive!(42, 43, 42));
157 }
158 
159 template allSameTypeHybrid(V...)
160 // TODO: restrict `V` to types only
161 {
162     static if (V.length >= 8)
163     {
164         static if (V.length <= 1)
165             enum allSameTypeHybrid = true;
166         else static if (V.length == 2)
167             enum allSameTypeHybrid = is(V[0] == V[1]);
168         static if (V.length & 1) // odd count
169             enum allSameTypeHybrid = (is(V[0] == V[$ - 1]) && // first equals last
170                                       is(V[0 .. $/2] == V[$/2 .. $-1]) && // (first half) equals (second half minus last element)
171                                       allSameTypeHybrid!(V[0 .. $/2]));
172         else                        // even count
173             enum allSameTypeHybrid = (is(V[0 .. $/2] == V[$/2 .. $]) && // (first half) equals (second half)
174                                       allSameTypeHybrid!(V[0 .. $/2]));
175     }
176     else
177         enum allSameTypeHybrid = allSameTypeIterative!(V);
178 }
179 
180 ///
181 @safe pure nothrow @nogc unittest
182 {
183     static assert(allSameTypeHybrid!());
184     static assert(allSameTypeHybrid!(int));
185     static assert(allSameTypeHybrid!(int, int));
186     static assert(!allSameTypeHybrid!(int, double));
187     static assert(!allSameTypeHybrid!(int, int, double));
188     static assert(allSameTypeHybrid!(Tuple!(int, int, int).Types, int));
189 
190     static assert(!allSameTypeHybrid!(int, const(int)));
191     static assert(!allSameTypeHybrid!(byte, const(int)));
192 }
193 
194 /** Variant of `allSameTypeRecursive`.
195  */
196 template allSameTypeRecursive2(V...)
197     if (isExpressions!(V))
198 {
199     static if (V.length <= 1)
200         enum allSameTypeRecursive2 = true;
201     else
202         enum allSameTypeRecursive2 = (V[0] == V[1] &&
203                                       allSameTypeRecursive2!(V[1..$]));
204 }
205 
206 ///
207 @safe pure nothrow @nogc unittest
208 {
209     static assert(allSameTypeRecursive2!());
210     static assert(allSameTypeRecursive2!(42));
211     static assert(!allSameTypeRecursive2!(41, 42));
212     static assert(allSameTypeRecursive2!(42, 42, 42));
213 }
214 
215 /** Returns: `true` iff all types `T` are the same.
216  */
217 template allSameTypeRecursive(V...)
218 // TODO: restrict `V` to types only
219 {
220     static if (V.length <= 1)
221         enum allSameTypeRecursive = true;
222     else static if (V.length & 1) // odd count
223         enum allSameTypeRecursive = (is(V[0] == V[$ - 1]) && // first equals last
224                                      is(V[0 .. $/2] == V[$/2 .. $-1]) && // (first half) equals (second half minus last element)
225                                      allSameTypeRecursive!(V[0 .. $/2]));
226     else                        // even count
227         enum allSameTypeRecursive = (is(V[0 .. $/2] == V[$/2 .. $]) && // (first half) equals (second half)
228                                      allSameTypeRecursive!(V[0 .. $/2]));
229 }
230 
231 ///
232 @safe pure nothrow @nogc unittest
233 {
234     static assert(allSameTypeRecursive!());
235     static assert(allSameTypeRecursive!(int));
236     static assert(allSameTypeRecursive!(int, int));
237     static assert(!allSameTypeRecursive!(int, double));
238     static assert(!allSameTypeRecursive!(int, int, double));
239     static assert(allSameTypeRecursive!(Tuple!(int, int, int).Types, int));
240 
241     static assert(!allSameTypeRecursive!(int, const(int)));
242     static assert(!allSameTypeRecursive!(byte, const(int)));
243 }
244 
245 /** Returns: `true` iff all types `T` are the same. */
246 enum allSameType_alternative(T...) = (!T.length ||
247                                       (is(T[0] == T[T.length > 1]) &&
248                                        allSameType1!(T[1 .. $])));
249 
250 
251 import std.typecons : isTuple;
252 
253 /**
254    Returns $(D true) if all types in the $(D Tuple T) are the same.
255    TODO: Remove when this is merged: https://github.com/D-Programming-Language/phobos/pull/3395
256    See_Also: https://github.com/D-Programming-Language/phobos/pull/1672/files
257 */
258 template allSameTypesInTuple(T)
259     if (isTuple!T)
260 {
261     alias types = T.Types;
262     static if (types.length > 0)
263     {
264         template isSameTypeAsHead(U)
265         {
266             enum isSameTypeAsHead = is(U == types[0]);
267         }
268         import std.meta : allSatisfy;
269         enum allSameTypesInTuple = allSatisfy!(isSameTypeAsHead, types);
270     }
271     else
272         enum allSameTypesInTuple = true;
273 }
274 
275 ///
276 @safe pure nothrow unittest
277 {
278     alias HOTUP = Tuple!(int, int, int);
279     static assert(allSameTypesInTuple!HOTUP);
280 
281     const HOTUP hotup = HOTUP(1, 2, 3);
282     static assert(allSameTypesInTuple!(typeof(hotup)));
283 
284     alias HETUP = Tuple!(string, bool, float);
285     static assert(!allSameTypesInTuple!(HETUP));
286 
287     const HETUP hetup = HETUP("test", false, 2.345);
288     static assert(!allSameTypesInTuple!(typeof(hetup)));
289 
290     alias ZTUP = Tuple!();
291     static assert(allSameTypesInTuple!ZTUP);
292 
293     const ZTUP ztup = ZTUP();
294     static assert(allSameTypesInTuple!(typeof(ztup)));
295 }
296 
297 /** Returns: tuple `tup` as a dynamic array.
298  */
299 auto asDynamicArray(T)(inout T tup)
300     if (allSameTypeRecursive!(T.Types))
301 {
302     alias E = T.Types[0];
303     E[] a = new E[T.length];
304     a.length = T.length;
305     foreach (const i, e; tup)
306         a[i] = e;
307     return a;
308 }
309 
310 ///
311 pure nothrow unittest
312 {
313     import std.typecons: tuple;
314     auto tup = tuple("a", "b", "c", "d");
315     string[4] arr = ["a", "b", "c", "d"];
316     assert(tup.asDynamicArray() == arr);
317 }
318 
319 /** Is `true` if `R` is iterable over references to its elements.
320  *
321  * Typically used to iterate over ranges with uncopyable elements.
322  *
323  * TODO: Move to Phobos.
324  */
325 enum bool isRefIterable(T) = is(typeof({ foreach (ref elem; T.init) {} }));
326 
327 /// Useful aliases for combinations of range predicates.
328 enum isIterableOf(R, E) = isIterable!R && is(ElementType!R == E);
329 enum isIterableOfUnqual(R, E) = isIterable!R && is(Unqual!(ElementType!R) == Unqual!E);
330 enum isIterableOfSomeString(R) = (isIterable!R && isSomeString!(ElementType!R));
331 
332 ///
333 @safe pure nothrow @nogc unittest
334 {
335     alias E = string;
336     alias I = int;
337     alias R = typeof(["a", "b"]);
338     static assert(isIterableOf!(R, E));
339     static assert(isIterableOfUnqual!(R, const(E)));
340     static assert(isIterableOfSomeString!(R));
341     static assert(!isIterableOf!(R, I));
342 }
343 
344 // TODO: use this instead?
345 version(none) private template isInputRangeOf(R, E)
346 {
347     import std.range.primitives: isInputRange, ElementType;
348     import std.traits: Unqual;
349     enum isInputRangeOf = isInputRange!R && is(Unqual!(ElementType!R) == E);
350 }
351 
352 /// Useful aliases for combinations of range predicates.
353 enum isRandomAccessRangeOf(R, E) = isRandomAccessRange!R && is(ElementType!R == E);
354 enum isForwardRangeOf(R, E) = isForwardRange!R && is(ElementType!R == E);
355 enum isInputRangeOf(R, E) = isInputRange!R && is(ElementType!R == E);
356 
357 enum isInputRangeOfUnqual(R, E) = (isInputRange!R &&
358                                    is(Unqual!(ElementType!R) == E));
359 
360 enum isBidirectionalRangeOf(R, E) = isBidirectionalRange!R && is(ElementType!R == E);
361 enum isOutputRangeOf(R, E) = isOutputRange!R && is(ElementType!R == E);
362 enum isArrayOf(R, E) = isArray!R && is(ElementType!R == E);
363 enum isArrayOfSomeString(R) = isArray!R && isSomeString!(ElementType!R);
364 
365 enum isSourceAssignableTo(R, E) = (isInputRange!R &&
366                                    isAssignable!(E, ElementType!R));
367 
368 ///
369 @safe pure unittest
370 {
371     static assert(isSomeString!(string));
372     static assert(isSomeString!(const string));
373 
374     static assert(isSomeString!(const(char)[]));
375     static assert(isSomeString!(const char[]));
376 }
377 
378 @safe pure nothrow @nogc unittest
379 {
380     alias R = typeof(["a", "b"]);
381     static assert(isArrayOf!(R, string));
382     static assert(isArrayOfSomeString!(R));
383 }
384 
385 enum isSource(R) = isInputRange!(R);
386 enum isRange(R) = isInputRange!(R);
387 
388 enum isSourceOf(R, E) = isInputRangeOf!(R, E);
389 enum isSourceOfUnqual(R, E) = isInputRangeOfUnqual!(R, E);
390 private enum isSink(R) = isOutputRange!(R);
391 private enum isSinkOf(R, E) = isOutputRangeOf!(R, E);
392 
393 enum isSourceOfSomeChar(R) = (isSource!R &&
394                               isSomeChar!(ElementType!R));
395 alias isSomeLazyString = isSourceOfSomeChar;
396 
397 @safe pure nothrow @nogc unittest
398 {
399     import std.meta : AliasSeq;
400     foreach (Ch; AliasSeq!(char, wchar, dchar))
401     {
402         assert(isSourceOfSomeChar!(Ch[]));
403         assert(isSourceOfSomeChar!(const(Ch)[]));
404         assert(isSourceOfSomeChar!(immutable(Ch)[]));
405     }
406 }
407 
408 enum isSourceOfSomeString(R) = (isSource!R && isSomeString!(ElementType!R));
409 alias isSomeStringSource = isSourceOfSomeString;
410 
411 import std.functional: unaryFun, binaryFun;
412 
413 /* TODO: Do we need use of unaryFun and binaryFun here? */
414 alias isEven = unaryFun!(a => (a & 1) == 0); // Limit to Integers?
415 alias isOdd = unaryFun!(a => (a & 1) == 1); // Limit to Integers?
416 alias lessThan = binaryFun!((a, b) => a < b);
417 alias greaterThan = binaryFun!((a, b) => a > b);
418 
419 /** Check if `T` has an even length. */
420 enum hasEvenLength(T...) = !(T.length & 1);
421 @safe pure nothrow @nogc unittest
422 {
423     static assert(!hasEvenLength!(1));
424     static assert(hasEvenLength!(1, 2));
425     static assert(!hasEvenLength!(1, 2, 3));
426     static assert(hasEvenLength!(1, 2, 3, 4));
427 }
428 
429 enum isSignedIntegral(T) = isIntegral!T && isSigned!T;
430 enum isUnsignedIntegral(T) = isIntegral!T && isUnsigned!T;
431 
432 enum isString (T) = is(T == string);
433 enum isWString(T) = is(T == wstring);
434 enum isDString(T) = is(T == dstring);
435 
436 enum isEnum(T) = is(T == enum);
437 @safe pure nothrow @nogc unittest
438 {
439     interface I {}
440     class A {}
441     class B( T ) {}
442     class C : B!int, I {}
443     struct S {}
444     enum E { X }
445     static assert(!isEnum!A );
446     static assert(!isEnum!( B!int ) );
447     static assert(!isEnum!C );
448     static assert(!isEnum!I );
449     static assert(isEnum!E );
450     static assert(!isEnum!int );
451     static assert(!isEnum!( int* ) );
452 }
453 
454 /* See_Also: http://d.puremagic.com/issues/show_bug.cgi?id=4427 */
455 enum isStruct(T) = is(T == struct);
456 @safe pure nothrow @nogc unittest
457 {
458     interface I {}
459     class A {}
460     class B( T ) {}
461     class C : B!int, I {}
462     struct S {}
463     static assert(!isStruct!A );
464     static assert(!isStruct!( B!int ) );
465     static assert(!isStruct!C );
466     static assert(!isStruct!I );
467     static assert(isStruct!S );
468     static assert(!isStruct!int );
469     static assert(!isStruct!( int* ) );
470 }
471 
472 enum isClass(T) = is(T == class);
473 @safe pure nothrow @nogc unittest
474 {
475     interface I {}
476     class A {}
477     class B( T ) {}
478     class C : B!int, I {}
479     struct S {}
480     static assert(isClass!A );
481     static assert(isClass!( B!int ) );
482     static assert(isClass!C );
483     static assert(!isClass!I );
484     static assert(!isClass!S );
485     static assert(!isClass!int );
486     static assert(!isClass!( int* ) );
487 }
488 
489 enum isInterface(T) = is(T == interface);
490 @safe pure nothrow @nogc unittest
491 {
492     interface I {}
493     class A {}
494     class B( T ) {}
495     class C : B!int, I {}
496     struct S {}
497     static assert(!isInterface!A );
498     static assert(!isInterface!( B!int ) );
499     static assert(!isInterface!C );
500     static assert(isInterface!I );
501     static assert(!isInterface!S );
502     static assert(!isInterface!int );
503     static assert(!isInterface!( int* ) );
504 }
505 
506 template isType(T)       { enum isType = true; }
507 template isType(alias T) { enum isType = false; }
508 
509 @safe pure nothrow @nogc unittest
510 {
511     struct S { alias int foo; }
512     static assert(isType!int );
513     static assert(isType!float );
514     static assert(isType!string );
515     //static assert(isType!S ); // Bugzilla 4431
516     static assert(isType!( S.foo ) );
517     static assert(!isType!4 );
518     static assert(!isType!"Hello world!" );
519 }
520 
521 enum nameOf(alias a) = a.stringof;
522 ///
523 @safe pure nothrow @nogc unittest
524 {
525     int var;
526     static assert(nameOf!var == var.stringof);
527 }
528 
529 /** Is $(D ElementType) of type of $(D a). */
530 alias ElementTypeOf(alias a) = ElementType!(typeof(a));
531 ///
532 @safe pure nothrow @nogc unittest
533 {
534     int[] var;
535     static assert(is(ElementTypeOf!var == int));
536 }
537 
538 template Chainable()
539 {
540     import std.range: chain;
541     auto ref opCast(Range)(Range r)
542     {
543         return chain(this, r);
544     }
545 }
546 @safe pure nothrow @nogc unittest { mixin Chainable; }
547 
548 /** Returns true if `T` is an instance of the template `S`.
549     See_Also: http://forum.dlang.org/thread/mailman.2901.1316118301.14074.digitalmars-d-learn@puremagic.com#post-zzdpfhsgfdgpszdbgbbt:40forum.dlang.org
550 */
551 template isA(alias S, T)
552 {
553     enum isA = is(T == S!(_), _);
554 }
555 
556 @safe pure nothrow @nogc unittest
557 {
558     import std.range : SortedRange, assumeSorted;
559     const x = [1, 2, 3].s[].assumeSorted;
560     static assert(is(typeof(x) == SortedRange!(_), _...));
561     static assert(isA!(SortedRange, typeof(x)));
562 }
563 
564 /** See_Also: http://forum.dlang.org/thread/bug-6384-3@http.d.puremagic.com/issues/
565     See_Also: http://forum.dlang.org/thread/jrqiiicmtpenzokfxvlz@forum.dlang.org */
566 enum isOpBinary(T, string op, U) = is(typeof(mixin("T.init" ~ op ~ "U.init")));
567 
568 enum isComparable(T) = is(typeof({ return T.init <  T.init; })); /// TODO: Move to Phobos std.traits
569 enum isEquable   (T) = is(typeof({ return T.init == T.init; })); /// TODO: Move to Phobos std.traits
570 enum isNotEquable(T) = is(typeof({ return T.init != T.init; })); /// TODO: Move to Phobos std.traits
571 
572 @safe pure nothrow @nogc unittest
573 {
574     static assert(isComparable!int);
575     static assert(isComparable!string);
576     static assert(!isComparable!creal);
577     static struct Foo {}
578     static assert(!isComparable!Foo);
579     static struct Bar { bool opCmp(Bar) { return true; } }
580     static assert(isComparable!Bar);
581 }
582 
583 // TODO:  variadic
584 enum areComparable(T, U) = is(typeof({ return T.init <  U.init; })); /// TODO: Move to Phobos std.traits
585 enum areEquable   (T, U) = is(typeof({ return T.init == U.init; })); /// TODO: Move to Phobos std.traits
586 enum areNotEquable(T, U) = is(typeof({ return T.init != U.init; })); /// TODO: Move to Phobos std.traits
587 
588 @safe pure nothrow @nogc unittest
589 {
590     static assert(areComparable!(int, float));
591     static assert(areEquable!(int, float));
592     static assert(areNotEquable!(int, float));
593 
594     static assert(!areComparable!(int, string));
595     static assert(!areEquable!(int, string));
596     static assert(!areNotEquable!(int, string));
597 }
598 
599 enum isValueType(T) = !hasIndirections!T;
600 enum hasValueSemantics(T) = !hasIndirections!T; // TODO: merge with isValueType
601 enum isReferenceType(T) = hasIndirections!T;
602 
603 enum arityMin0(alias fun) = __traits(compiles, fun());
604 
605 /** TODO: Unite into a variadic.
606     See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org
607 */
608 enum isCallableWith(alias fun, T) = (is(typeof(fun(T.init))) ||
609                                      is(typeof(T.init.fun))); // TODO: Are both these needed?
610 @safe pure nothrow @nogc unittest
611 {
612     auto sqr(T)(T x) { return x*x; }
613     assert(isCallableWith!(sqr, int));
614     assert(!isCallableWith!(sqr, string));
615 }
616 
617 /* TODO: Unite into a variadic.
618    See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org
619  */
620 enum isCallableWith(alias fun, T, U) = (is(typeof(fun(T.init,
621                                                       U.init))) ||
622                                         is(typeof(T.init.fun(U)))); // TODO: Are both these needed?
623 @safe pure nothrow @nogc unittest
624 {
625     auto sqr2(T)(T x, T y) { return x*x + y*y; }
626     assert(isCallableWith!(sqr2, int, int));
627     assert(!isCallableWith!(sqr2, int, string));
628 }
629 
630 /** Check if `T` is a Sorted Range.
631     See_Also: http://forum.dlang.org/thread/lt1g3q$15fe$1@digitalmars.com
632 */
633 template isSortedRange(T)
634 {
635     import std.range: SortedRange;
636     enum isSortedRange = is(T == SortedRange!(_), _); // TODO: Or use: __traits(isSame, TemplateOf!R, SortedRange)
637 }
638 
639 /** Check if Function $(D expr) is callable at compile-time.
640     See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org
641 */
642 template isCTFEable(alias fun)
643 {
644     template isCTFEable_aux(alias T)
645     {
646         enum isCTFEable_aux = T;
647     }
648     enum isCTFEable = __traits(compiles, isCTFEable_aux!(fun()));
649 }
650 
651 template isCTFEable2(fun...)
652 {
653     enum isCTFEable2 = true;
654 }
655 
656 @safe pure nothrow unittest
657 {
658     int fun1() { return 1; }
659     auto fun1_N()
660     {
661         import std.array;
662 //would return Error: gc_malloc cannot be interpreted at compile time,
663         /* because it has no available source code due to a bug */
664             return [1].array;
665     }
666     int fun2(int x)
667     {
668         return 1;
669     }
670     auto fun2_N(int x){
671         import std.array;
672 //same as fun1_N
673         return [1].array;
674     }
675 
676     int a1;
677     enum a2=0;
678 
679     static assert(!isCTFEable!(()=>a1));
680     static assert(isCTFEable!(()=>a2));
681 
682     static assert(isCTFEable!fun1);
683     /* static assert(!isCTFEable!fun1_N); */
684 
685     static assert(isCTFEable!(()=>fun2(0)));
686     /* static assert(!isCTFEable!(()=>fun2_N(0))); */
687 //NOTE:an alternate syntax which could be implemented would be: static
688     /* assert(!isCTFEable!(fun2_N,0)); */
689 }
690 
691 /** Check if the value of $(D expr) is known at compile-time.
692     See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org
693 */
694 enum isCTEable(alias expr) = __traits(compiles, { enum id = expr; });
695 
696 @safe pure nothrow @nogc unittest
697 {
698     static assert(isCTEable!11);
699     enum x = 11;
700     static assert(isCTEable!x);
701     auto y = 11;
702     static assert(!isCTEable!y);
703 }
704 
705 import std.traits: hasFunctionAttributes, isCallable, ParameterTypeTuple, Unqual;
706 
707 /** Returns $(D true) if `T` is not $(D const) or $(D immutable).
708     Note that isConst is true for string, or immutable(char)[], because the
709     'head' is mutable.
710 */
711 import std.traits : isMutable;
712 enum isConst(T) = !isMutable!T;
713 
714 @safe pure nothrow @nogc unittest
715 {
716     static assert(isConst!(const(int)));
717     static assert(!isConst!int);
718 }
719 
720 import std.traits : CommonType;
721 
722 /// Is `true` iff `Types` all share a common type.
723 enum bool haveCommonType(Types...) = !is(CommonType!Types == void);
724 
725 ///
726 @safe pure nothrow @nogc unittest
727 {
728     static assert(haveCommonType!(bool, int, long));
729     static assert(!haveCommonType!(bool, int, string));
730 }
731 
732 /** Check if $(D fun) is a pure function. */
733 enum bool isPure(alias fun) = hasFunctionAttributes!(fun, `pure`);
734 
735 /** Check if $(D fun) is a function purely callable with arguments T. */
736 enum bool isPurelyCallableWith(alias fun, T...) = (isPure!fun &&
737                                                    is(T == ParameterTypeTuple!fun));
738 
739 ///
740 @safe pure nothrow @nogc unittest
741 {
742     static int foo(int x) @safe pure nothrow { return x; }
743     static assert(isPure!foo);
744     static assert(isPurelyCallableWith!(foo, int));
745 }
746 
747 /** Check if $(D fun) is a @nogc function.
748     See_Also: http://forum.dlang.org/thread/dyumjfmxmstpgyxbozry@forum.dlang.org
749 */
750 enum bool isNogc(alias fun) = hasFunctionAttributes!(fun, `@nogc`);
751 
752 ///
753 @safe pure nothrow @nogc unittest
754 {
755     static int foo(int x) @nogc pure nothrow;
756     static int goo(int x) pure nothrow;
757     static assert(isNogc!foo);
758     static assert(!isNogc!goo);
759 }
760 
761 /** Persistently Call Function $(D fun) with arguments $(D args).
762 
763     Hash Id Build-Timestamp (Code-Id because we currently have stable way of hashing-algorithms) is Constructed from Data Structure:
764     - Hierarchically Mangled Unqual!typeof(instance)
765     - Use msgpack in combination with sha1Of or only sha1Of (with extended
766     overloads for sha1Of) if available.
767 
768     Extend std.functional : memoize to accept pure functions that takes an
769     immutable mmap as input. Create wrapper that converts file to immutable mmap
770     and performs memoization on the pure function.
771 
772 */
773 auto persistentlyMemoizedCall(alias fun, T...)(T args)
774     if (isPure!fun &&
775         isCallable!(fun, args))
776 {
777     import std.functional: memoize;
778     return fun(args);
779 }
780 
781 /** Move std.uni.newLine?
782     TODO: What to do with Windows style endings?
783     See_Also: https://en.wikipedia.org/wiki/Newline
784 */
785 bool isNewline(C)(C c) @safe pure nothrow @nogc
786     if (isSomeChar!C)
787 {
788     import std.ascii: newline; // TODO: Probably not useful.
789     static if (newline == "\n")
790         return (c == '\n' || c == '\r'); // optimized for systems with \n as default
791     else static if (newline == "\r")
792         return (c == '\r' || c == '\n'); // optimized for systems with \r as default
793     else
794         static assert(0, "Support Windows?");
795 }
796 
797 bool isNewline(S)(S s) @safe pure nothrow @nogc
798     if (isSomeString!S)
799 {
800     import std.ascii: newline; // TODO: Probably not useful.
801     static if (newline == "\n")
802         return (s == '\n' || s == '\r'); // optimized for systems with \n as default
803     else static if (newline == "\r")
804         return (s == '\r' || s == '\n'); // optimized for systems with \r as default
805     else static if (newline == "\r\n")
806         return (s == "\r\n" || s == '\r' || s == '\n'); // optimized for systems with \r\n as default
807     else static if (newline == "\n\r")
808         return (s == "\n\r" || s == '\r' || s == '\n'); // optimized for systems with \n\r as default
809     else
810         static assert(0, "Support windows?");
811 }
812 
813 /** Dynamic variant of $(D EnumMembers) returning enum member constants
814  * (enumerators) of `T`.
815  *
816  * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org
817  */
818 T[] enumMembersAsEnumerators(T)()
819 if (is(T == enum))
820 {
821     import std.array : Appender;
822     Appender!(T[]) members; // TODO: use static array instead
823     enum maxLength = T.max - T.min + 1; // possibly overestimate of final length needed
824     members.reserve(maxLength);
825     foreach (const member; __traits(allMembers, T))
826         members.put(__traits(getMember, T, member));
827     return members.data[];
828 }
829 
830 /** Dynamic Variant of $(D EnumMembers) excluding the enumerator aliases.
831  *
832  * See_Also: http://forum.dlang.org/post/ziappmtvucmuefphblse@forum.dlang.org
833  * See_Also: http://forum.dlang.org/post/awihyvzjswwayeqtklly@forum.dlang.org
834  * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org
835  * See_Also: https://issues.dlang.org/show_bug.cgi?id=10951
836  */
837 auto uniqueEnumMembers(T)() @trusted
838 if (is(T == enum))
839 {
840     import std.array : Appender;
841     Appender!(T[]) uniqueMembers;
842     enum maxLength = T.max - T.min + 1; // possibly overestimate of final length
843     uniqueMembers.reserve(maxLength);
844     enum maxBitCount = ((maxLength / (8*size_t.sizeof)) +
845                         (maxLength % (8*size_t.sizeof) ? 1 : 0));
846     size_t[maxBitCount] uniqueBits; // dense set representation of enumerators
847     foreach (const member; __traits(allMembers, T))
848     {
849         const memberEnumerator = __traits(getMember, T, member);
850         const member_ = cast(size_t)memberEnumerator;
851         import core.bitop : bt, bts;
852         if (!bt(&uniqueBits[0], member_))
853         {
854             uniqueMembers.put(memberEnumerator);
855             bts(&uniqueBits[0], member_);
856         }
857     }
858     return uniqueMembers.data[];
859 }
860 
861 ///
862 @safe pure nothrow /*@nogc*/ unittest
863 {
864     enum E { x, y, z, Z = z, Y = y }
865     import std.algorithm.comparison : equal;
866     assert(enumMembersAsEnumerators!E.equal([E.x, E.y, E.z, E.Z, E.Y])); // run-time
867     assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // run-time
868     // static assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // compile-time
869     static assert(E.x == 0);
870     static assert(E.y == 1);
871     static assert(E.z == 2);
872     static assert(E.Z == E.z);
873     static assert(E.Y == E.y);
874 }
875 
876 enum sizeOf(T) = T.sizeof;      // TODO: Move to Phobos
877 template sizesOf(T...)          // TODO: Move to Phobos
878 {
879     import std.meta : staticMap;
880     enum sizesOf = staticMap!(sizeOf, T);
881 }
882 
883 ///
884 @safe pure nothrow unittest
885 {
886     enum sizes = sizesOf!(bool, short, int, long);
887 
888     // static use
889     static assert(sizes[0] == 1);
890     static assert(sizes[1] == 2);
891     static assert(sizes[2] == 4);
892     static assert(sizes[3] == 8);
893 
894     // dynamic use
895     const i = 0;
896     assert([sizes][i] == 1);
897 }
898 
899 enum stringOf(T) = T.stringof;  // TODO: Move to Phobos
900 template stringsOf(T...)        // TODO: Move to Phobos
901 {
902     import std.meta : staticMap;
903     enum stringsOf = staticMap!(stringOf, T);
904 }
905 
906 ///
907 @safe pure nothrow @nogc unittest
908 {
909     enum strings = stringsOf!(bool, short, int, long);
910 }
911 
912 /** Get Dimensionality of Type `T`.
913    See_Also: http://forum.dlang.org/thread/hiuhqdxtpifhzwebewjh@forum.dlang.org?page=2
914 */
915 
916 template dimensionality (T)
917 {
918     import std.range.primitives : isInputRange;
919     template count_dim (uint i = 0)
920     {
921         static if (is(typeof(T.init.opSlice!i(0, 0))))
922             enum count_dim = count_dim!(i+1);
923         else static if (i == 0 &&
924                         (isInputRange!T ||
925                          is(typeof(T.init[0]))))
926             enum count_dim = 1;
927         else
928             enum count_dim = i;
929     }
930     alias dimensionality = count_dim!();
931 }
932 
933 ///
934 @safe pure nothrow @nogc unittest
935 {
936     static assert(dimensionality!(int[]) == 1);
937 }
938 
939 /// Rank of type `T`.
940 template rank(T)
941 {
942     import std.range.primitives : isInputRange;
943     static if (isInputRange!T) // is T a range?
944         enum rank = 1 + rank!(ElementType!T); // if yes, recurse
945     else
946         enum rank = 0; // base case, stop there
947 }
948 
949 ///
950 @safe pure nothrow @nogc unittest
951 {
952     import std.range : cycle;
953 
954     auto c = cycle([[0,1].s[],
955                     [2,3].s[]].s[]); // == [[0,1],[2,3],[0,1],[2,3],[0,1]...
956 
957     assert(rank!(typeof(c)) == 2); // range of ranges
958 
959     static assert(rank!(int[]) == 1);
960     static assert(rank!(int[][]) == 2);
961 }
962 
963 /// Returns: `true` iff `T` is a template instance, `false` otherwise.
964 template isTemplateInstance(T)
965 {
966     import std.traits : TemplateOf;
967     enum isTemplateInstance = is(typeof(TemplateOf!(T)));
968 }
969 
970 ///
971 @safe pure nothrow @nogc unittest
972 {
973     struct S(T) { T x; }
974     static assert(isTemplateInstance!(S!int));
975     static assert(!isTemplateInstance!(int));
976 }
977 
978 /** Get identifier (name) string of template instance `I`, or `null` if `I` is
979     not a template instance. */
980 template templateIdentifierOf(I)
981 {
982     import std.traits : TemplateOf;
983     static if (isTemplateInstance!I)
984         enum templateIdentifierOf = __traits(identifier, TemplateOf!I);
985     else
986         enum templateIdentifierOf = null;
987 }
988 alias templateNameOf = templateIdentifierOf;
989 
990 ///
991 @safe pure nothrow @nogc unittest
992 {
993     struct S(T) { T x; }
994     static assert(templateIdentifierOf!(S!int) == "S");
995     static assert(templateIdentifierOf!(int) == null);
996 }
997 
998 /** Get entropy in number of bits of `T`. */
999 template EntropyBitsOf(T)
1000 {
1001     import std.traits : isAggregateType, isArray;
1002     static if (isAggregateType!T)
1003     {
1004         // foreach (memberName; __traits(allMembers, T)) // for each member name in `struct TypedKey`
1005         // {
1006         //     const member = __traits(getMember, T.init, memberName); // member
1007         // }
1008         enum EntropyBitsOf = 8*T.sizeof;
1009     }
1010     else
1011         enum EntropyBitsOf = 8*T.sizeof;
1012 }
1013 
1014 ///
1015 @safe pure nothrow @nogc unittest
1016 {
1017     static assert(EntropyBitsOf!int == 8*int.sizeof);
1018 }
1019 
1020 /** Is `true` if `sym` is an l-value, `false` otherwise.
1021  *
1022  * See_Also: https://forum.dlang.org/post/mailman.4192.1454351296.22025.digitalmars-d-learn@puremagic.com
1023  *
1024  * TODO: Move to Phobos
1025  */
1026 enum isLvalue(alias sym) = is(typeof((ref _){}(sym)));
1027 
1028 ///
1029 @safe pure nothrow @nogc unittest
1030 {
1031     int i;
1032     string s;
1033     static assert(isLvalue!i);
1034     static assert(isLvalue!s);
1035     // static assert(!isLvalue!13);
1036     // static assert(!isLvalue!"a");
1037 }
1038 
1039 template ownsItsElements(C)
1040 {
1041     import std.traits : hasIndirections;
1042     import std.range.primitives : ElementType;
1043     enum ownsItsElements = !__traits(isCopyable, C) && !hasIndirections!(ElementType!C);
1044 }
1045 
1046 /** Copied from private definition in Phobos' std.meta.
1047  */
1048 private template isSame(ab...)
1049     if (ab.length == 2)
1050 {
1051     static if (__traits(compiles, expectType!(ab[0]),
1052                                   expectType!(ab[1])))
1053         enum isSame = is(ab[0] == ab[1]);
1054     else static if (!__traits(compiles, expectType!(ab[0])) &&
1055                     !__traits(compiles, expectType!(ab[1])) &&
1056                      __traits(compiles, expectBool!(ab[0] == ab[1])))
1057     {
1058         static if (!__traits(compiles, &ab[0]) ||
1059                    !__traits(compiles, &ab[1]))
1060             enum isSame = (ab[0] == ab[1]);
1061         else
1062             enum isSame = __traits(isSame, ab[0], ab[1]);
1063     }
1064     else
1065         enum isSame = __traits(isSame, ab[0], ab[1]);
1066 }
1067 private template expectType(T) {}
1068 private template expectBool(bool b) {}
1069 
1070 template allSatisfyIterative(alias F, T...)
1071 {
1072     static foreach (Ti; T)
1073         static if (is(typeof(allSatisfyIterative) == void) && // not yet defined
1074                    !F!(Ti))
1075             enum allSatisfyIterative = false;
1076     static if (is(typeof(allSatisfyIterative) == void)) // if not yet defined
1077         enum allSatisfyIterative = true;
1078 }
1079 
1080 ///
1081 @safe unittest
1082 {
1083     import std.traits : isIntegral;
1084 
1085     static assert( allSatisfyIterative!(isIntegral));
1086     static assert( allSatisfyIterative!(isIntegral, int));
1087     static assert(!allSatisfyIterative!(isIntegral, int, double));
1088     static assert( allSatisfyIterative!(isIntegral, int, long));
1089     static assert(!allSatisfyIterative!(isIntegral, string));
1090 }
1091 
1092 template anySatisfyIterative(alias F, T...)
1093 {
1094     static foreach (Ti; T)
1095     {
1096         static if (is(typeof(anySatisfyIterative) == void) && // not yet defined
1097                    F!(Ti))
1098         {
1099             enum anySatisfyIterative = true;
1100         }
1101     }
1102     static if (is(typeof(anySatisfyIterative) == void)) // if not yet defined
1103     {
1104         enum anySatisfyIterative = false;
1105     }
1106 }
1107 
1108 ///
1109 @safe unittest
1110 {
1111     import std.traits : isIntegral;
1112 
1113     static assert(!anySatisfyIterative!(isIntegral));
1114     static assert( anySatisfyIterative!(isIntegral, int));
1115     static assert(!anySatisfyIterative!(isIntegral, string, double));
1116     static assert( anySatisfyIterative!(isIntegral, int, double));
1117     static assert( anySatisfyIterative!(isIntegral, int, string));
1118 }
1119 
1120 version(unittest)
1121 {
1122     import std.typecons : Tuple;
1123     import nxt.array_help : s;
1124 }
1125 
1126 /** Is `true` iff `T` has a property member non-function named `name`. */
1127 template hasPropertyFunction(T, string name)
1128 {
1129     static if (__traits(hasMember, T, name))
1130         enum hasPropertyFunction = (!is(typeof(__traits(getMember, T, name)) == function) &&
1131                                     __traits(getOverloads, T, name).length);
1132     else
1133         enum hasPropertyFunction = false;
1134 }
1135 
1136 ///
1137 unittest
1138 {
1139     struct S
1140     {
1141         int m;
1142         static int sm;
1143 
1144         void f() {}
1145         static void sf() {}
1146 
1147         @property int rp() { return m; }
1148         @property void wp(int) {}
1149     }
1150 
1151     static assert(hasPropertyFunction!(S, "rp"));
1152     static assert(hasPropertyFunction!(S, "wp"));
1153 
1154     static assert(!hasPropertyFunction!(S, "na"));
1155     static assert(!hasPropertyFunction!(S, "m"));
1156     static assert(!hasPropertyFunction!(S, "sm"));
1157     static assert(!hasPropertyFunction!(S, "f"));
1158     static assert(!hasPropertyFunction!(S, "sf"));
1159 }
1160 
1161 /** Is `true` if `T.name` is a manifest constant, built-in type field, or
1162  * immutable static.
1163  */
1164 template isManifestAssignable(T, string name)
1165 {
1166     enum isManifestAssignable = is(typeof({ enum x = mixin("T." ~ name); }));
1167 }
1168 
1169 ///
1170 unittest
1171 {
1172     struct A
1173     {
1174         int m;
1175         static immutable int sim = 1;
1176         enum e = 1;
1177     }
1178     static assert(!isManifestAssignable!(A*, "na"));
1179     static assert(!isManifestAssignable!(A, "na"));
1180     static assert(!isManifestAssignable!(A, "m"));
1181     static assert(isManifestAssignable!(A, "e"));
1182     static assert(isManifestAssignable!(A, "sim"));
1183 }
1184 
1185 /** Tells you if a name is a read and/or write property
1186  *
1187  * Returns: `Tuple!(bool, "isRead", bool, "isWrite")`
1188  */
1189 auto propertySemantics(T, string name)()
1190     if (hasPropertyFunction!(T, name))
1191 {
1192     import std.typecons : tuple;
1193 
1194     enum overloads = __traits(getOverloads, T, name).length;
1195     enum canInstantiateAsField = is(typeof(mixin("T.init." ~ name)));
1196 
1197     static if (overloads > 1 || canInstantiateAsField)
1198         enum canRead = true;
1199     else
1200         enum canRead = false;
1201     static if (overloads > 1 || !canInstantiateAsField)
1202         enum canWrite = true;
1203     else
1204         enum canWrite = false;
1205 
1206     return tuple!("canRead", "canWrite")(canRead, canWrite);
1207 }
1208 
1209 ///
1210 unittest
1211 {
1212     import std.typecons;
1213 
1214     struct S
1215     {
1216         int m;
1217         @property int rp()
1218         {
1219             return m;
1220         }
1221 
1222         @property void wp(int)
1223         {
1224         }
1225 
1226         @property int rwp()
1227         {
1228             return m;
1229         }
1230 
1231         @property void rwp(int)
1232         {
1233         }
1234     }
1235 
1236     static assert(!__traits(compiles, propertySemantics!(S, "na")));
1237     static assert(!__traits(compiles, propertySemantics!(S, "m")));
1238 
1239     static assert(propertySemantics!(S, "rp") == tuple!("canRead", "canWrite")(true, false));
1240     static assert(propertySemantics!(S, "wp") == tuple!("canRead", "canWrite")(false, true));
1241     static assert(propertySemantics!(S, "rwp") == tuple!("canRead", "canWrite")(true, true));
1242 }
1243 
1244 /** Is `true` iff the postblit of `T` is disabled (`@disable this(this)`).
1245  *
1246  * See_Also: https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org
1247  */
1248 template hasDisabledPostblit(T)
1249 {
1250     static if (__traits(hasMember, T, "__postblit"))
1251         enum hasDisabledPostblit = __traits(isDisabled, T.__postblit);
1252     else
1253         enum hasDisabledPostblit = false;
1254 }
1255 
1256 ///
1257 @safe pure unittest
1258 {
1259     static struct S
1260     {
1261         @disable this(this);
1262     }
1263     static assert(!hasDisabledPostblit!int);
1264     static assert( hasDisabledPostblit!S);
1265 }
1266 
1267 template isSubclassOf(Class, BaseClass)
1268 {
1269     import std.traits : BaseClassesTuple;
1270     alias BaseClasses = BaseClassesTuple!Class;
1271     import std.meta : staticIndexOf;
1272     enum isSubclassOf = staticIndexOf!(BaseClass, BaseClasses) != -1;
1273 }
1274 
1275 ///
1276 @safe pure unittest
1277 {
1278     class X {}
1279     class Y : X {}
1280 
1281     static assert(!isSubclassOf!(X, Y));
1282     static assert( isSubclassOf!(X, Object));
1283 
1284     static assert( isSubclassOf!(Y, X));
1285     static assert( isSubclassOf!(Y, Object));
1286 }
1287 
1288 /** Is `true` iff F are all.
1289  *
1290  * See_Also: https://forum.dlang.org/post/p9orut$2h4$1@digitalmars.com
1291  */
1292 bool isStronglyPure(alias F)()
1293 {
1294     import std.traits : Parameters, ParameterDefaults;
1295 	import std.algorithm : canFind;
1296     static if (!__traits(getFunctionAttributes, F).canFind("pure")) // TODO: use staticIndexOf
1297         return false;
1298     enum length = ParameterDefaults!F.length;
1299     alias P = Parameters!F;
1300     static foreach (i; 0 .. length)
1301     {
1302         immutable c = __traits(getParameterStorageClasses, F, i);
1303         /* Why does this print:
1304            const(int) int true (d-dmd-all)
1305            const(int) int true (d-dmd-all)
1306            const(int) int true (d-dmd-all)
1307            ?
1308          */
1309         // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", const(P[i]).stringof, " ", P[i].stringof, " ", is(P[i] == const(P[i])));
1310         // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", is(int == const(int)));
1311         if (c.canFind("ref", "out") && // TODO: use staticIndexOf
1312             is(const(P[i]) == P[i]))
1313             return false;
1314     }
1315     return true;
1316 }
1317 
1318 unittest
1319 {
1320     static int f(int x) pure { return x + 1; }
1321     static int g(ref int x) pure { return x + 1; }
1322     static int gc(const ref int x) pure { return x + 1; }
1323     static int gi(immutable ref int x) pure { return x + 1; }
1324     static void h(out int x) pure { x = 0; }
1325     static assert(isStronglyPure!f);
1326     static assert(!isStronglyPure!g);
1327     static assert(!isStronglyPure!h);
1328     // TODO: static assert(isStronglyPure!gc);
1329     // TODO: static assert(isStronglyPure!gi);
1330 }
1331 
1332 /** Is `true` iff `F` returns a value whose mutable indirections are unique.
1333  *
1334  * TODO: Check if the uniqueness property only applies to the first indirection layer.
1335  * TODO: Move to Phobos std.traits.
1336  *
1337  * See_Also: https://dlang.org/changelog/2.101.0.html#dmd.fix-immutable-conv
1338  */
1339 enum returnsUnique(alias F) = __traits(compiles, {
1340 		import std.traits : Parameters;
1341 
1342 		alias P = Parameters!(F);
1343 		P args = P.init;
1344 
1345 		static if (__traits(isPOD, P)) {
1346 			auto m = F(args);
1347 			immutable i = F(args);
1348 		} else {
1349 			import core.lifetime : move;
1350 			auto m = F(move(args));
1351 			immutable i = F(move(args));
1352 		}
1353 
1354 		/*
1355 		  Doesn’t suffice to pass `P.init` here because dmd knows that passing
1356 		  `null` to `x` in
1357 
1358 		  static int* f(scope return int *x) @safe pure nothrow @nogc { return x; }
1359 
1360 		  is strongly pure.
1361 		*/
1362 		static assert(!is(typeof(m) == typeof(i)));
1363 	});
1364 
1365 alias hasUniqueReturn = returnsUnique;
1366 
1367 ///
1368 @safe pure nothrow @nogc unittest {
1369 	import core.memory : pureMalloc;
1370 
1371 	static int identity(int x)  @safe nothrow @nogc { return x; }
1372 	static int identityP(int x) @safe pure nothrow @nogc { return x; }
1373 	static assert( returnsUnique!identity);
1374 	static assert( returnsUnique!identityP);
1375 
1376 	static auto makeBytes(size_t n)  @safe nothrow @nogc { return pureMalloc(n); }
1377 	static auto makeBytesP(size_t n) @safe pure nothrow @nogc { return pureMalloc(n); }
1378 	static assert( returnsUnique!makeBytes);
1379 	static assert( returnsUnique!makeBytesP);
1380 
1381 	static int* f(scope return int *x) @safe pure nothrow @nogc { return x; }
1382 	static assert(!returnsUnique!f);
1383 
1384 	struct Sint { int x; }
1385 	static Sint fS(scope return Sint x) @safe pure nothrow @nogc { return x; }
1386 	static assert( returnsUnique!fS);
1387 
1388 	struct Sintp { int* x; }
1389 	static Sintp fSintp(scope return Sintp x) @safe pure nothrow @nogc { return x; }
1390 	static assert(!returnsUnique!fSintp);
1391 
1392 	struct S3 { immutable int* x; }
1393 	static S3 fS3(scope return S3 x) @safe pure nothrow @nogc { return x; }
1394 	static assert( returnsUnique!fS3);
1395 }