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