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