1 module d.ast.type;
2 
3 public import d.common.builtintype;
4 public import d.common.qualifier;
5 
6 import source.context;
7 import d.common.type;
8 
9 enum AstTypeKind : ubyte {
10 	Builtin,
11 	Identifier,
12 
13 	// Type constructors
14 	Pointer,
15 	Slice,
16 	Array,
17 	Map,
18 	Bracket,
19 	Function,
20 
21 	// typeof
22 	TypeOf,
23 }
24 
25 struct AstType {
26 private:
27 	mixin TypeMixin!(AstTypeKind, Payload);
28 
29 	this(Desc d, inout Payload p = Payload.init) inout {
30 		desc = d;
31 		payload = p;
32 	}
33 
34 	import util.fastcast;
35 	this(Desc d, inout Identifier i) inout {
36 		this(d, fastCast!(inout Payload)(i));
37 	}
38 
39 	this(Desc d, inout AstExpression e) inout {
40 		this(d, fastCast!(inout Payload)(e));
41 	}
42 
43 	this(Desc d, inout ArrayPayload* a) inout {
44 		this(d, fastCast!(inout Payload)(a));
45 	}
46 
47 	this(Desc d, inout MapPayload* m) inout {
48 		this(d, fastCast!(inout Payload)(m));
49 	}
50 
51 	this(Desc d, inout BracketPayload* b) inout {
52 		this(d, fastCast!(inout Payload)(b));
53 	}
54 
55 	this(Desc d, inout AstType* t) inout {
56 		this(d, fastCast!(inout Payload)(t));
57 	}
58 
59 	AstType getConstructedType(this T)(AstTypeKind k, TypeQualifier q) in {
60 		assert(!isAuto, "Cannot build on top of auto type.");
61 	} do {
62 		return getConstructedMixin(k, q);
63 	}
64 
65 	auto acceptImpl(T)(T t) {
66 		final switch (kind) with (AstTypeKind) {
67 			case Builtin:
68 				return t.visit(builtin);
69 
70 			case Identifier:
71 				return t.visit(identifier);
72 
73 			case Pointer:
74 				return t.visitPointerOf(element);
75 
76 			case Slice:
77 				return t.visitSliceOf(element);
78 
79 			case Array:
80 				return t.visitArrayOf(size, element);
81 
82 			case Map:
83 				return t.visitMapOf(key, element);
84 
85 			case Bracket:
86 				return t.visitBracketOf(ikey, element);
87 
88 			case Function:
89 				return t.visit(asFunctionType());
90 
91 			case TypeOf:
92 				return desc.data ? t.visitTypeOfReturn() : t.visit(expression);
93 		}
94 	}
95 
96 public:
97 	auto accept(T)(ref T t) if (is(T == struct)) {
98 		return acceptImpl(&t);
99 	}
100 
101 	auto accept(T)(T t) if (is(T == class)) {
102 		return acceptImpl(t);
103 	}
104 
105 	AstType qualify(TypeQualifier q) {
106 		auto d = desc;
107 		d.qualifier = q.add(qualifier);
108 		return AstType(d, payload);
109 	}
110 
111 	AstType unqual() {
112 		auto d = desc;
113 		d.qualifier = TypeQualifier.Mutable;
114 		return AstType(d, payload);
115 	}
116 
117 	@property
118 	BuiltinType builtin() inout in {
119 		assert(kind == AstTypeKind.Builtin);
120 	} do {
121 		return cast(BuiltinType) desc.data;
122 	}
123 
124 	@property
125 	bool isAuto() inout {
126 		return payload.next is null && kind == AstTypeKind.Builtin
127 			&& builtin == BuiltinType.None;
128 	}
129 
130 	@property
131 	auto identifier() inout in {
132 		assert(kind == AstTypeKind.Identifier);
133 	} do {
134 		return payload.identifier;
135 	}
136 
137 	AstType getPointer(TypeQualifier q = TypeQualifier.Mutable) {
138 		return getConstructedType(AstTypeKind.Pointer, q);
139 	}
140 
141 	AstType getSlice(TypeQualifier q = TypeQualifier.Mutable) {
142 		return getConstructedType(AstTypeKind.Slice, q);
143 	}
144 
145 	AstType getArray(AstExpression size,
146 	                 TypeQualifier q = TypeQualifier.Mutable) in {
147 		assert(!isAuto, "Cannot build on top of auto type.");
148 	} do {
149 		return (payload.next is null && isPackable())
150 			? AstType(Desc(AstTypeKind.Array, q, raw_desc), size)
151 			: AstType(Desc(AstTypeKind.Array, q), new ArrayPayload(size, this));
152 	}
153 
154 	AstType getMap(AstType key, TypeQualifier q = TypeQualifier.Mutable) in {
155 		assert(!isAuto, "Cannot build on top of auto type.");
156 	} do {
157 		return AstType(Desc(AstTypeKind.Map, q), new MapPayload(key, this));
158 	}
159 
160 	AstType getBracket(Identifier ikey,
161 	                   TypeQualifier q = TypeQualifier.Mutable) in {
162 		assert(!isAuto, "Cannot build on top of auto type.");
163 	} do {
164 		return (payload.next is null && isPackable())
165 			? AstType(Desc(AstTypeKind.Bracket, q, raw_desc), ikey)
166 			: AstType(Desc(AstTypeKind.Bracket, q),
167 			          new BracketPayload(ikey, this));
168 	}
169 
170 	bool hasElement() const {
171 		return (kind >= AstTypeKind.Pointer) && (kind <= AstTypeKind.Bracket);
172 	}
173 
174 	@property
175 	auto element() inout in {
176 		assert(hasElement, "element called on a type with no element.");
177 	} do {
178 		if (kind < AstTypeKind.Array) {
179 			return getElementMixin();
180 		}
181 
182 		switch (kind) with (AstTypeKind) {
183 			case Array:
184 				return desc.data
185 					? inout(AstType)(getElementMixin().desc)
186 					: payload.array.type;
187 
188 			case Map:
189 				return payload.map.type;
190 
191 			case Bracket:
192 				return desc.data
193 					? inout(AstType)(getElementMixin().desc)
194 					: payload.bracket.type;
195 
196 			default:
197 				assert(0);
198 		}
199 	}
200 
201 	@property
202 	auto size() inout in {
203 		assert(kind == AstTypeKind.Array, "Only array have size.");
204 	} do {
205 		return desc.data ? payload.expr : payload.array.size;
206 	}
207 
208 	@property
209 	auto key() inout in {
210 		assert(kind == AstTypeKind.Map, "Only maps have key.");
211 	} do {
212 		return payload.map.key;
213 	}
214 
215 	@property
216 	auto ikey() inout in {
217 		assert(kind == AstTypeKind.Bracket,
218 		       "Only bracket[identifier] have ikey.");
219 	} do {
220 		return desc.data ? payload.identifier : payload.bracket.key;
221 	}
222 
223 	@property
224 	auto expression() inout in {
225 		assert(kind == AstTypeKind.TypeOf && desc.data == 0);
226 	} do {
227 		return payload.expr;
228 	}
229 
230 	@property
231 	bool isTypeOfReturn() inout {
232 		return kind == AstTypeKind.TypeOf && desc.data != 0;
233 	}
234 
235 	string toString(const Context c) const {
236 		auto s = toUnqualString(c);
237 
238 		final switch (qualifier) with (TypeQualifier) {
239 			case Mutable:
240 				return s;
241 
242 			case Inout:
243 				return "inout(" ~ s ~ ")";
244 
245 			case Const:
246 				return "const(" ~ s ~ ")";
247 
248 			case Shared:
249 				return "shared(" ~ s ~ ")";
250 
251 			case ConstShared:
252 				assert(0, "const shared isn't supported");
253 
254 			case Immutable:
255 				return "immutable(" ~ s ~ ")";
256 		}
257 	}
258 
259 	string toUnqualString(const Context c) const {
260 		final switch (kind) with (AstTypeKind) {
261 			case Builtin:
262 				import d.common.builtintype : toString;
263 				return toString(builtin);
264 
265 			case Identifier:
266 				return identifier.toString(c);
267 
268 			case Pointer:
269 				return element.toString(c) ~ "*";
270 
271 			case Slice:
272 				return element.toString(c) ~ "[]";
273 
274 			case Array:
275 				return element.toString(c) ~ "[" ~ size.toString(c) ~ "]";
276 
277 			case Map:
278 				return element.toString(c) ~ "[" ~ key.toString(c) ~ "]";
279 
280 			case Bracket:
281 				return element.toString(c) ~ "[" ~ ikey.toString(c) ~ "]";
282 
283 			case Function:
284 				auto f = asFunctionType();
285 				auto ret = f.returnType.toString(c);
286 				auto base = f.contexts.length ? " delegate(" : " function(";
287 				import std.algorithm, std.range;
288 				auto args = f.parameters.map!(p => p.toString(c)).join(", ");
289 				return ret ~ base ~ args ~ (f.isVariadic ? ", ...)" : ")");
290 
291 			case TypeOf:
292 				return desc.data
293 					? "typeof(return)"
294 					: "typeof(" ~ expression.toString(c) ~ ")";
295 		}
296 	}
297 
298 static:
299 	AstType get(BuiltinType bt, TypeQualifier q = TypeQualifier.Mutable) {
300 		Payload p; // Needed because of lolbug in inout
301 		return AstType(Desc(AstTypeKind.Builtin, q, bt), p);
302 	}
303 
304 	AstType getAuto(TypeQualifier q = TypeQualifier.Mutable) {
305 		return get(BuiltinType.None, q);
306 	}
307 
308 	AstType get(Identifier i, TypeQualifier q = TypeQualifier.Mutable) {
309 		return AstType(Desc(AstTypeKind.Identifier, q), i);
310 	}
311 
312 	AstType getTypeOf(AstExpression e,
313 	                  TypeQualifier q = TypeQualifier.Mutable) {
314 		return AstType(Desc(AstTypeKind.TypeOf, q), e);
315 	}
316 
317 	AstType getTypeOfReturn(TypeQualifier q = TypeQualifier.Mutable) {
318 		Payload p; // Needed because of lolbug in inout
319 		return AstType(Desc(AstTypeKind.TypeOf, q, 1), p);
320 	}
321 }
322 
323 unittest {
324 	AstType t;
325 	assert(t.isAuto);
326 	assert(t.qualifier == TypeQualifier.Mutable);
327 
328 	t = t.qualify(TypeQualifier.Immutable);
329 	assert(t.isAuto);
330 	assert(t.qualifier == TypeQualifier.Immutable);
331 
332 	t = AstType.getAuto(TypeQualifier.Const);
333 	assert(t.isAuto);
334 	assert(t.qualifier == TypeQualifier.Const);
335 
336 	auto l = AstType.get(BuiltinType.Long);
337 	auto p = l.getPointer();
338 	assert(p.element == l);
339 
340 	import source.location;
341 	auto s1 = new DollarExpression(Location.init);
342 	auto a1 = l.getArray(s1);
343 	assert(a1.size is s1);
344 	assert(a1.element == l);
345 
346 	auto s2 = new DollarExpression(Location.init);
347 	auto a2 = a1.getArray(s2);
348 	assert(a2.size is s2);
349 	assert(a2.element == a1);
350 
351 	auto f = AstType.get(BuiltinType.Float);
352 	auto m = l.getMap(f);
353 	assert(m.key == f);
354 	assert(m.element == l);
355 
356 	import source.name;
357 	auto i = new BasicIdentifier(Location.init, BuiltinName!"");
358 	t = AstType.get(i, TypeQualifier.Shared);
359 	assert(t.identifier is i);
360 	assert(t.qualifier is TypeQualifier.Shared);
361 
362 	auto b1 = l.getBracket(i);
363 	assert(b1.ikey is i);
364 	assert(b1.element == l);
365 
366 	auto b2 = b1.getBracket(i);
367 	assert(b2.ikey is i);
368 	assert(b2.element == b1);
369 
370 	auto e = new DollarExpression(Location.init);
371 	t = AstType.getTypeOf(e);
372 	assert(t.expression is e);
373 
374 	t = AstType.getTypeOfReturn();
375 
376 	import source.context;
377 	Context c;
378 	assert(t.toString(c) == "typeof(return)");
379 }
380 
381 alias ParamAstType = AstType.ParamType;
382 
383 string toString(const ParamAstType t, const Context c) {
384 	string s;
385 	final switch (t.paramKind) with (ParamKind) {
386 		case Regular:
387 			s = "";
388 			break;
389 
390 		case Final:
391 			s = "final ";
392 			break;
393 
394 		case Ref:
395 			s = "ref ";
396 			break;
397 	}
398 
399 	return s ~ t.getType().toString(c);
400 }
401 
402 inout(ParamAstType) getParamType(inout ParamAstType t, ParamKind kind) {
403 	return t.getType().getParamType(kind);
404 }
405 
406 alias FunctionAstType = AstType.FunctionType;
407 
408 private:
409 
410 // XXX: we put it as a UFCS property to avoid forward reference.
411 @property
412 inout(ParamAstType)* params(inout Payload p) {
413 	import util.fastcast;
414 	return cast(inout ParamAstType*) p.next;
415 }
416 
417 import d.ast.expression;
418 import d.ast.identifier;
419 
420 union Payload {
421 	AstType* next;
422 
423 	Identifier identifier;
424 
425 	ArrayPayload* array;
426 	MapPayload* map;
427 	BracketPayload* bracket;
428 
429 	AstExpression expr;
430 }
431 
432 struct ArrayPayload {
433 	AstExpression size;
434 	AstType type;
435 }
436 
437 struct MapPayload {
438 	AstType key;
439 	AstType type;
440 }
441 
442 struct BracketPayload {
443 	Identifier key;
444 	AstType type;
445 }