1 module d.common.type;
2 
3 public import d.common.builtintype;
4 public import d.common.qualifier;
5 
6 // Because bitfields won't work with the current stringof semantic.
7 // It is needed to import all that instanciate TypeDescriptor.
8 import d.ast.type, d.ir.type;
9 
10 mixin template TypeMixin(K, Payload) {
11 private:
12 	import util.bitfields;
13 	alias Desc = TypeDescriptor!K;
14 
15 	union {
16 		Desc desc;
17 		ulong raw_desc;
18 	}
19 
20 	Payload payload;
21 
22 	static assert(K.Builtin == 0,
23 	              K.stringof ~ " must have a member Builtin of value 0.");
24 	static assert(K.Function != 0, K.stringof ~ " must have a Function kind.");
25 
26 	static assert(Payload.sizeof == ulong.sizeof,
27 	              "Payload must be the same size as ulong.");
28 	static assert(is(typeof(Payload.init.next) == typeof(&this)),
29 	              "Payload.next must be a pointer to the next type element.");
30 	static assert(is(typeof(Payload.init.params) == ParamType*),
31 	              "Payload.params must be a pointer to parameter's types.");
32 
33 	bool isPackable() const {
34 		return (raw_desc & (-1L << Desc.DataSize)) == 0;
35 	}
36 
37 	auto getConstructedMixin(this T)(K k, TypeQualifier q) in {
38 		assert(raw_desc != 0, "You can't construct type on None.");
39 	} do {
40 		// XXX: Consider caching in context, and stick in payload
41 		// instead of heap if it fit.
42 		return isPackable()
43 			? T(Desc(k, q, raw_desc), payload)
44 			: T(Desc(k, q), new T(desc, payload));
45 	}
46 
47 	auto getElementMixin(this T)() {
48 		auto data = desc.data;
49 		if (data == 0) {
50 			assert(payload.next !is null, "None shouldn't have been packed.");
51 			return *payload.next;
52 		}
53 
54 		union U {
55 			ulong raw;
56 			Desc desc;
57 		}
58 
59 		return T(U(data).desc, payload);
60 	}
61 
62 	alias ParamType = PackedType!(typeof(this), ParamTuple);
63 
64 	template getPackedBitfield(U...) {
65 		auto getPackedBitfield(A...)(A args) inout {
66 			import std.traits;
67 			alias T = Unqual!(typeof(this));
68 			return inout(PackedType!(T, U))(desc, args, payload);
69 		}
70 	}
71 
72 	struct PackedType(T, U...) {
73 	private:
74 		alias BaseDesc = TypeDescriptor!K;
75 		alias Desc = TypeDescriptor!(K, U);
76 
77 		union {
78 			Desc desc;
79 			ulong raw_desc;
80 		}
81 
82 		Payload payload;
83 
84 		this(A...)(BaseDesc bd, A args, inout Payload p) inout {
85 			union BU {
86 				BaseDesc base;
87 				ulong raw;
88 			}
89 
90 			Desc d;
91 			foreach (i, a; args) {
92 				mixin("d." ~ U[3 * i + 1] ~ " = a;");
93 			}
94 
95 			auto raw = BU(bd).raw;
96 			auto redux = raw & (-1UL >> SizeOfBitField!U);
97 			if (raw == redux) {
98 				union U {
99 					Desc desc;
100 					ulong raw;
101 				}
102 
103 				raw_desc = raw | U(d).raw;
104 				payload = p;
105 			} else {
106 				desc = d;
107 				payload = inout(Payload)(new inout(T)(bd, p));
108 			}
109 		}
110 
111 		this(Desc d, inout Payload p) inout {
112 			desc = d;
113 			payload = p;
114 		}
115 
116 		template PackedMixin(T...) {
117 			static if (T.length == 0) {
118 				enum PackedMixin = "";
119 			} else {
120 				enum PackedMixin = "\n@property auto " ~ T[1]
121 					~ "() const { return desc." ~ T[1] ~ "; }"
122 					~ PackedMixin!(T[3 .. $]);
123 			}
124 		}
125 
126 	public:
127 		mixin TypeAccessorMixin!K;
128 		mixin(PackedMixin!U);
129 
130 		auto getType() inout {
131 			union BU {
132 				ulong raw;
133 				BaseDesc desc;
134 			}
135 
136 			auto u = BU(raw_desc & (-1UL >> SizeOfBitField!U));
137 			if (u.raw == 0 && payload.next !is null) {
138 				return *payload.next;
139 			}
140 
141 			return inout(T)(u.desc, payload);
142 		}
143 	}
144 
145 	struct UnionTypeTpl(T, U...) {
146 	private:
147 		template TagFields(uint i, U...) {
148 			import std.conv;
149 			static if (U.length == 0) {
150 				enum TagFields =
151 					"\n\t" ~ T.stringof ~ " = " ~ to!string(i) ~ ",";
152 			} else {
153 				enum S = U[0].stringof;
154 				static assert((S[0] & 0x80) == 0,
155 				              S ~ " must not start with an unicode.");
156 				static assert(U[0].sizeof <= size_t.sizeof,
157 				              "Elements must be of pointer size or smaller.");
158 				import std.ascii;
159 				enum Name = (S == "typeof(null)")
160 					? "Undefined"
161 					: toUpper(S[0]) ~ S[1 .. $];
162 				enum TagFields = "\n\t" ~ Name ~ " = " ~ to!string(i) ~ ","
163 					~ TagFields!(i + 1, U[1 .. $]);
164 			}
165 		}
166 
167 		mixin("public enum Tag {" ~ TagFields!(0, U) ~ "\n}");
168 
169 		import std.traits;
170 		alias Tags = EnumMembers!Tag;
171 
172 		// Using uint here as Tag is not accessible where the bitfield is mixed in.
173 		import std.typetuple;
174 		alias TagTuple = TypeTuple!(uint, "tag", EnumSize!Tag);
175 
176 		alias Desc = TypeDescriptor!(K, TagTuple);
177 
178 		union {
179 			Desc desc;
180 			ulong raw_desc;
181 		}
182 
183 		union {
184 			U u;
185 			Payload payload;
186 		}
187 
188 		// XXX: probably worthy of adding to phobos.
189 		template isImplicitelyConvertibleFrom(T) {
190 			import std.traits;
191 			enum isImplicitelyConvertibleFrom(U) =
192 				isImplicitlyConvertible!(T, U);
193 		}
194 
195 		import std.typetuple, std.traits;
196 		enum canConstructFrom(P) =
197 			anySatisfy!(isImplicitelyConvertibleFrom!(Unqual!P), U);
198 
199 	public:
200 		this(inout T t) inout {
201 			import std.conv;
202 			auto packed = t.getPackedBitfield!TagTuple(U.length.to!Tag());
203 			desc = packed.desc;
204 			payload = packed.payload;
205 		}
206 
207 		this(P)(inout P p) inout if (canConstructFrom!P) {
208 			// Sanity check, in case canConstructFrom is bogous.
209 			bool constructed = false;
210 
211 			import std.traits;
212 			alias UT = Unqual!P;
213 			foreach (E; Tags[0 .. $ - 1]) {
214 				static if (is(UT : U[E])) {
215 					Desc d;
216 					d.tag = E;
217 
218 					// Assign d instead of setting tag to be inout compatible.
219 					desc = d;
220 					u[E] = p;
221 
222 					constructed = true;
223 				}
224 			}
225 
226 			assert(constructed, "canConstructFrom is bogous.");
227 		}
228 
229 		@property
230 		auto tag() const {
231 			import std.conv;
232 			return desc.tag.to!Tag();
233 		}
234 
235 		@property
236 		auto get(Tag E)() inout in {
237 			assert(tag == E);
238 		} do {
239 			static if (E == U.length) {
240 				alias R = inout(PackedType!(T, TagTuple));
241 				return R(desc, payload).getType();
242 			} else {
243 				return u[E];
244 			}
245 		}
246 	}
247 
248 	alias FunctionType = FunctionTypeTpl!(typeof(this));
249 
250 	struct FunctionTypeTpl(T) {
251 	private:
252 		enum Pad = ulong.sizeof * 8 - Desc.DataSize;
253 		enum CountSize = ulong.sizeof * 8 - Pad - 8;
254 
255 		import std.bitmanip;
256 		mixin(bitfields!(
257 			// sdfmt off
258 			Linkage, "lnk", 3,
259 			bool, "variadic", 1,
260 			bool, "dpure", 1,
261 			ulong, "ctxCount", 3,
262 			ulong, "paramCount", CountSize,
263 			uint, "", Pad, // Pad for TypeKind and qualifier
264 			// sdfmt on
265 		));
266 
267 		ParamType* params;
268 
269 		this(Desc desc, inout ParamType* params) inout {
270 			// /!\ Black magic ahead.
271 			auto raw_desc = cast(ulong*) &desc;
272 
273 			// Remove the TypeKind and qualifier
274 			*raw_desc = (*raw_desc >> Pad);
275 
276 			// This should point to an area of memory that have
277 			// the correct layout for the bitfield.
278 			auto f = cast(FunctionType*) raw_desc;
279 
280 			// unqual trick required for bitfield
281 			auto unqual_this = cast(FunctionType*) &this;
282 			unqual_this.lnk = f.lnk;
283 			unqual_this.variadic = f.variadic;
284 			unqual_this.dpure = f.dpure;
285 			unqual_this.ctxCount = f.ctxCount;
286 			unqual_this.paramCount = f.paramCount;
287 
288 			this.params = params;
289 		}
290 
291 	public:
292 		this(Linkage linkage, ParamType returnType, ParamType[] params,
293 		     bool isVariadic) {
294 			lnk = linkage;
295 			variadic = isVariadic;
296 			dpure = false;
297 			ctxCount = 0;
298 			paramCount = params.length;
299 			this.params = (params ~ returnType).ptr;
300 		}
301 
302 		this(Linkage linkage, ParamType returnType, ParamType ctxType,
303 		     ParamType[] params, bool isVariadic) {
304 			lnk = linkage;
305 			variadic = isVariadic;
306 			dpure = false;
307 			ctxCount = 1;
308 			paramCount = params.length;
309 			this.params = (ctxType ~ params ~ returnType).ptr;
310 		}
311 
312 		T getType(TypeQualifier q = TypeQualifier.Mutable) {
313 			ulong d = *cast(ulong*) &this;
314 			auto p = Payload(cast(T*) params);
315 			return T(Desc(K.Function, q, d), p);
316 		}
317 
318 		FunctionType getDelegate(ulong contextCount = 1) in {
319 			assert(contextCount <= paramCount + ctxCount);
320 		} do {
321 			auto t = this;
322 			t.ctxCount = contextCount;
323 			t.paramCount = paramCount + ctxCount - contextCount;
324 			return t;
325 		}
326 
327 		FunctionType getFunction() {
328 			return getDelegate(0);
329 		}
330 
331 		@property
332 		Linkage linkage() const {
333 			return lnk;
334 		}
335 
336 		auto withLinkage(Linkage linkage) inout {
337 			// Bypass type qualifier for params, but it's alright because
338 			// we do not touch any of the params and put the qualifier back.
339 			auto r = *(cast(FunctionType*) &this);
340 			r.lnk = linkage;
341 			return *(cast(inout(FunctionType)*) &r);
342 		}
343 
344 		@property
345 		bool isVariadic() const {
346 			return variadic;
347 		}
348 
349 		@property
350 		bool isPure() const {
351 			return dpure;
352 		}
353 
354 		@property
355 		auto returnType() inout {
356 			return params[ctxCount + paramCount];
357 		}
358 
359 		@property
360 		auto contexts() inout {
361 			return params[0 .. ctxCount];
362 		}
363 
364 		@property
365 		auto parameters() inout {
366 			return params[ctxCount .. ctxCount + paramCount];
367 		}
368 	}
369 
370 public:
371 	mixin TypeAccessorMixin!K;
372 
373 	alias UnionType(T...) = UnionTypeTpl!(typeof(this), T);
374 
375 	auto getParamType(ParamKind kind) inout {
376 		return getPackedBitfield!ParamTuple(kind);
377 	}
378 
379 	auto asFunctionType() inout in {
380 		assert(kind == K.Function, "Not a function.");
381 	} do {
382 		return inout(FunctionType)(desc, payload.params);
383 	}
384 }
385 
386 mixin template TypeAccessorMixin(K) {
387 	@property
388 	K kind() const {
389 		return desc.kind;
390 	}
391 
392 	@property
393 	TypeQualifier qualifier() const {
394 		return desc.qualifier;
395 	}
396 
397 	auto unqual(this T)() {
398 		Desc d = desc;
399 		d.qualifier = TypeQualifier.Mutable;
400 		return T(d, payload);
401 	}
402 
403 	@property
404 	BuiltinType builtin() inout in {
405 		assert(kind == K.Builtin);
406 	} do {
407 		return cast(BuiltinType) desc.data;
408 	}
409 }
410 
411 struct TypeDescriptor(K, T...) {
412 	import util.bitfields;
413 	enum DataSize = ulong.sizeof * 8 - 3 - EnumSize!K - SizeOfBitField!T;
414 
415 	import std.bitmanip;
416 	mixin(bitfields!(
417 		// sdfmt off
418 		K, "kind", EnumSize!K,
419 		TypeQualifier, "qualifier", 3,
420 		ulong, "data", DataSize,
421 		T,
422 		// sdfmt on
423 	));
424 
425 	static assert(TypeDescriptor.sizeof == ulong.sizeof);
426 
427 	this(K k, TypeQualifier q, ulong d = 0) {
428 		kind = k;
429 		qualifier = q;
430 		data = d;
431 	}
432 }
433 
434 import std.typetuple;
435 alias ParamTuple = TypeTuple!(
436 	// sdfmt off
437 	ParamKind, "paramKind", 2,
438 	// sdfmt on
439 );