1 /// Replace a type with an "equivalent" type without constructors, destructors, and mutable.
2 module rebindable.DeepUnqual;
3 
4 import std.meta;
5 import std.traits;
6 import std.typecons;
7 
8 /**
9  * `DeepUnqual!T` is a data type that is equivalent to `T` in terms of size, alignment, and GC properties,
10  * except that it can be freely reassigned.
11  *
12  * All methods of `T`, including constructors, destructors and overloads, are lost.
13  * In fact, the result type has barely anything to do with `T`, except the following properties:
14  *
15  * $(UL
16  *  $(LI mutable)
17  *  $(LI pointers where `T` has pointers)
18  *  $(LI non-pointer values where `T` has no pointers (best-effort).))
19  *
20  * In other words, `T` is equivalent to `DeepUnqual!T` for the purpose of memory management, and nothing else.
21  */
22 public template DeepUnqual(T)
23 {
24     alias DeepUnqual = DeepUnqualImpl!T;
25 
26     static assert(T.sizeof == DeepUnqual.sizeof);
27     static assert(T.alignof == DeepUnqual.alignof);
28     static assert(hasIndirections!T == hasIndirections!DeepUnqual);
29 }
30 
31 private template DeepUnqualImpl(T)
32 {
33     static if (is(T == struct))
34     {
35         static if (anyPairwiseEqual!(staticMap!(offsetOf, T.tupleof)))
36         {
37             // Struct with anonymous unions detected!
38             // Danger, danger!
39             // Fall back to void[].
40             align(T.alignof)
41             struct DeepUnqualImpl
42             {
43                 void[T.sizeof] data;
44             }
45         }
46         else
47         {
48             align(T.alignof)
49             struct DeepUnqualImpl
50             {
51                 staticMap!(DeepUnqual, typeof(T.init.tupleof)) fields;
52             }
53         }
54     }
55     else static if (is(T == union) || isAssociativeArray!T)
56     {
57         align(T.alignof)
58         struct DeepUnqualImpl
59         {
60             static if (hasIndirections!T)
61             {
62                 void[T.sizeof] data;
63             }
64             else
65             {
66                 // union of non-pointer types?
67                 ubyte[T.sizeof] data;
68             }
69         }
70     }
71     else static if (is(T == class) || is(T == interface) || is(T == function) || is(T : U*, U))
72     {
73         alias DeepUnqualImpl = void*;
74     }
75     else static if (is(T : K[], K))
76     {
77         struct DeepUnqualImpl
78         {
79             size_t length;
80             void* ptr;
81         }
82     }
83     else static if (is(T == delegate))
84     {
85         struct DeepUnqualImpl
86         {
87             void* data;
88             void* funcptr;
89         }
90     }
91     else static if (is(T == enum))
92     {
93         alias DeepUnqualImpl = DeepUnqual!(OriginalType!T);
94     }
95     else static if (staticIndexOf!(typeof(cast() T.init),
96         bool, byte, ubyte, short, ushort, int, uint, long, ulong,
97         char, wchar, dchar, float, double, real, ifloat, idouble, ireal, cfloat, cdouble, creal) != -1)
98     {
99         alias DeepUnqualImpl = typeof(cast() T.init);
100     }
101     else static if (is(T == U[size], U, size_t size))
102     {
103         alias DeepUnqualImpl = DeepUnqual!U[size];
104     }
105     else
106     {
107         static assert(false, "Unsupported type " ~ T.stringof);
108     }
109 }
110 
111 private enum offsetOf(alias member) = member.offsetof;
112 
113 private template anyPairwiseEqual(T...)
114 {
115     static if (T.length == 0 || T.length == 1)
116     {
117         enum anyPairwiseEqual = false;
118     }
119     else static if (T.length == 2)
120     {
121         enum anyPairwiseEqual = T[0] == T[1];
122     }
123     else
124     {
125         enum anyPairwiseEqual = T[0] == T[1] || anyPairwiseEqual!(T[1 .. $]);
126     }
127 }