1 module mir.internal.meta;
2 
3 template memberTypeOf(T, string member)
4 {
5     T* aggregate;
6     alias memberTypeOf = typeof(__traits(getMember, aggregate, member));
7 }
8 
9 template isMemberType(T, string member)
10 {
11     enum isMemberType = is(typeof((ref __traits(getMember, T, member) v){})) || is(__traits(getMember, T, member) : void);
12 }
13 
14 template isSingleMember(T, string member)
15 {
16     import std.meta: AliasSeq;
17     enum isSingleMember = AliasSeq!(__traits(getMember, T, member)).length == 1;
18 }
19 
20 template AllMembersRec(T)
21 {
22     static if (is(T == class) || is(T == struct) || is(T == union) || is(T == interface))
23     {
24         static if (__traits(getAliasThis, T).length)
25         {
26             T* aggregate;
27             static if (is(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)))))
28             {
29                 import std.meta: Filter, AliasSeq;
30                 alias baseMembers = AllMembersRec!(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))));
31                 alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T));
32                 alias AllMembersRec = NoDuplicates!(AliasSeq!(baseMembers, members));
33             }
34             else
35             {
36                 alias AllMembersRec = __traits(allMembers, T);
37             }
38         }
39         else
40         {
41             alias AllMembersRec = __traits(allMembers, T);
42         }
43     }
44     else
45     {
46         import std.meta: AliasSeq;
47         alias AllMembersRec = AliasSeq!();
48     }
49 }
50 
51 alias ConstOf(T) = const T;
52 enum Alignof(T) = T.alignof;
53 enum canConstructWith(From, To) = __traits(compiles, (From a) { To b = a; } );
54 enum canImplicitlyRemoveConst(T) = __traits(compiles, {static T _function_(ref const T a) { return a; }} );
55 enum canRemoveConst(T) = canConstructWith!(const T, T);
56 enum canRemoveImmutable(T) = canConstructWith!(immutable T, T);
57 enum hasOpPostMove(T) = __traits(hasMember, T, "opPostMove");
58 enum hasToHash(T) = __traits(hasMember, T, "toHash");
59 static if (__VERSION__ < 2094)
60     enum isCopyable(S) = is(typeof({ S foo = S.init; S copy = foo; }));
61 else
62     enum isCopyable(S) = __traits(isCopyable, S); 
63 enum isPOD(T) = __traits(isPOD, T);
64 enum Sizeof(T) = T.sizeof;
65 
66 enum hasInoutConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope inout S rhs) inout { this.a = rhs.a; } }} );
67 enum hasConstConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) const { this.a = rhs.a; } }} );
68 enum hasImmutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope immutable S rhs) immutable { this.a = rhs.a; } }} );
69 enum hasMutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope S rhs) { this.a = rhs.a; } }} );
70 enum hasSemiImmutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) immutable { this.a = rhs.a; } }} );
71 enum hasSemiMutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) { this.a = rhs.a; } }} );
72 
73 @safe version(mir_core_test) unittest
74 {
75     static struct S { this(ref return scope inout S) inout {} }
76     static inout(S) _function_(ref inout S a) { return a; }
77     static struct C2 { uint* a; this(ref return scope const S) const {} }
78     static assert(hasInoutConstruction!uint);
79     static assert(hasInoutConstruction!(immutable(uint)[]));
80     static assert(hasInoutConstruction!(typeof(null)));
81     static assert(hasInoutConstruction!S);
82 }
83 
84 template staticIsSorted(alias cmp, Seq...)
85 {
86     static if (Seq.length <= 1)
87         enum staticIsSorted = true;
88     else static if (Seq.length == 2)
89         enum staticIsSorted = cmp!(Seq[0], Seq[1]);
90     else
91     {
92         enum staticIsSorted =
93             cmp!(Seq[($ / 2) - 1], Seq[$ / 2]) &&
94             staticIsSorted!(cmp, Seq[0 .. $ / 2]) &&
95             staticIsSorted!(cmp, Seq[$ / 2 .. $]);
96     }
97 }
98 
99 template TryRemoveConst(T)
100 {
101     import std.traits: Unqual;
102     alias U = Unqual!T;
103     static if (canImplicitlyRemoveConst!U)
104     {
105         alias TryRemoveConst = U;
106     }
107     else
108     {
109         alias TryRemoveConst = T;
110     }
111 }
112 
113 
114 template TypeCmp(A, B)
115 {
116     enum bool TypeCmp = is(A == B) ? false:
117     is(A == typeof(null)) ? true:
118     is(B == typeof(null)) ? false:
119     is(A == void) ? true:
120     is(B == void) ? false:
121     A.sizeof < B.sizeof ? true:
122     A.sizeof > B.sizeof ? false:
123     A.mangleof < B.mangleof;
124 }
125 
126 template isInstanceOf(alias S)
127 {
128     enum isInstanceOf(T) = is(T == S!Args, Args...);
129 }
130 
131 version(mir_core_test) unittest
132 {
133     static assert(is(TryRemoveConst!(const int) == int));
134 }
135 
136 
137 // taken from std.meta.allSatisfy
138 template allSatisfy(alias F, T...)
139 {
140     static foreach (Ti; T)
141     {
142         static if (!is(typeof(allSatisfy) == bool) && // not yet defined
143                    !F!(Ti))
144         {
145             enum allSatisfy = false;
146         }
147     }
148     static if (!is(typeof(allSatisfy) == bool)) // if not yet defined
149     {
150         enum allSatisfy = true;
151     }
152 }
153 
154 template Erase(T, TList...)
155 {
156     alias Erase = GenericErase!(T, TList).result;
157 }
158 
159 template Erase(alias T, TList...)
160 {
161     alias Erase = GenericErase!(T, TList).result;
162 }
163 
164 template GenericErase(args...)
165 if (args.length >= 1)
166 {
167     import std.meta: AliasSeq;
168 
169     alias e     = OldAlias!(args[0]);
170     alias tuple = args[1 .. $] ;
171 
172     static if (tuple.length)
173     {
174         alias head = OldAlias!(tuple[0]);
175         alias tail = tuple[1 .. $];
176 
177         static if (isSame!(e, head))
178             alias result = tail;
179         else
180             alias result = AliasSeq!(head, GenericErase!(e, tail).result);
181     }
182     else
183     {
184         alias result = AliasSeq!();
185     }
186 }
187 
188 template Pack(T...)
189 {
190     alias Expand = T;
191     enum equals(U...) = isSame!(Pack!T, Pack!U);
192 }
193 
194 
195 template EraseAll(T, TList...)
196 {
197     alias EraseAll = GenericEraseAll!(T, TList).result;
198 }
199 
200 template EraseAll(alias T, TList...)
201 {
202     alias EraseAll = GenericEraseAll!(T, TList).result;
203 }
204 
205 template GenericEraseAll(args...)
206 if (args.length >= 1)
207 {
208     import std.meta: AliasSeq;
209 
210     alias e     = OldAlias!(args[0]);
211     alias tuple = args[1 .. $];
212 
213     static if (tuple.length)
214     {
215         alias head = OldAlias!(tuple[0]);
216         alias tail = tuple[1 .. $];
217         alias next = AliasSeq!(
218             GenericEraseAll!(e, tail[0..$/2]).result,
219             GenericEraseAll!(e, tail[$/2..$]).result
220             );
221 
222         static if (isSame!(e, head))
223             alias result = next;
224         else
225             alias result = AliasSeq!(head, next);
226     }
227     else
228     {
229         alias result = AliasSeq!();
230     }
231 }
232 
233 template OldAlias(T)
234 {
235     alias OldAlias = T;
236 }
237 
238 template OldAlias(alias T)
239 {
240     alias OldAlias = T;
241 }
242 
243 template EraseAllN(uint N, TList...)
244 {
245     static if (N == 1)
246     {
247         alias EraseAllN = EraseAll!(TList[0], TList[1 .. $]);
248     }
249     else
250     {
251         static if (N & 1)
252             alias EraseAllN = EraseAllN!(N / 2, TList[N / 2 + 1 .. N],
253                     EraseAllN!(N / 2 + 1, TList[0 .. N / 2 + 1], TList[N .. $]));
254         else
255             alias EraseAllN = EraseAllN!(N / 2, TList[N / 2 .. N],
256                     EraseAllN!(N / 2, TList[0 .. N / 2], TList[N .. $]));
257     }
258 }
259 
260 template NoDuplicates(TList...)
261 {
262     static if (TList.length >= 2)
263     {
264         import std.meta: AliasSeq;
265 
266         alias fst = NoDuplicates!(TList[0 .. $/2]);
267         alias snd = NoDuplicates!(TList[$/2 .. $]);
268         alias NoDuplicates = AliasSeq!(fst, EraseAllN!(fst.length, fst, snd));
269     }
270     else
271     {
272         alias NoDuplicates = TList;
273     }
274 }
275 
276 
277 template isSame(ab...)
278 if (ab.length == 2)
279 {
280     static if (is(ab[0]) && is(ab[1]))
281     {
282         enum isSame = is(ab[0] == ab[1]);
283     }
284     else static if (!is(ab[0]) && !is(ab[1]) &&
285                     !(is(typeof(&ab[0])) && is(typeof(&ab[1]))) &&
286                      __traits(compiles, { enum isSame = ab[0] == ab[1]; }))
287     {
288         enum isSame = (ab[0] == ab[1]);
289     }
290     else
291     {
292         enum isSame = __traits(isSame, ab[0], ab[1]);
293     }
294 }
295 
296 template Mod(From, To)
297 {
298     template Mod(T)
299     {
300         static if (is(T == From))
301             alias Mod = To;
302         else
303             alias Mod = T;
304     }
305 }
306 
307 template Replace(From, To, T...)
308 {
309     import std.meta: staticMap;
310     alias Replace = staticMap!(Mod!(From, To), T);
311 }
312 
313 template ReplaceTypeUnless(alias pred, From, To, T...)
314 {
315     static if (T.length == 1)
316     {
317         import std.meta: staticMap;
318         static if (pred!(T[0]))
319             alias ReplaceTypeUnless = T[0];
320         else static if (is(T[0] == From))
321             alias ReplaceTypeUnless = To;
322         else static if (is(T[0] == const(U), U))
323             alias ReplaceTypeUnless = const(ReplaceTypeUnless!(pred, From, To, U));
324         else static if (is(T[0] == immutable(U), U))
325             alias ReplaceTypeUnless = immutable(ReplaceTypeUnless!(pred, From, To, U));
326         else static if (is(T[0] == shared(U), U))
327             alias ReplaceTypeUnless = shared(ReplaceTypeUnless!(pred, From, To, U));
328         else static if (is(T[0] == U*, U))
329         {
330             static if (is(U == function))
331                 alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]);
332             else
333                 alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)*;
334         }
335         else static if (is(T[0] == delegate))
336         {
337             alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]);
338         }
339         else static if (is(T[0] == function))
340         {
341             static assert(0, "Function types not supported," ~
342                 " use a function pointer type instead of " ~ T[0].stringof);
343         }
344         else static if (is(T[0] == U!V, alias U, V...))
345         {
346             template replaceTemplateArgs(T...)
347             {
348                 static if (is(typeof(T[0])))
349                     static if (__traits(compiles, {alias replaceTemplateArgs = T[0];}))
350                         alias replaceTemplateArgs = T[0];
351                     else
352                         enum replaceTemplateArgs = T[0];
353                 else
354                     alias replaceTemplateArgs = ReplaceTypeUnless!(pred, From, To, T[0]);
355             }
356             alias ReplaceTypeUnless = U!(staticMap!(replaceTemplateArgs, V));
357         }
358         else static if (is(T[0] == struct))
359             // don't match with alias this struct below
360             // https://issues.dlang.org/show_bug.cgi?id=15168
361             alias ReplaceTypeUnless = T[0];
362         else static if (is(T[0] == enum))
363             alias ReplaceTypeUnless = T[0];
364         else static if (is(T[0] == U[], U))
365             alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[];
366         else static if (is(T[0] == U[n], U, size_t n))
367             alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[n];
368         else static if (is(T[0] == U[V], U, V))
369             alias ReplaceTypeUnless =
370                 ReplaceTypeUnless!(pred, From, To, U)[ReplaceTypeUnless!(pred, From, To, V)];
371         else
372             alias ReplaceTypeUnless = T[0];
373     }
374     else static if (T.length > 1)
375     {
376         import std.meta: AliasSeq;
377         alias ReplaceTypeUnless = AliasSeq!(ReplaceTypeUnless!(pred, From, To, T[0]),
378             ReplaceTypeUnless!(pred, From, To, T[1 .. $]));
379     }
380     else
381     {
382         import std.meta: AliasSeq;
383         alias ReplaceTypeUnless = AliasSeq!();
384     }
385 }
386 
387 @safe version(mir_core_test) unittest
388 {
389     import std.typecons: Tuple;
390     import std.traits : isArray;
391     static assert(
392         is(ReplaceTypeUnless!(isArray, int, string, int*) == string*) &&
393         is(ReplaceTypeUnless!(isArray, int, string, int[]) == int[]) &&
394         is(ReplaceTypeUnless!(isArray, int, string, Tuple!(int, int[]))
395             == Tuple!(string, int[]))
396    );
397 }
398 
399 template Contains(Types...)
400 {
401     import std.meta: staticIndexOf;
402     enum Contains(T) = staticIndexOf!(T, Types) >= 0;
403 }
404 
405 template replaceTypeInFunctionTypeUnless(alias pred, From, To, fun)
406 {
407     import std.meta;
408     import std.traits;
409     alias RX = ReplaceTypeUnless!(pred, From, To, ReturnType!fun);
410     alias PX = AliasSeq!(ReplaceTypeUnless!(pred, From, To, Parameters!fun));
411     // Wrapping with AliasSeq is neccesary because ReplaceType doesn't return
412     // tuple if Parameters!fun.length == 1
413     string gen()
414     {
415         enum  linkage = functionLinkage!fun;
416         alias attributes = functionAttributes!fun;
417         enum  variadicStyle = variadicFunctionStyle!fun;
418         alias storageClasses = ParameterStorageClassTuple!fun;
419         string result;
420         result ~= "extern(" ~ linkage ~ ") ";
421         static if (attributes & FunctionAttribute.ref_)
422         {
423             result ~= "ref ";
424         }
425         result ~= "RX";
426         static if (is(fun == delegate))
427             result ~= " delegate";
428         else
429             result ~= " function";
430         result ~= "(";
431         static foreach (i; 0 .. PX.length)
432         {
433             if (i)
434                 result ~= ", ";
435             if (storageClasses[i] & ParameterStorageClass.scope_)
436                 result ~= "scope ";
437             if (storageClasses[i] & ParameterStorageClass.out_)
438                 result ~= "out ";
439             if (storageClasses[i] & ParameterStorageClass.ref_)
440                 result ~= "ref ";
441             if (storageClasses[i] & ParameterStorageClass.lazy_)
442                 result ~= "lazy ";
443             if (storageClasses[i] & ParameterStorageClass.return_)
444                 result ~= "return ";
445             result ~= "PX[" ~ i.stringof ~ "]";
446         }
447         static if (variadicStyle == Variadic.typesafe)
448             result ~= " ...";
449         else static if (variadicStyle != Variadic.no)
450             result ~= ", ...";
451         result ~= ")";
452         static if (attributes & FunctionAttribute.pure_)
453             result ~= " pure";
454         static if (attributes & FunctionAttribute.nothrow_)
455             result ~= " nothrow";
456         static if (attributes & FunctionAttribute.property)
457             result ~= " @property";
458         static if (attributes & FunctionAttribute.trusted)
459             result ~= " @trusted";
460         static if (attributes & FunctionAttribute.safe)
461             result ~= " @safe";
462         static if (attributes & FunctionAttribute.nogc)
463             result ~= " @nogc";
464         static if (attributes & FunctionAttribute.system)
465             result ~= " @system";
466         static if (attributes & FunctionAttribute.const_)
467             result ~= " const";
468         static if (attributes & FunctionAttribute.immutable_)
469             result ~= " immutable";
470         static if (attributes & FunctionAttribute.inout_)
471             result ~= " inout";
472         static if (attributes & FunctionAttribute.shared_)
473             result ~= " shared";
474         static if (attributes & FunctionAttribute.return_)
475             result ~= " return";
476         return result;
477     }
478     mixin("alias replaceTypeInFunctionTypeUnless = " ~ gen() ~ ";");
479 }
480 
481 enum false_(T) = false;
482 
483 alias ReplaceType(From, To, T...) = ReplaceTypeUnless!(false_, From, To, T);
484 
485 version(mir_core_test) @safe unittest
486 {
487     import std.typecons: Unique, Tuple;
488     template Test(Ts...)
489     {
490         static if (Ts.length)
491         {
492             //pragma(msg, "Testing: ReplaceType!("~Ts[0].stringof~", "
493             //    ~Ts[1].stringof~", "~Ts[2].stringof~")");
494             static assert(is(ReplaceType!(Ts[0], Ts[1], Ts[2]) == Ts[3]),
495                 "ReplaceType!("~Ts[0].stringof~", "~Ts[1].stringof~", "
496                     ~Ts[2].stringof~") == "
497                     ~ReplaceType!(Ts[0], Ts[1], Ts[2]).stringof);
498             alias Test = Test!(Ts[4 .. $]);
499         }
500         else alias Test = void;
501     }
502     //import core.stdc.stdio;
503     alias RefFun1 = ref int function(float, long);
504     alias RefFun2 = ref float function(float, long);
505     extern(C) int printf(const char*, ...) nothrow @nogc @system;
506     extern(C) float floatPrintf(const char*, ...) nothrow @nogc @system;
507     int func(float);
508     int x;
509     struct S1 { void foo() { x = 1; } }
510     struct S2 { void bar() { x = 2; } }
511     alias Pass = Test!(
512         int, float, typeof(&func), float delegate(float),
513         int, float, typeof(&printf), typeof(&floatPrintf),
514         int, float, int function(out long, ...),
515             float function(out long, ...),
516         int, float, int function(ref float, long),
517             float function(ref float, long),
518         int, float, int function(ref int, long),
519             float function(ref float, long),
520         int, float, int function(out int, long),
521             float function(out float, long),
522         int, float, int function(lazy int, long),
523             float function(lazy float, long),
524         int, float, int function(out long, ref const int),
525             float function(out long, ref const float),
526         int, int, int, int,
527         int, float, int, float,
528         int, float, const int, const float,
529         int, float, immutable int, immutable float,
530         int, float, shared int, shared float,
531         int, float, int*, float*,
532         int, float, const(int)*, const(float)*,
533         int, float, const(int*), const(float*),
534         const(int)*, float, const(int*), const(float),
535         int*, float, const(int)*, const(int)*,
536         int, float, int[], float[],
537         int, float, int[42], float[42],
538         int, float, const(int)[42], const(float)[42],
539         int, float, const(int[42]), const(float[42]),
540         int, float, int[int], float[float],
541         int, float, int[double], float[double],
542         int, float, double[int], double[float],
543         int, float, int function(float, long), float function(float, long),
544         int, float, int function(float), float function(float),
545         int, float, int function(float, int), float function(float, float),
546         int, float, int delegate(float, long), float delegate(float, long),
547         int, float, int delegate(float), float delegate(float),
548         int, float, int delegate(float, int), float delegate(float, float),
549         int, float, Unique!int, Unique!float,
550         int, float, Tuple!(float, int), Tuple!(float, float),
551         int, float, RefFun1, RefFun2,
552         S1, S2,
553             S1[1][][S1]* function(),
554             S2[1][][S2]* function(),
555         int, string,
556                int[3] function(   int[] arr,    int[2] ...) pure @trusted,
557             string[3] function(string[] arr, string[2] ...) pure @trusted,
558     );
559     // https://issues.dlang.org/show_bug.cgi?id=15168
560     static struct T1 { string s; alias s this; }
561     static struct T2 { char[10] s; alias s this; }
562     static struct T3 { string[string] s; alias s this; }
563     alias Pass2 = Test!(
564         ubyte, ubyte, T1, T1,
565         ubyte, ubyte, T2, T2,
566         ubyte, ubyte, T3, T3,
567     );
568 }
569 // https://issues.dlang.org/show_bug.cgi?id=17116
570 version(mir_core_test) @safe unittest
571 {
572     alias ConstDg = void delegate(float) const;
573     alias B = void delegate(int) const;
574     alias A = ReplaceType!(float, int, ConstDg);
575     static assert(is(B == A));
576 }
577  // https://issues.dlang.org/show_bug.cgi?id=19696
578 version(mir_core_test) @safe unittest
579 {
580     static struct T(U) {}
581     static struct S { T!int t; alias t this; }
582     static assert(is(ReplaceType!(float, float, S) == S));
583 }
584  // https://issues.dlang.org/show_bug.cgi?id=19697
585 version(mir_core_test) @safe unittest
586 {
587     class D(T) {}
588     class C : D!C {}
589     static assert(is(ReplaceType!(float, float, C)));
590 }
591 // https://issues.dlang.org/show_bug.cgi?id=16132
592 version(mir_core_test) @safe unittest
593 {
594     interface I(T) {}
595     class C : I!int {}
596     static assert(is(ReplaceType!(int, string, C) == C));
597 }