1 /** Various extensions to std.traits.
2 
3     Copyright: Per Nordlöw 2018-.
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, isStaticArray, 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: Add 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 enum isSink(R) = isOutputRange!(R);
391 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     import std.traits : isInstanceOf;
554     enum isA = isInstanceOf!(S, T);
555 }
556 
557 @safe pure nothrow @nogc unittest
558 {
559     import std.traits : isInstanceOf;
560     import std.range : SortedRange, assumeSorted;
561     const x = [1, 2, 3].s[].assumeSorted;
562     static assert(isInstanceOf!(SortedRange, typeof(x)));
563     static assert(isA!(SortedRange, typeof(x)));
564 }
565 
566 /** See_Also: http://forum.dlang.org/thread/bug-6384-3@http.d.puremagic.com/issues/
567     See_Also: http://forum.dlang.org/thread/jrqiiicmtpenzokfxvlz@forum.dlang.org */
568 enum isOpBinary(T, string op, U) = is(typeof(mixin("T.init" ~ op ~ "U.init")));
569 
570 enum isComparable(T) = is(typeof({ return T.init <  T.init; })); /// TODO: Move to Phobos' std.traits
571 enum isEquable   (T) = is(typeof({ return T.init == T.init; })); /// TODO: Move to Phobos' std.traits
572 enum isNotEquable(T) = is(typeof({ return T.init != T.init; })); /// TODO: Move to Phobos' std.traits
573 
574 @safe pure nothrow @nogc unittest
575 {
576     static assert(isComparable!int);
577     static assert(isComparable!string);
578     static assert(!isComparable!creal);
579     static struct Foo {}
580     static assert(!isComparable!Foo);
581     static struct Bar { bool opCmp(Bar) { return true; } }
582     static assert(isComparable!Bar);
583 }
584 
585 // TODO:  variadic
586 enum areComparable(T, U) = is(typeof({ return T.init <  U.init; })); /// TODO: Move to Phobos' std.traits
587 enum areEquable   (T, U) = is(typeof({ return T.init == U.init; })); /// TODO: Move to Phobos' std.traits
588 enum areNotEquable(T, U) = is(typeof({ return T.init != U.init; })); /// TODO: Move to Phobos' std.traits
589 
590 @safe pure nothrow @nogc unittest
591 {
592     static assert(areComparable!(int, float));
593     static assert(areEquable!(int, float));
594     static assert(areNotEquable!(int, float));
595 
596     static assert(!areComparable!(int, string));
597     static assert(!areEquable!(int, string));
598     static assert(!areNotEquable!(int, string));
599 }
600 
601 enum isValueType(T) = !hasIndirections!T;
602 // enum isValueType(T) = isScalarType!T || isStaticArray!T || isStruct!T;
603 enum hasValueSemantics(T) = !hasIndirections!T; // TODO: merge with isValueType
604 
605 enum isReferenceType(T) = hasIndirections!T;
606 
607 enum arityMin0(alias fun) = __traits(compiles, fun());
608 
609 /** TODO: Unite into a variadic.
610     See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org
611 */
612 enum isCallableWith(alias fun, T) = (is(typeof(fun(T.init))) ||
613                                      is(typeof(T.init.fun))); // TODO: Are both these needed?
614 @safe pure nothrow @nogc unittest
615 {
616     auto sqr(T)(T x) { return x*x; }
617     assert(isCallableWith!(sqr, int));
618     assert(!isCallableWith!(sqr, string));
619 }
620 
621 /* TODO: Unite into a variadic.
622    See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org
623  */
624 enum isCallableWith(alias fun, T, U) = (is(typeof(fun(T.init,
625                                                       U.init))) ||
626                                         is(typeof(T.init.fun(U)))); // TODO: Are both these needed?
627 @safe pure nothrow @nogc unittest
628 {
629     auto sqr2(T)(T x, T y) { return x*x + y*y; }
630     assert(isCallableWith!(sqr2, int, int));
631     assert(!isCallableWith!(sqr2, int, string));
632 }
633 
634 /** Check if `T` is a Sorted Range.
635     See_Also: http://forum.dlang.org/thread/lt1g3q$15fe$1@digitalmars.com
636 */
637 template isSortedRange(T)
638 {
639     import std.traits : isInstanceOf;
640     import std.range: SortedRange;
641     enum isSortedRange = isInstanceOf!(SortedRange, T); // TODO: Or use: __traits(isSame, TemplateOf!R, SortedRange)
642 }
643 
644 /** Check if Function $(D expr) is callable at compile-time.
645     See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org
646 */
647 template isCTFEable(alias fun)
648 {
649     template isCTFEable_aux(alias T)
650     {
651         enum isCTFEable_aux = T;
652     }
653     enum isCTFEable = __traits(compiles, isCTFEable_aux!(fun()));
654 }
655 
656 template isCTFEable2(fun...)
657 {
658     enum isCTFEable2 = true;
659 }
660 
661 @safe pure nothrow unittest
662 {
663     int fun1() { return 1; }
664     auto fun1_N()
665     {
666         import std.array;
667 //would return Error: gc_malloc cannot be interpreted at compile time,
668         /* because it has no available source code due to a bug */
669             return [1].array;
670     }
671     int fun2(int x)
672     {
673         return 1;
674     }
675     auto fun2_N(int x){
676         import std.array;
677 //same as fun1_N
678         return [1].array;
679     }
680 
681     int a1;
682     enum a2=0;
683 
684     static assert(!isCTFEable!(()=>a1));
685     static assert(isCTFEable!(()=>a2));
686 
687     static assert(isCTFEable!fun1);
688     /* static assert(!isCTFEable!fun1_N); */
689 
690     static assert(isCTFEable!(()=>fun2(0)));
691     /* static assert(!isCTFEable!(()=>fun2_N(0))); */
692 //NOTE:an alternate syntax which could be implemented would be: static
693     /* assert(!isCTFEable!(fun2_N,0)); */
694 }
695 
696 /** Check if the value of $(D expr) is known at compile-time.
697     See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org
698 */
699 enum isCTEable(alias expr) = __traits(compiles, { enum id = expr; });
700 
701 @safe pure nothrow @nogc unittest
702 {
703     static assert(isCTEable!11);
704     enum x = 11;
705     static assert(isCTEable!x);
706     auto y = 11;
707     static assert(!isCTEable!y);
708 }
709 
710 import std.traits: hasFunctionAttributes, isCallable, ParameterTypeTuple, Unqual;
711 
712 /** Returns $(D true) if `T` is not $(D const) or $(D immutable).
713     Note that isConst is true for string, or immutable(char)[], because the
714     'head' is mutable.
715 */
716 import std.traits : isMutable;
717 enum isConst(T) = !isMutable!T;
718 
719 @safe pure nothrow @nogc unittest
720 {
721     static assert(isConst!(const(int)));
722     static assert(!isConst!int);
723 }
724 
725 import std.traits : CommonType;
726 
727 /// Is `true` iff `Types` all share a common type.
728 enum bool haveCommonType(Types...) = !is(CommonType!Types == void);
729 
730 ///
731 @safe pure nothrow @nogc unittest
732 {
733     static assert(haveCommonType!(bool, int, long));
734     static assert(!haveCommonType!(bool, int, string));
735 }
736 
737 /** Check if $(D fun) is a pure function. */
738 enum bool isPure(alias fun) = hasFunctionAttributes!(fun, `pure`);
739 
740 /** Check if $(D fun) is a function purely callable with arguments T. */
741 enum bool isPurelyCallableWith(alias fun, T...) = (isPure!fun &&
742                                                    is(T == ParameterTypeTuple!fun));
743 
744 ///
745 @safe pure nothrow @nogc unittest
746 {
747     static int foo(int x) @safe pure nothrow { return x; }
748     static assert(isPure!foo);
749     static assert(isPurelyCallableWith!(foo, int));
750 }
751 
752 /** Check if $(D fun) is a @nogc function.
753     See_Also: http://forum.dlang.org/thread/dyumjfmxmstpgyxbozry@forum.dlang.org
754 */
755 enum bool isNogc(alias fun) = hasFunctionAttributes!(fun, `@nogc`);
756 
757 ///
758 @safe pure nothrow @nogc unittest
759 {
760     static int foo(int x) @nogc pure nothrow;
761     static int goo(int x) pure nothrow;
762     static assert(isNogc!foo);
763     static assert(!isNogc!goo);
764 }
765 
766 /** Persistently Call Function $(D fun) with arguments $(D args).
767 
768     Hash Id Build-Timestamp (Code-Id because we currently have stable way of hashing-algorithms) is Constructed from Data Structure:
769     - Hierarchically Mangled Unqual!typeof(instance)
770     - Use msgpack in combination with sha1Of or only sha1Of (with extended
771     overloads for sha1Of) if available.
772 
773     Extend std.functional : memoize to accept pure functions that takes an
774     immutable mmap as input. Create wrapper that converts file to immutable mmap
775     and performs memoization on the pure function.
776 
777 */
778 auto persistentlyMemoizedCall(alias fun, T...)(T args)
779     if (isPure!fun &&
780         isCallable!(fun, args))
781 {
782     import std.functional: memoize;
783     return fun(args);
784 }
785 
786 /** Move std.uni.newLine?
787     TODO: What to do with Windows style endings?
788     See_Also: https://en.wikipedia.org/wiki/Newline
789 */
790 bool isNewline(C)(C c) @safe pure nothrow @nogc
791     if (isSomeChar!C)
792 {
793     import std.ascii: newline; // TODO: Probably not useful.
794     static if (newline == "\n")
795         return (c == '\n' || c == '\r'); // optimized for systems with \n as default
796     else static if (newline == "\r")
797         return (c == '\r' || c == '\n'); // optimized for systems with \r as default
798     else
799         static assert(0, "Support Windows?");
800 }
801 
802 bool isNewline(S)(S s) @safe pure nothrow @nogc
803     if (isSomeString!S)
804 {
805     import std.ascii: newline; // TODO: Probably not useful.
806     static if (newline == "\n")
807         return (s == '\n' || s == '\r'); // optimized for systems with \n as default
808     else static if (newline == "\r")
809         return (s == '\r' || s == '\n'); // optimized for systems with \r as default
810     else static if (newline == "\r\n")
811         return (s == "\r\n" || s == '\r' || s == '\n'); // optimized for systems with \r\n as default
812     else static if (newline == "\n\r")
813         return (s == "\n\r" || s == '\r' || s == '\n'); // optimized for systems with \n\r as default
814     else
815         static assert(0, "Support windows?");
816 }
817 
818 /** Dynamic variant of $(D EnumMembers) returning enum member constants
819  * (enumerators) of `T`.
820  *
821  * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org
822  */
823 T[] enumMembersAsEnumerators(T)()
824 if (is(T == enum))
825 {
826     import std.array : Appender;
827     Appender!(T[]) members; // TODO: use static array instead
828     enum maxLength = T.max - T.min + 1; // possibly overestimate of final length needed
829     members.reserve(maxLength);
830     foreach (const member; __traits(allMembers, T))
831         members.put(__traits(getMember, T, member));
832     return members.data[];
833 }
834 
835 /** Dynamic Variant of $(D EnumMembers) excluding the enumerator aliases.
836  *
837  * See_Also: http://forum.dlang.org/post/ziappmtvucmuefphblse@forum.dlang.org
838  * See_Also: http://forum.dlang.org/post/awihyvzjswwayeqtklly@forum.dlang.org
839  * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org
840  * See_Also: https://issues.dlang.org/show_bug.cgi?id=10951
841  */
842 auto uniqueEnumMembers(T)() @trusted
843 if (is(T == enum))
844 {
845     import std.array : Appender;
846     Appender!(T[]) uniqueMembers;
847     enum maxLength = T.max - T.min + 1; // possibly overestimate of final length
848     uniqueMembers.reserve(maxLength);
849     enum maxBitCount = ((maxLength / (8*size_t.sizeof)) +
850                         (maxLength % (8*size_t.sizeof) ? 1 : 0));
851     size_t[maxBitCount] uniqueBits; // dense set representation of enumerators
852     foreach (const member; __traits(allMembers, T))
853     {
854         const memberEnumerator = __traits(getMember, T, member);
855         const member_ = cast(size_t)memberEnumerator;
856         import core.bitop : bt, bts;
857         if (!bt(&uniqueBits[0], member_))
858         {
859             uniqueMembers.put(memberEnumerator);
860             bts(&uniqueBits[0], member_);
861         }
862     }
863     return uniqueMembers.data[];
864 }
865 
866 ///
867 @safe pure nothrow /*@nogc*/ unittest
868 {
869     enum E { x, y, z, Z = z, Y = y }
870     import std.algorithm.comparison : equal;
871     assert(enumMembersAsEnumerators!E.equal([E.x, E.y, E.z, E.Z, E.Y])); // run-time
872     assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // run-time
873     // static assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // compile-time
874     static assert(E.x == 0);
875     static assert(E.y == 1);
876     static assert(E.z == 2);
877     static assert(E.Z == E.z);
878     static assert(E.Y == E.y);
879 }
880 
881 enum sizeOf(T) = T.sizeof;      // TODO: Add to Phobos
882 template sizesOf(T...)          // TODO: Add to Phobos
883 {
884     import std.meta : staticMap;
885     enum sizesOf = staticMap!(sizeOf, T);
886 }
887 
888 ///
889 @safe pure nothrow unittest
890 {
891     enum sizes = sizesOf!(bool, short, int, long);
892 
893     // static use
894     static assert(sizes[0] == 1);
895     static assert(sizes[1] == 2);
896     static assert(sizes[2] == 4);
897     static assert(sizes[3] == 8);
898 
899     // dynamic use
900     const i = 0;
901     assert([sizes][i] == 1);
902 }
903 
904 enum stringOf(T) = T.stringof;  // TODO: Add to Phobos
905 template stringsOf(T...)        // TODO: Add to Phobos
906 {
907     import std.meta : staticMap;
908     enum stringsOf = staticMap!(stringOf, T);
909 }
910 
911 ///
912 @safe pure nothrow @nogc unittest
913 {
914     enum strings = stringsOf!(bool, short, int, long);
915 }
916 
917 /** Get Dimensionality of Type `T`.
918    See_Also: http://forum.dlang.org/thread/hiuhqdxtpifhzwebewjh@forum.dlang.org?page=2
919 */
920 
921 template dimensionality (T)
922 {
923     import std.range.primitives : isInputRange;
924     template count_dim (uint i = 0)
925     {
926         static if (is(typeof(T.init.opSlice!i(0, 0))))
927             enum count_dim = count_dim!(i+1);
928         else static if (i == 0 &&
929                         (isInputRange!T ||
930                          is(typeof(T.init[0]))))
931             enum count_dim = 1;
932         else
933             enum count_dim = i;
934     }
935     alias dimensionality = count_dim!();
936 }
937 
938 ///
939 @safe pure nothrow @nogc unittest
940 {
941     static assert(dimensionality!(int[]) == 1);
942 }
943 
944 /// Rank of type `T`.
945 template rank(T)
946 {
947     import std.range.primitives : isInputRange;
948     static if (isInputRange!T) // is T a range?
949         enum rank = 1 + rank!(ElementType!T); // if yes, recurse
950     else
951         enum rank = 0; // base case, stop there
952 }
953 
954 ///
955 @safe pure nothrow @nogc unittest
956 {
957     import std.range : cycle;
958 
959     auto c = cycle([[0,1].s[],
960                     [2,3].s[]].s[]); // == [[0,1],[2,3],[0,1],[2,3],[0,1]...
961 
962     assert(rank!(typeof(c)) == 2); // range of ranges
963 
964     static assert(rank!(int[]) == 1);
965     static assert(rank!(int[][]) == 2);
966 }
967 
968 /// Returns: `true` iff `T` is a template instance, `false` otherwise.
969 template isTemplateInstance(T)
970 {
971     import std.traits : TemplateOf;
972     enum isTemplateInstance = is(typeof(TemplateOf!(T)));
973 }
974 
975 ///
976 @safe pure nothrow @nogc unittest
977 {
978     struct S(T) { T x; }
979     static assert(isTemplateInstance!(S!int));
980     static assert(!isTemplateInstance!(int));
981 }
982 
983 /** Get identifier (name) string of template instance `I`, or `null` if `I` is
984     not a template instance. */
985 template templateIdentifierOf(I)
986 {
987     import std.traits : TemplateOf;
988     static if (isTemplateInstance!I)
989         enum templateIdentifierOf = __traits(identifier, TemplateOf!I);
990     else
991         enum templateIdentifierOf = null;
992 }
993 alias templateNameOf = templateIdentifierOf;
994 
995 ///
996 @safe pure nothrow @nogc unittest
997 {
998     struct S(T) { T x; }
999     static assert(templateIdentifierOf!(S!int) == "S");
1000     static assert(templateIdentifierOf!(int) == null);
1001 }
1002 
1003 /** Get entropy in number of bits of `T`. */
1004 template EntropyBitsOf(T)
1005 {
1006     import std.traits : isAggregateType, isArray;
1007     static if (isAggregateType!T)
1008     {
1009         // foreach (memberName; __traits(allMembers, T)) // for each member name in `struct TypedKey`
1010         // {
1011         //     const member = __traits(getMember, T.init, memberName); // member
1012         // }
1013         enum EntropyBitsOf = 8*T.sizeof;
1014     }
1015     else
1016         enum EntropyBitsOf = 8*T.sizeof;
1017 }
1018 
1019 ///
1020 @safe pure nothrow @nogc unittest
1021 {
1022     static assert(EntropyBitsOf!int == 8*int.sizeof);
1023 }
1024 
1025 /** Is `true` if `sym` is an l-value, `false` otherwise.
1026  *
1027  * See_Also: https://forum.dlang.org/post/mailman.4192.1454351296.22025.digitalmars-d-learn@puremagic.com
1028  *
1029  * TODO: Add to Phobos
1030  */
1031 enum isLvalue(alias sym) = is(typeof((ref _){}(sym)));
1032 
1033 ///
1034 @safe pure nothrow @nogc unittest
1035 {
1036     int i;
1037     string s;
1038     static assert(isLvalue!i);
1039     static assert(isLvalue!s);
1040     // static assert(!isLvalue!13);
1041     // static assert(!isLvalue!"a");
1042 }
1043 
1044 template ownsItsElements(C)
1045 {
1046     import std.traits : hasIndirections;
1047     import std.range.primitives : ElementType;
1048     enum ownsItsElements = !__traits(isCopyable, C) && !hasIndirections!(ElementType!C);
1049 }
1050 
1051 /** Copied from private definition in Phobos' std.meta.
1052  */
1053 private template isSame(ab...)
1054     if (ab.length == 2)
1055 {
1056     static if (__traits(compiles, expectType!(ab[0]),
1057                                   expectType!(ab[1])))
1058         enum isSame = is(ab[0] == ab[1]);
1059     else static if (!__traits(compiles, expectType!(ab[0])) &&
1060                     !__traits(compiles, expectType!(ab[1])) &&
1061                      __traits(compiles, expectBool!(ab[0] == ab[1])))
1062     {
1063         static if (!__traits(compiles, &ab[0]) ||
1064                    !__traits(compiles, &ab[1]))
1065             enum isSame = (ab[0] == ab[1]);
1066         else
1067             enum isSame = __traits(isSame, ab[0], ab[1]);
1068     }
1069     else
1070         enum isSame = __traits(isSame, ab[0], ab[1]);
1071 }
1072 private template expectType(T) {}
1073 private template expectBool(bool b) {}
1074 
1075 template allSatisfyIterative(alias F, T...)
1076 {
1077     static foreach (Ti; T)
1078         static if (is(typeof(allSatisfyIterative) == void) && // not yet defined
1079                    !F!(Ti))
1080             enum allSatisfyIterative = false;
1081     static if (is(typeof(allSatisfyIterative) == void)) // if not yet defined
1082         enum allSatisfyIterative = true;
1083 }
1084 
1085 ///
1086 @safe unittest
1087 {
1088     import std.traits : isIntegral;
1089 
1090     static assert( allSatisfyIterative!(isIntegral));
1091     static assert( allSatisfyIterative!(isIntegral, int));
1092     static assert(!allSatisfyIterative!(isIntegral, int, double));
1093     static assert( allSatisfyIterative!(isIntegral, int, long));
1094     static assert(!allSatisfyIterative!(isIntegral, string));
1095 }
1096 
1097 template anySatisfyIterative(alias F, T...)
1098 {
1099     static foreach (Ti; T)
1100     {
1101         static if (is(typeof(anySatisfyIterative) == void) && // not yet defined
1102                    F!(Ti))
1103         {
1104             enum anySatisfyIterative = true;
1105         }
1106     }
1107     static if (is(typeof(anySatisfyIterative) == void)) // if not yet defined
1108     {
1109         enum anySatisfyIterative = false;
1110     }
1111 }
1112 
1113 ///
1114 @safe unittest
1115 {
1116     import std.traits : isIntegral;
1117 
1118     static assert(!anySatisfyIterative!(isIntegral));
1119     static assert( anySatisfyIterative!(isIntegral, int));
1120     static assert(!anySatisfyIterative!(isIntegral, string, double));
1121     static assert( anySatisfyIterative!(isIntegral, int, double));
1122     static assert( anySatisfyIterative!(isIntegral, int, string));
1123 }
1124 
1125 version(unittest)
1126 {
1127     import std.typecons : Tuple;
1128     import nxt.array_help : s;
1129 }
1130 
1131 /** Is `true` iff `T` has a property member non-function named `name`. */
1132 template hasPropertyFunction(T, string name)
1133 {
1134     static if (__traits(hasMember, T, name))
1135         enum hasPropertyFunction = (!is(typeof(__traits(getMember, T, name)) == function) &&
1136                                     __traits(getOverloads, T, name).length);
1137     else
1138         enum hasPropertyFunction = false;
1139 }
1140 
1141 ///
1142 unittest
1143 {
1144     struct S
1145     {
1146         int m;
1147         static int sm;
1148 
1149         void f() {}
1150         static void sf() {}
1151 
1152         @property int rp() { return m; }
1153         @property void wp(int) {}
1154     }
1155 
1156     static assert(hasPropertyFunction!(S, "rp"));
1157     static assert(hasPropertyFunction!(S, "wp"));
1158 
1159     static assert(!hasPropertyFunction!(S, "na"));
1160     static assert(!hasPropertyFunction!(S, "m"));
1161     static assert(!hasPropertyFunction!(S, "sm"));
1162     static assert(!hasPropertyFunction!(S, "f"));
1163     static assert(!hasPropertyFunction!(S, "sf"));
1164 }
1165 
1166 /** Is `true` if `T.name` is a manifest constant, built-in type field, or
1167  * immutable static.
1168  */
1169 template isManifestAssignable(T, string name)
1170 {
1171     enum isManifestAssignable = is(typeof({ enum x = mixin("T." ~ name); }));
1172 }
1173 
1174 ///
1175 unittest
1176 {
1177     struct A
1178     {
1179         int m;
1180         static immutable int sim = 1;
1181         enum e = 1;
1182     }
1183     static assert(!isManifestAssignable!(A*, "na"));
1184     static assert(!isManifestAssignable!(A, "na"));
1185     static assert(!isManifestAssignable!(A, "m"));
1186     static assert(isManifestAssignable!(A, "e"));
1187     static assert(isManifestAssignable!(A, "sim"));
1188 }
1189 
1190 /** Tells you if a name is a read and/or write property
1191  *
1192  * Returns: `Tuple!(bool, "isRead", bool, "isWrite")`
1193  */
1194 auto propertySemantics(T, string name)()
1195     if (hasPropertyFunction!(T, name))
1196 {
1197     import std.typecons : tuple;
1198 
1199     enum overloads = __traits(getOverloads, T, name).length;
1200     enum canInstantiateAsField = is(typeof(mixin("T.init." ~ name)));
1201 
1202     static if (overloads > 1 || canInstantiateAsField)
1203         enum canRead = true;
1204     else
1205         enum canRead = false;
1206     static if (overloads > 1 || !canInstantiateAsField)
1207         enum canWrite = true;
1208     else
1209         enum canWrite = false;
1210 
1211     return tuple!("canRead", "canWrite")(canRead, canWrite);
1212 }
1213 
1214 ///
1215 unittest
1216 {
1217     import std.typecons;
1218 
1219     struct S
1220     {
1221         int m;
1222         @property int rp()
1223         {
1224             return m;
1225         }
1226 
1227         @property void wp(int)
1228         {
1229         }
1230 
1231         @property int rwp()
1232         {
1233             return m;
1234         }
1235 
1236         @property void rwp(int)
1237         {
1238         }
1239     }
1240 
1241     static assert(!__traits(compiles, propertySemantics!(S, "na")));
1242     static assert(!__traits(compiles, propertySemantics!(S, "m")));
1243 
1244     static assert(propertySemantics!(S, "rp") == tuple!("canRead", "canWrite")(true, false));
1245     static assert(propertySemantics!(S, "wp") == tuple!("canRead", "canWrite")(false, true));
1246     static assert(propertySemantics!(S, "rwp") == tuple!("canRead", "canWrite")(true, true));
1247 }
1248 
1249 /** Is `true` iff the postblit of `T` is disabled (`@disable this(this)`).
1250  *
1251  * See_Also: https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org
1252  */
1253 template hasDisabledPostblit(T)
1254 {
1255     static if (__traits(hasMember, T, "__postblit"))
1256         enum hasDisabledPostblit = __traits(isDisabled, T.__postblit);
1257     else
1258         enum hasDisabledPostblit = false;
1259 }
1260 
1261 ///
1262 @safe pure unittest
1263 {
1264     static struct S
1265     {
1266         @disable this(this);
1267     }
1268     static assert(!hasDisabledPostblit!int);
1269     static assert( hasDisabledPostblit!S);
1270 }
1271 
1272 template isSubclassOf(Class, BaseClass)
1273 {
1274     import std.traits : BaseClassesTuple;
1275     alias BaseClasses = BaseClassesTuple!Class;
1276     import std.meta : staticIndexOf;
1277     enum isSubclassOf = staticIndexOf!(BaseClass, BaseClasses) != -1;
1278 }
1279 
1280 ///
1281 @safe pure unittest
1282 {
1283     class X {}
1284     class Y : X {}
1285 
1286     static assert(!isSubclassOf!(X, Y));
1287     static assert( isSubclassOf!(X, Object));
1288 
1289     static assert( isSubclassOf!(Y, X));
1290     static assert( isSubclassOf!(Y, Object));
1291 }
1292 
1293 /** Is `true` iff F are all
1294  *
1295  * See_Also: https://forum.dlang.org/post/p9orut$2h4$1@digitalmars.com
1296  */
1297 bool isStronglyPure(alias F)()
1298 {
1299     import std.traits : Parameters, ParameterDefaults;
1300 	import std.algorithm : canFind;
1301     static if (!__traits(getFunctionAttributes, F).canFind("pure")) // TODO: use staticIndexOf
1302         return false;
1303     enum length = ParameterDefaults!F.length;
1304     alias P = Parameters!F;
1305     static foreach (i; 0 .. length)
1306     {
1307         immutable c = __traits(getParameterStorageClasses, F, i);
1308         /* Why does this print:
1309            const(int) int true (d-dmd-all)
1310            const(int) int true (d-dmd-all)
1311            const(int) int true (d-dmd-all)
1312            ?
1313          */
1314         // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", const(P[i]).stringof, " ", P[i].stringof, " ", is(P[i] == const(P[i])));
1315         // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", is(int == const(int)));
1316         if (c.canFind("ref", "out") && // TODO: use staticIndexOf
1317             is(const(P[i]) == P[i]))
1318             return false;
1319     }
1320     return true;
1321 }
1322 
1323 unittest
1324 {
1325     static int f(int x) pure { return x + 1; }
1326     static int g(ref int x) pure { return x + 1; }
1327     static int gc(const ref int x) pure { return x + 1; }
1328     static int gi(immutable ref int x) pure { return x + 1; }
1329     static void h(out int x) pure { x = 0; }
1330     static assert(isStronglyPure!f);
1331     static assert(!isStronglyPure!g);
1332     static assert(!isStronglyPure!h);
1333     // TODO: static assert(isStronglyPure!gc);
1334     // TODO: static assert(isStronglyPure!gi);
1335 }