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