1 module rt.util.typeinfo;
2 
3 import rt.util.utility;
4 
5 // Reduces to `T` if `cond` is `true` or `U` otherwise.
6 private template Select(bool cond, T, U)
7 {
8     static if (cond) alias Select = T;
9     else alias Select = U;
10 }
11 
12 template Floating(T)
13 if (is(T == float) || is(T == double) || is(T == real))
14 {
15 	pure nothrow @safe:
16 
17     bool equals(T f1, T f2)
18     {
19         return f1 == f2;
20     }
21 
22     int compare(T d1, T d2)
23     {
24         if (d1 != d1 || d2 != d2) // if either are NaN
25         {
26             if (d1 != d1)
27             {
28                 if (d2 != d2)
29                     return 0;
30                 return -1;
31             }
32             return 1;
33         }
34         return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1);
35     }
36 }
37 
38 // @@@DEPRECATED_2.105@@@
39 template Floating(T)
40 if (isComplex!T)
41 {
42 	pure nothrow @safe:
43 
44     bool equals(T f1, T f2)
45     {
46         return f1.re == f2.re && f1.im == f2.im;
47     }
48 
49     int compare(T f1, T f2)
50     {
51         int result;
52 
53         if (f1.re < f2.re)
54             result = -1;
55         else if (f1.re > f2.re)
56             result = 1;
57         else if (f1.im < f2.im)
58             result = -1;
59         else if (f1.im > f2.im)
60             result = 1;
61         else
62             result = 0;
63         return result;
64     }
65 
66     size_t hashOf(scope const T val)
67     {
68         return 0;
69         //return core.internal.hash.hashOf(val.re, core.internal.hash.hashOf(val.im));
70     }
71 }
72 
73 template Array(T)
74 if (is(T == float) || is(T == double) || is(T == real))
75 {
76 	pure nothrow @safe:
77 
78     bool equals(T[] s1, T[] s2)
79     {
80         size_t len = s1.length;
81         if (len != s2.length)
82             return false;
83         for (size_t u = 0; u < len; u++)
84         {
85             if (!Floating!T.equals(s1[u], s2[u]))
86                 return false;
87         }
88         return true;
89     }
90 
91     int compare(T[] s1, T[] s2)
92     {
93         size_t len = s1.length;
94         if (s2.length < len)
95             len = s2.length;
96         for (size_t u = 0; u < len; u++)
97         {
98             if (int c = Floating!T.compare(s1[u], s2[u]))
99                 return c;
100         }
101         return (s1.length > s2.length) - (s1.length < s2.length);
102     }
103 
104     //public alias hashOf = core.internal.hash.hashOf;
105 }
106 
107 // @@@DEPRECATED_2.105@@@
108 template Array(T)
109 if (isComplex!T)
110 {
111 	pure nothrow @safe:
112 
113     bool equals(T[] s1, T[] s2)
114     {
115         size_t len = s1.length;
116         if (len != s2.length)
117             return false;
118         for (size_t u = 0; u < len; u++)
119         {
120             if (!Floating!T.equals(s1[u], s2[u]))
121                 return false;
122         }
123         return true;
124     }
125 
126     int compare(T[] s1, T[] s2)
127     {
128         size_t len = s1.length;
129         if (s2.length < len)
130             len = s2.length;
131         for (size_t u = 0; u < len; u++)
132         {
133             if (int c = Floating!T.compare(s1[u], s2[u]))
134                 return c;
135         }
136         return (s1.length > s2.length) - (s1.length < s2.length);
137     }
138 
139     size_t hashOf(scope const T[] val)
140     {
141         return 0;
142         /+size_t hash = 0;
143         foreach (ref o; val)
144         {
145 		hash = core.internal.hash.hashOf(Floating!T.hashOf(o), hash);
146         }
147         return hash;+/
148     }
149 }
150 
151 
152 
153 /*
154 TypeInfo information for built-in types.
155 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
156 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
157 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
158 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
159 during compilation whether they have different signedness and override appropriately. For initializer, we
160 detect if we need to override. The overriding initializer should be nonzero.
161 */
162 private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base)
163 if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
164 {
165 const: nothrow: pure: @trusted:
166 
167     // Returns the type name.
168     override string toString() const pure nothrow @safe { return T.stringof; }
169 
170     // `getHash` is the same for `Base` and `T`, introduce it just once.
171     static if (is(T == Base))
172         /+override size_t getHash(scope const void* p)
173         {
174 		static if (__traits(isFloating, T) || isComplex!T)
175 		return Floating!T.hashOf(*cast(T*)p);
176 		else
177 		return hashOf(*cast(const T *)p);
178         }+/
179 
180 		// `equals` is the same for `Base` and `T`, introduce it just once.
181 		static if (is(T == Base))
182 			override bool equals(in void* p1, in void* p2)
183 			{
184 				static if (__traits(isFloating, T) || isComplex!T)
185 					return Floating!T.equals(*cast(T*)p1, *cast(T*)p2);
186 				else
187 					return *cast(T *)p1 == *cast(T *)p2;
188 			}
189 
190     // `T` and `Base` may have different signedness, so this function is introduced conditionally.
191     static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
192         /+override int compare(in void* p1, in void* p2)
193         {
194 		static if (__traits(isFloating, T) || isComplex!T)
195 		{
196 		return Floating!T.compare(*cast(T*)p1, *cast(T*)p2);
197 		}
198 		else static if (T.sizeof < int.sizeof)
199 		{
200 		// Taking the difference will always fit in an int.
201 		return int(*cast(T *) p1) - int(*cast(T *) p2);
202 		}
203 		else
204 		{
205 		auto lhs = *cast(T *) p1, rhs = *cast(T *) p2;
206 		return (lhs > rhs) - (lhs < rhs);
207 		}
208         +/
209 
210 		static if (is(T == Base))
211 			override @property size_t tsize() nothrow pure
212 			{
213 				return T.sizeof;
214 			}
215 
216     static if (is(T == Base))
217         /+override @property size_t talign() nothrow pure
218         {
219 		return T.alignof;
220         }+/
221 
222 		// Override initializer only if necessary.
223 		static if (is(T == Base) || T.init != Base.init)
224 			override const(void)[] initializer() @trusted
225 			{
226 				static if (__traits(isZeroInit, T))
227 				{
228 					return (cast(void *)null)[0 .. T.sizeof];
229 				}
230 				else
231 				{
232 					static immutable T[1] c;
233 					return c;
234 				}
235 			}
236 
237     // `swap` is the same for `Base` and `T`, so introduce only once.
238     static if (is(T == Base))
239         /+override void swap(void *p1, void *p2)
240         {
241 		auto t = *cast(T *) p1;
242 		*cast(T *)p1 = *cast(T *)p2;
243 		*cast(T *)p2 = t;
244         }+/
245 
246 		static if (is(T == Base) || RTInfo!T != RTInfo!Base)
247 			/+override @property immutable(void)* rtInfo() nothrow pure const @safe
248 			{
249             return RTInfo!T;
250 			}+/
251 
252 			static if (is(T == Base))
253 			{
254 				/+static if ((__traits(isFloating, T) && T.mant_dig != 64) ||
255 				(isComplex!T && T.re.mant_dig != 64))
256 				// FP types except 80-bit X87 are passed in SIMD register.
257 				override @property uint flags() const { return 2; }+/
258 			}
259 }
260 
261 
262 /*
263 TypeInfo information for arrays of built-in types.
264 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
265 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
266 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
267 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
268 during compilation whether they have different signedness and override appropriately. For initializer, we
269 detect if we need to override. The overriding initializer should be nonzero.
270 */
271 version(LWDR_INTERNAL_ti_next)
272 private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base)
273 {
274     static if (is(T == Base))
275         override bool opEquals(Object o) { return TypeInfo.opEquals(o); }
276 
277     override string toString() const { return (T[]).stringof; }
278 
279     /+static if (is(T == Base)) {
280 	override size_t getHash(scope const void* p) @trusted const
281 	{
282 	return 0;
283 	}
284     }+/
285 
286     static if (is(T == Base)) {
287         override bool equals(in void* p1, in void* p2) const
288         {
289             static if (__traits(isFloating, T) || isComplex!T)
290             {
291                 return Array!T.equals(*cast(T[]*)p1, *cast(T[]*)p2);
292             }
293             else
294             {
295                 import core.stdc.string;
296                 auto s1 = *cast(T[]*)p1;
297                 auto s2 = *cast(T[]*)p2;
298                 return s1.length == s2.length &&
299                     memcmp(s1.ptr, s2.ptr, s1.length) == 0;
300             }
301         }
302     }
303 
304     static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) {
305         /+override int compare(in void* p1, in void* p2) const
306         {
307 		static if (__traits(isFloating, T) || isComplex!T)
308 		{
309 		return Array!T.compare(*cast(T[]*)p1, *cast(T[]*)p2);
310 		}
311 		else
312 		{
313 		auto s1 = *cast(T[]*)p1;
314 		auto s2 = *cast(T[]*)p2;
315 		auto len = s1.length;
316 
317 		if (s2.length < len)
318 		len = s2.length;
319 		for (size_t u = 0; u < len; u++)
320 		{
321 		if (int result = (s1[u] > s2[u]) - (s1[u] < s2[u]))
322 		return result;
323 		}
324 		return (s1.length > s2.length) - (s1.length < s2.length);
325 		}
326         }+/
327 	}
328 
329     version(LWDR_INTERNAL_ti_next)
330     override @property inout(TypeInfo) next() inout
331     {
332         return cast(inout) typeid(T);
333     }
334 }
335 
336 // void
337 class TypeInfo_v : TypeInfoGeneric!ubyte
338 {
339 const: nothrow: pure: @trusted:
340 
341     override string toString() const pure nothrow @safe { return "void"; }
342 
343     /+override size_t getHash(scope const void* p)
344     {
345 	assert(0);
346     }
347 
348     override @property uint flags() nothrow pure
349     {
350 	return 1;
351     }
352 
353     unittest
354     {
355 	assert(typeid(void).toString == "void");
356 	assert(typeid(void).flags == 1);
357     }+/
358 }
359 
360 class TypeInfo_h : TypeInfoGeneric!ubyte {}
361 class TypeInfo_b : TypeInfoGeneric!(bool, ubyte) {}
362 class TypeInfo_g : TypeInfoGeneric!(byte, ubyte) {}
363 class TypeInfo_a : TypeInfoGeneric!(char, ubyte) {}
364 class TypeInfo_t : TypeInfoGeneric!ushort {}
365 class TypeInfo_s : TypeInfoGeneric!(short, ushort) {}
366 class TypeInfo_u : TypeInfoGeneric!(wchar, ushort) {}
367 class TypeInfo_w : TypeInfoGeneric!(dchar, uint) {}
368 class TypeInfo_k : TypeInfoGeneric!uint {}
369 class TypeInfo_i : TypeInfoGeneric!(int, uint) {}
370 class TypeInfo_m : TypeInfoGeneric!ulong {}
371 class TypeInfo_l : TypeInfoGeneric!(long, ulong) {}
372 static if (is(cent)) class TypeInfo_zi : TypeInfoGeneric!cent {}
373 static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {}
374 
375 // All simple floating-point types.
376 class TypeInfo_f : TypeInfoGeneric!float {}
377 class TypeInfo_d : TypeInfoGeneric!double {}
378 class TypeInfo_e : TypeInfoGeneric!real {}
379 
380 version(LWDR_DynamicArray) {
381 
382 class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {}
383 class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {}
384 class TypeInfo_Ag : TypeInfoArrayGeneric!(byte, ubyte) {}
385 class TypeInfo_Aa : TypeInfoArrayGeneric!(char, ubyte) {}
386 class TypeInfo_Axa : TypeInfoArrayGeneric!(const char) {}
387 class TypeInfo_Aya : TypeInfoArrayGeneric!(immutable char)
388 {
389     // Must override this, otherwise "string" is returned.
390     override string toString() const { return "immutable(char)[]"; }
391 }
392 class TypeInfo_At : TypeInfoArrayGeneric!ushort {}
393 class TypeInfo_As : TypeInfoArrayGeneric!(short, ushort) {}
394 class TypeInfo_Au : TypeInfoArrayGeneric!(wchar, ushort) {}
395 class TypeInfo_Ak : TypeInfoArrayGeneric!uint {}
396 class TypeInfo_Ai : TypeInfoArrayGeneric!(int, uint) {}
397 class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {}
398 class TypeInfo_Am : TypeInfoArrayGeneric!ulong {}
399 class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {}
400 
401 // Arrays of all simple floating-point types.
402 class TypeInfo_Af : TypeInfoArrayGeneric!float {}
403 class TypeInfo_Ad : TypeInfoArrayGeneric!double {}
404 class TypeInfo_Ae : TypeInfoArrayGeneric!real {}
405 }
406 
407 // typeof(null)
408 class TypeInfo_n : TypeInfo
409 {
410     override string toString() const @safe { return "typeof(null)"; }
411 
412     /+override size_t getHash(scope const void* p) const
413     {
414 	return 0;
415     }+/
416 
417     override bool equals(in void* p1, in void* p2) const @trusted
418     {
419         return true;
420     }
421 
422     /+override int compare(in void* p1, in void* p2) const @trusted
423     {
424 	return 0;
425     }+/
426 
427     override @property size_t tsize() const
428     {
429         return typeof(null).sizeof;
430     }
431 
432     override const(void)[] initializer() const @trusted
433     {
434         __gshared immutable void[typeof(null).sizeof] init;
435         return init;
436     }
437 
438     /+override void swap(void *p1, void *p2) const @trusted
439     {
440     }+/
441 
442     //override @property immutable(void)* rtInfo() nothrow pure const @safe { return rtinfoNoPointers; }
443 
444     unittest
445     {
446         with (typeid(typeof(null)))
447         {
448             assert(toString == "typeof(null)");
449             assert(getHash(null) == 0);
450             assert(equals(null, null));
451             assert(compare(null, null) == 0);
452             assert(tsize == typeof(null).sizeof);
453             assert(initializer == new ubyte[(void*).sizeof]);
454             assert(rtInfo == rtinfoNoPointers);
455         }
456     }
457 }
458 
459 version(LWDR_DynamicArray) {
460 // void[] is a bit different, behaves like ubyte[] for comparison purposes.
461 class TypeInfo_Av : TypeInfo_Ah
462 {
463     override string toString() const { return "void[]"; }
464 
465     version(LWDR_INTERNAL_ti_next)
466     override @property inout(TypeInfo) next() inout
467     {
468         return cast(inout) typeid(void);
469     }
470 
471     unittest
472     {
473         assert(typeid(void[]).toString == "void[]");
474         assert(typeid(void[]).next == typeid(void));
475     }
476 }
477 }