1 module rt.util.typeinfo;
2 
3 static import core.internal.hash;
4 
5 private struct _Complex(T) { T re; T im; }
6 
7 enum __c_complex_float : _Complex!float;
8 enum __c_complex_double : _Complex!double;
9 enum __c_complex_real : _Complex!real;  // This is why we don't use stdc.config
10 
11 alias d_cfloat = __c_complex_float;
12 alias d_cdouble = __c_complex_double;
13 alias d_creal = __c_complex_real;
14 
15 enum isComplex(T) = is(T == d_cfloat) || is(T == d_cdouble) || is(T == d_creal);
16 // Three-way compare for integrals: negative if `lhs < rhs`, positive if `lhs > rhs`, 0 otherwise.
17 pragma(inline, true)
18 private int cmp3(T)(const T lhs, const T rhs)
19 if (__traits(isIntegral, T))
20 {
21     static if (T.sizeof < int.sizeof)
22         // Taking the difference will always fit in an int.
23         return int(lhs) - int(rhs);
24     else
25         return (lhs > rhs) - (lhs < rhs);
26 }
27 
28 // Three-way compare for real fp types. NaN is smaller than all valid numbers.
29 // Code is small and fast, see https://godbolt.org/z/fzb877
30 pragma(inline, true)
31 private int cmp3(T)(const T d1, const T d2)
32 if (is(T == float) || is(T == double) || is(T == real))
33 {
34     if (d2 != d2)
35         return d1 == d1; // 0 if both ar NaN, 1 if d1 is valid and d2 is NaN.
36     // If d1 is NaN, both comparisons are false so we get -1, as needed.
37     return (d1 > d2) - !(d1 >= d2);
38 }
39 
40 // Three-way compare for complex types.
41 pragma(inline, true)
42 private int cmp3(T)(const T f1, const T f2)
43 if (isComplex!T)
44 {
45     if (int result = cmp3(f1.re, f2.re))
46         return result;
47     return cmp3(f1.im, f2.im);
48 }
49 
50 // Reduces to `T` if `cond` is `true` or `U` otherwise. Consider moving elsewhere if useful.
51 private template Select(bool cond, T, U)
52 {
53     static if (cond) alias Select = T;
54     else alias Select = U;
55 }
56 
57 /*
58 TypeInfo information for built-in types.
59 
60 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
61 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
62 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
63 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
64 during compilation whether they have different signedness and override appropriately. For initializer, we
65 detect if we need to override. The overriding initializer should be nonzero.
66 */
67 private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base)
68 if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
69 {
70     const: nothrow: pure: @trusted:
71 
72     // Returns the type name.
73     override string toString() const pure nothrow @safe { return T.stringof; }
74 
75     // `getHash` is the same for `Base` and `T`, introduce it just once.
76     static if (is(T == Base))
77         override size_t getHash(scope const void* p)
78         {
79             return hashOf(*cast(const T *)p);
80         }
81 
82     // `equals` is the same for `Base` and `T`, introduce it just once.
83     static if (is(T == Base))
84         override bool equals(in void* p1, in void* p2)
85         {
86             return *cast(const T *)p1 == *cast(const T *)p2;
87         }
88 
89     // `T` and `Base` may have different signedness, so this function is introduced conditionally.
90     static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
91         override int compare(in void* p1, in void* p2)
92         {
93             return cmp3(*cast(const T*) p1, *cast(const T*) p2);
94         }
95 
96     static if (is(T == Base))
97         override @property size_t tsize()
98         {
99             return T.sizeof;
100         }
101 
102     static if (is(T == Base))
103         override @property size_t talign()
104         {
105             return T.alignof;
106         }
107 
108     // Override initializer only if necessary.
109     static if (is(T == Base) || T.init != Base.init)
110         override const(void)[] initializer()
111         {
112             static if (__traits(isZeroInit, T))
113             {
114                 return (cast(void *)null)[0 .. T.sizeof];
115             }
116             else
117             {
118                 static immutable T[1] c;
119                 return c;
120             }
121         }
122 
123     // `swap` is the same for `Base` and `T`, so introduce only once.
124     static if (is(T == Base))
125         override void swap(void *p1, void *p2)
126         {
127             auto t = *cast(T *) p1;
128             *cast(T *)p1 = *cast(T *)p2;
129             *cast(T *)p2 = t;
130         }
131 
132     static if (is(T == Base) || RTInfo!T != RTInfo!Base)
133         override @property immutable(void)* rtInfo()
134         {
135             return RTInfo!T;
136         }
137 
138     static if (is(T == Base))
139     {
140         static if ((__traits(isFloating, T) && T.mant_dig != 64) ||
141                    (isComplex!T && T.re.mant_dig != 64))
142             // FP types except 80-bit X87 are passed in SIMD register.
143             override @property uint flags() const { return 2; }
144     }
145 }
146 
147 /*
148 TypeInfo information for arrays of built-in types.
149 
150 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
151 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
152 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
153 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
154 during compilation whether they have different signedness and override appropriately. For initializer, we
155 detect if we need to override. The overriding initializer should be nonzero.
156 */
157 private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base)
158 {
159     static if (is(T == Base))
160         override bool opEquals(const Object o) const @safe nothrow { return TypeInfo.opEquals(cast(const TypeInfo) o); }
161 
162     alias opEquals = typeof(super).opEquals;
163     alias opEquals = TypeInfo.opEquals;
164 
165     override string toString() const { return (T[]).stringof; }
166 
167     static if (is(T == Base))
168         override size_t getHash(scope const void* p) @trusted const
169         {
170             return hashOf(*cast(const T[]*) p);
171         }
172 
173     static if (is(T == Base))
174         override bool equals(in void* p1, in void* p2) const
175         {
176             // Just reuse the builtin.
177             return *cast(const(T)[]*) p1 == *cast(const(T)[]*) p2;
178         }
179 
180     static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
181         override int compare(in void* p1, in void* p2) const
182         {
183             // Can't reuse __cmp in object.d because that handles NaN differently.
184             // (Q: would it make sense to unify behaviors?)
185             // return __cmp(*cast(const T[]*) p1, *cast(const T[]*) p2);
186             auto lhs = *cast(const T[]*) p1;
187             auto rhs = *cast(const T[]*) p2;
188             size_t len = lhs.length;
189             if (rhs.length < len)
190                 len = rhs.length;
191             for (size_t u = 0; u < len; u++)
192             {
193                 if (int result = cmp3(lhs.ptr[u], rhs.ptr[u]))
194                     return result;
195             }
196             return cmp3(lhs.length, rhs.length);        }
197 
198     override @property inout(TypeInfo) next() inout
199     {
200         return cast(inout) typeid(T);
201     }
202 }
203 
204 ////////////////////////////////////////////////////////////////////////////////
205 // Predefined TypeInfos
206 ////////////////////////////////////////////////////////////////////////////////
207 
208 // void
209 class TypeInfo_v : TypeInfoGeneric!ubyte
210 {
211     const: nothrow: pure: @trusted:
212 
213     override string toString() const pure nothrow @safe { return "void"; }
214 
215     override size_t getHash(scope const void* p)
216     {
217         assert(0);
218     }
219 
220     override @property uint flags() nothrow pure
221     {
222         return 1;
223     }
224 }
225 
226 // All integrals.
227 class TypeInfo_h : TypeInfoGeneric!ubyte {}
228 class TypeInfo_b : TypeInfoGeneric!(bool, ubyte) {}
229 class TypeInfo_g : TypeInfoGeneric!(byte, ubyte) {}
230 class TypeInfo_a : TypeInfoGeneric!(char, ubyte) {}
231 class TypeInfo_t : TypeInfoGeneric!ushort {}
232 class TypeInfo_s : TypeInfoGeneric!(short, ushort) {}
233 class TypeInfo_u : TypeInfoGeneric!(wchar, ushort) {}
234 class TypeInfo_w : TypeInfoGeneric!(dchar, uint) {}
235 class TypeInfo_k : TypeInfoGeneric!uint {}
236 class TypeInfo_i : TypeInfoGeneric!(int, uint) {}
237 class TypeInfo_m : TypeInfoGeneric!ulong {}
238 class TypeInfo_l : TypeInfoGeneric!(long, ulong) {}
239 static if (is(cent)) class TypeInfo_zi : TypeInfoGeneric!cent {}
240 static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {}
241 
242 // All simple floating-point types.
243 class TypeInfo_f : TypeInfoGeneric!float {}
244 class TypeInfo_d : TypeInfoGeneric!double {}
245 class TypeInfo_e : TypeInfoGeneric!real {}
246 
247 // Arrays of all integrals.
248 class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {}
249 class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {}
250 class TypeInfo_Ag : TypeInfoArrayGeneric!(byte, ubyte) {}
251 class TypeInfo_Aa : TypeInfoArrayGeneric!(char, ubyte) {}
252 class TypeInfo_Axa : TypeInfoArrayGeneric!(const char) {}
253 class TypeInfo_Aya : TypeInfoArrayGeneric!(immutable char)
254 {
255     // Must override this, otherwise "string" is returned.
256     override string toString() const { return "immutable(char)[]"; }
257 }
258 class TypeInfo_At : TypeInfoArrayGeneric!ushort {}
259 class TypeInfo_As : TypeInfoArrayGeneric!(short, ushort) {}
260 class TypeInfo_Au : TypeInfoArrayGeneric!(wchar, ushort) {}
261 class TypeInfo_Ak : TypeInfoArrayGeneric!uint {}
262 class TypeInfo_Ai : TypeInfoArrayGeneric!(int, uint) {}
263 class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {}
264 class TypeInfo_Am : TypeInfoArrayGeneric!ulong {}
265 class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {}
266 
267 // Arrays of all simple floating-point types.
268 class TypeInfo_Af : TypeInfoArrayGeneric!float {}
269 class TypeInfo_Ad : TypeInfoArrayGeneric!double {}
270 class TypeInfo_Ae : TypeInfoArrayGeneric!real {}
271 
272 // void[] is a bit different, behaves like ubyte[] for comparison purposes.
273 class TypeInfo_Av : TypeInfo_Ah
274 {
275     override string toString() const { return "void[]"; }
276 
277     override @property inout(TypeInfo) next() inout
278     {
279         return cast(inout) typeid(void);
280     }
281 
282     unittest
283     {
284         assert(typeid(void[]).toString == "void[]");
285         assert(typeid(void[]).next == typeid(void));
286     }
287 }
288 // typeof(null)
289 class TypeInfo_n : TypeInfo
290 {
291     const: pure: @nogc: nothrow: @safe:
292 
293     override string toString() { return "typeof(null)"; }
294 
295     override size_t getHash(scope const void*) { return 0; }
296 
297     override bool equals(in void*, in void*) { return true; }
298 
299     override int compare(in void*, in void*) { return 0; }
300 
301     override @property size_t tsize() { return typeof(null).sizeof; }
302 
303     override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; }
304 
305     override void swap(void*, void*) {}
306 
307     override @property immutable(void)* rtInfo() { return rtinfoNoPointers; }
308 }