1 module d.ir.expression;
2 
3 import d.ir.symbol;
4 import d.ir.type;
5 
6 import d.ast.expression;
7 
8 import source.context;
9 import source.location;
10 import source.name;
11 
12 abstract class Expression : AstExpression {
13 	Type type;
14 
15 	this(Location location, Type type) {
16 		super(location);
17 
18 		this.type = type;
19 	}
20 
21 	@property
22 	bool isLvalue() const {
23 		return false;
24 	}
25 }
26 
27 Expression build(E, T...)(T args)
28 		if (is(E : Expression) && is(typeof(new E(T.init)))) {
29 	import d.ir.error;
30 	if (auto ce = errorize(args)) {
31 		return ce.expression;
32 	}
33 
34 	return new E(args);
35 }
36 
37 alias TernaryExpression = d.ast.expression.TernaryExpression!Expression;
38 alias ArrayLiteral = d.ast.expression.ArrayLiteral!Expression;
39 alias StaticTypeidExpression =
40 	d.ast.expression.StaticTypeidExpression!(Type, Expression);
41 
42 alias UnaryOp = d.ast.expression.UnaryOp;
43 
44 /**
45  * Any expression that have a value known at compile time.
46  */
47 abstract class CompileTimeExpression : Expression {
48 	this(Location location, Type type) {
49 		super(location, type);
50 	}
51 }
52 
53 final:
54 class UnaryExpression : Expression {
55 	Expression expr;
56 	UnaryOp op;
57 
58 	this(Location location, Type type, UnaryOp op, Expression expr) {
59 		super(location, type);
60 
61 		this.expr = expr;
62 		this.op = op;
63 	}
64 
65 	invariant() {
66 		assert(expr);
67 	}
68 
69 	override string toString(const Context c) const {
70 		return unarizeString(expr.toString(c), op);
71 	}
72 
73 	@property
74 	override bool isLvalue() const {
75 		return op == UnaryOp.Dereference;
76 	}
77 }
78 
79 enum BinaryOp {
80 	Comma,
81 	Assign,
82 	Add,
83 	Sub,
84 	Mul,
85 	Pow,
86 	UDiv,
87 	SDiv,
88 	URem,
89 	SRem,
90 	Or,
91 	And,
92 	Xor,
93 	LeftShift,
94 	UnsignedRightShift,
95 	SignedRightShift,
96 	LogicalOr,
97 	LogicalAnd,
98 }
99 
100 class BinaryExpression : Expression {
101 	BinaryOp op;
102 
103 	Expression lhs;
104 	Expression rhs;
105 
106 	this(Location location, Type type, BinaryOp op, Expression lhs,
107 	     Expression rhs) {
108 		super(location, type);
109 
110 		this.op = op;
111 		this.lhs = lhs;
112 		this.rhs = rhs;
113 	}
114 
115 	override string toString(const Context c) const {
116 		import std.conv;
117 		return lhs.toString(c) ~ " " ~ to!string(op) ~ " " ~ rhs.toString(c);
118 	}
119 }
120 
121 enum ICmpOp {
122 	Equal,
123 	NotEqual,
124 	Greater,
125 	GreaterEqual,
126 	Less,
127 	LessEqual,
128 }
129 
130 /**
131  * Integral comparisons (integers, pointers, ...)
132  */
133 class ICmpExpression : Expression {
134 	ICmpOp op;
135 
136 	Expression lhs;
137 	Expression rhs;
138 
139 	this(Location location, ICmpOp op, Expression lhs, Expression rhs) {
140 		super(location, Type.get(BuiltinType.Bool));
141 
142 		this.op = op;
143 		this.lhs = lhs;
144 		this.rhs = rhs;
145 	}
146 
147 	override string toString(const Context c) const {
148 		import std.conv;
149 		return lhs.toString(c) ~ " " ~ to!string(op) ~ " " ~ rhs.toString(c);
150 	}
151 }
152 
153 enum FPCmpOp {
154 	Equal,
155 	NotEqual,
156 	Greater,
157 	GreaterEqual,
158 	Less,
159 	LessEqual,
160 
161 	// Weird float operators
162 	LessGreater,
163 	LessEqualGreater,
164 	UnorderedLess,
165 	UnorderedLessEqual,
166 	UnorderedGreater,
167 	UnorderedGreaterEqual,
168 	Unordered,
169 	UnorderedEqual,
170 }
171 
172 class FPCmpExpression : Expression {
173 	FPCmpOp op;
174 
175 	Expression lhs;
176 	Expression rhs;
177 
178 	this(Location location, FPCmpOp op, Expression lhs, Expression rhs) {
179 		super(location, Type.get(BuiltinType.Bool));
180 
181 		this.op = op;
182 		this.lhs = lhs;
183 		this.rhs = rhs;
184 	}
185 
186 	override string toString(const Context c) const {
187 		import std.conv;
188 		return lhs.toString(c) ~ " " ~ to!string(op) ~ " " ~ rhs.toString(c);
189 	}
190 }
191 
192 enum LifetimeOp {
193 	Copy,
194 	Consume,
195 	Destroy,
196 }
197 
198 class LifetimeExpression : Expression {
199 	import std.bitmanip;
200 	mixin(taggedClassRef!(
201 		// sdfmt off
202 		Expression, "value",
203 		LifetimeOp, "op", 2,
204 		// sdfmt on
205 	));
206 
207 	this(Location location, LifetimeOp op, Expression value) {
208 		super(location, value.type);
209 
210 		this.op = op;
211 		this.value = value;
212 	}
213 }
214 
215 class CallExpression : Expression {
216 	Expression callee;
217 	Expression[] args;
218 
219 	this(Location location, Type type, Expression callee, Expression[] args) {
220 		super(location, type);
221 
222 		this.callee = callee;
223 		this.args = args;
224 	}
225 
226 	override string toString(const Context c) const {
227 		import std.algorithm, std.range;
228 		auto aa = args.map!(a => a.toString(c)).join(", ");
229 		return callee.toString(c) ~ "(" ~ aa ~ ")";
230 	}
231 
232 	@property
233 	override bool isLvalue() const {
234 		return callee.type.asFunctionType().returnType.isRef;
235 	}
236 }
237 
238 enum Intrinsic {
239 	None,
240 	Expect,
241 	CompareAndSwap,
242 	CompareAndSwapWeak,
243 	PopCount,
244 	CountLeadingZeros,
245 	CountTrailingZeros,
246 	ByteSwap,
247 }
248 
249 /**
250  * This is where the compiler does its magic.
251  */
252 class IntrinsicExpression : Expression {
253 	Intrinsic intrinsic;
254 	Expression[] args;
255 
256 	this(Location location, Type type, Intrinsic intrinsic, Expression[] args) {
257 		super(location, type);
258 
259 		this.intrinsic = intrinsic;
260 		this.args = args;
261 	}
262 
263 	override string toString(const Context c) const {
264 		import std.algorithm, std.range, std.conv;
265 		auto aa = args.map!(a => a.toString(c)).join(", ");
266 		return "sdc.intrinsics." ~ intrinsic.to!string ~ "(" ~ aa ~ ")";
267 	}
268 }
269 
270 /**
271  * Index expression : indexed[arguments]
272  */
273 class IndexExpression : Expression {
274 	Expression indexed;
275 	Expression index;
276 
277 	this(Location location, Type type, Expression indexed, Expression index) {
278 		super(location, type);
279 
280 		this.indexed = indexed;
281 		this.index = index;
282 	}
283 
284 	override string toString(const Context c) const {
285 		return indexed.toString(c) ~ "[" ~ index.toString(c) ~ "]";
286 	}
287 
288 	@property
289 	override bool isLvalue() const {
290 		// FIXME: make this const compliant
291 		auto t = (cast() indexed.type).getCanonical();
292 		if (t.kind == TypeKind.Slice || t.kind == TypeKind.Pointer) {
293 			return true;
294 		}
295 
296 		return indexed.isLvalue;
297 	}
298 }
299 
300 /**
301  * Slice expression : [first .. second]
302  */
303 class SliceExpression : Expression {
304 	Expression sliced;
305 
306 	Expression first;
307 	Expression second;
308 
309 	this(Location location, Type type, Expression sliced, Expression first,
310 	     Expression second) {
311 		super(location, type);
312 
313 		this.sliced = sliced;
314 		this.first = first;
315 		this.second = second;
316 	}
317 
318 	override string toString(const Context c) const {
319 		return sliced.toString(c) ~ "[" ~ first.toString(c) ~ " .. "
320 			~ second.toString(c) ~ "]";
321 	}
322 }
323 
324 /**
325  * Expression that can in fact be several expressions.
326  * A good example is IdentifierExpression that resolve as overloaded functions.
327  */
328 class PolysemousExpression : Expression {
329 	Expression[] expressions;
330 
331 	this(Location location, Expression[] expressions) {
332 		super(location, Type.get(BuiltinType.None));
333 
334 		this.expressions = expressions;
335 	}
336 
337 	invariant() {
338 		assert(expressions.length > 1);
339 	}
340 }
341 
342 /**
343  * Super
344  */
345 class SuperExpression : Expression {
346 	this(Location location) {
347 		super(location, Type.get(BuiltinType.None));
348 	}
349 
350 	this(Location location, Type type) {
351 		super(location, type);
352 	}
353 
354 	override string toString(const Context) const {
355 		return "super";
356 	}
357 
358 	@property
359 	override bool isLvalue() const {
360 		return true;
361 	}
362 }
363 
364 /**
365  * Context
366  */
367 class ContextExpression : Expression {
368 	this(Location location, Function f) {
369 		super(location, Type.getContextType(f));
370 	}
371 
372 	override string toString(const Context) const {
373 		return "__ctx";
374 	}
375 
376 	@property
377 	override bool isLvalue() const {
378 		return true;
379 	}
380 }
381 
382 /**
383  * Virtual table
384  * XXX: This is highly dubious. Explore the alternatives and get rid of that.
385  */
386 class VtblExpression : Expression {
387 	Class dclass;
388 
389 	this(Location location, Class dclass) {
390 		super(location, Type.get(BuiltinType.Void).getPointer());
391 
392 		this.dclass = dclass;
393 	}
394 
395 	override string toString(const Context c) const {
396 		return dclass.toString(c) ~ ".__vtbl";
397 	}
398 }
399 
400 /**
401  * Boolean literal
402  */
403 class BooleanLiteral : CompileTimeExpression {
404 	bool value;
405 
406 	this(Location location, bool value) {
407 		super(location, Type.get(BuiltinType.Bool));
408 
409 		this.value = value;
410 	}
411 
412 	override string toString(const Context) const {
413 		return value ? "true" : "false";
414 	}
415 }
416 
417 /**
418  * Integer literal
419  */
420 class IntegerLiteral : CompileTimeExpression {
421 	ulong value;
422 
423 	this(Location location, ulong value, BuiltinType t) in {
424 		assert(isIntegral(t));
425 	} do {
426 		super(location, Type.get(t));
427 
428 		this.value = value;
429 	}
430 
431 	override string toString(const Context) const {
432 		import std.conv;
433 		return isSigned(type.builtin)
434 			? to!string(cast(long) value)
435 			: to!string(value);
436 	}
437 }
438 
439 /**
440  * Float literal
441  */
442 class FloatLiteral : CompileTimeExpression {
443 	double value;
444 
445 	this(Location location, double value, BuiltinType t) in {
446 		assert(isFloat(t));
447 	} do {
448 		super(location, Type.get(t));
449 
450 		this.value = value;
451 	}
452 
453 	override string toString(const Context) const {
454 		import std.conv;
455 		return to!string(value);
456 	}
457 }
458 
459 /**
460  * Character literal
461  */
462 class CharacterLiteral : CompileTimeExpression {
463 	dchar value;
464 
465 	this(Location location, dchar value, BuiltinType t) in {
466 		assert(isChar(t));
467 	} do {
468 		super(location, Type.get(t));
469 
470 		this.value = value;
471 	}
472 
473 	override string toString(const Context) const {
474 		import std.conv;
475 		return "'" ~ to!string(value) ~ "'";
476 	}
477 }
478 
479 /**
480  * String literal
481  */
482 class StringLiteral : CompileTimeExpression {
483 	string value;
484 
485 	this(Location location, string value) {
486 		super(location,
487 		      Type.get(BuiltinType.Char).getSlice(TypeQualifier.Immutable));
488 
489 		this.value = value;
490 	}
491 
492 	override string toString(const Context) const {
493 		return "\"" ~ value ~ "\"";
494 	}
495 }
496 
497 /**
498  * Null literal
499  */
500 class NullLiteral : CompileTimeExpression {
501 	this(Location location) {
502 		this(location, Type.get(BuiltinType.Null));
503 	}
504 
505 	this(Location location, Type t) {
506 		super(location, t);
507 	}
508 
509 	override string toString(const Context) const {
510 		return "null";
511 	}
512 }
513 
514 /**
515  * Cast expressions
516  */
517 enum CastKind {
518 	Invalid,
519 	IntToPtr,
520 	PtrToInt,
521 	Down,
522 	IntToBool,
523 	Trunc,
524 	UPad,
525 	SPad,
526 	Bit,
527 	Qual,
528 	Exact,
529 }
530 
531 class CastExpression : Expression {
532 	Expression expr;
533 
534 	CastKind kind;
535 
536 	this(Location location, CastKind kind, Type type, Expression expr) {
537 		super(location, type);
538 
539 		this.kind = kind;
540 		this.expr = expr;
541 	}
542 
543 	override string toString(const Context c) const {
544 		return "cast(" ~ type.toString(c) ~ ") " ~ expr.toString(c);
545 	}
546 
547 	@property
548 	override bool isLvalue() const {
549 		final switch (kind) with (CastKind) {
550 			case Invalid:
551 			case IntToPtr:
552 			case PtrToInt:
553 			case Down:
554 			case IntToBool:
555 			case Trunc:
556 			case UPad:
557 			case SPad:
558 				return false;
559 
560 			case Bit:
561 			case Qual:
562 			case Exact:
563 				return expr.isLvalue;
564 		}
565 	}
566 }
567 
568 /**
569  * new
570  */
571 class NewExpression : Expression {
572 	Expression dinit;
573 	Function ctor;
574 	Expression[] args;
575 
576 	this(Location location, Type type, Expression dinit, Function ctor,
577 	     Expression[] args) {
578 		super(location, type);
579 
580 		this.dinit = dinit;
581 		this.ctor = ctor;
582 		this.args = args;
583 	}
584 
585 	override string toString(const Context c) const {
586 		import std.algorithm, std.range;
587 		auto aa = args.map!(a => a.toString(c)).join(", ");
588 		return "new " ~ type.toString(c) ~ "(" ~ aa ~ ")";
589 	}
590 }
591 
592 /**
593  * IdentifierExpression that as been resolved as a Variable.
594  */
595 class VariableExpression : Expression {
596 	Variable var;
597 
598 	this(Location location, Variable var) {
599 		super(location, var.type);
600 
601 		this.var = var;
602 	}
603 
604 	override string toString(const Context c) const {
605 		return var.name.toString(c);
606 	}
607 
608 	@property
609 	override bool isLvalue() const {
610 		return var.storage != Storage.Enum;
611 	}
612 }
613 
614 /**
615  * Field access.
616  */
617 class FieldExpression : Expression {
618 	Expression expr;
619 	Field field;
620 
621 	this(Location location, Expression expr, Field field) {
622 		super(location, field.type);
623 
624 		this.expr = expr;
625 		this.field = field;
626 	}
627 
628 	override string toString(const Context c) const {
629 		return expr.toString(c) ~ "." ~ field.name.toString(c);
630 	}
631 
632 	@property
633 	override bool isLvalue() const {
634 		// FIXME: make this const compliant
635 		auto t = (cast() expr.type).getCanonical();
636 		if (t.kind == TypeKind.Class || t.kind == TypeKind.Pointer) {
637 			return true;
638 		}
639 
640 		return expr.isLvalue;
641 	}
642 }
643 
644 /**
645  * IdentifierExpression that as been resolved as a Function.
646  * XXX: Deserve to be merged with VariableExpression somehow.
647  */
648 class FunctionExpression : Expression {
649 	Function fun;
650 
651 	this(Location location, Function fun) {
652 		super(location, fun.type.getType());
653 
654 		this.fun = fun;
655 	}
656 
657 	override string toString(const Context c) const {
658 		return fun.name.toString(c);
659 	}
660 }
661 
662 /**
663  * Delegate from a function + contextes.
664  */
665 class DelegateExpression : Expression {
666 	Expression[] contexts;
667 	Function method;
668 
669 	this(Location location, Expression[] contexts, Function method) {
670 		super(location, method.type.getDelegate(contexts.length).getType());
671 
672 		this.contexts = contexts;
673 		this.method = method;
674 	}
675 
676 	override string toString(const Context c) const {
677 		return contexts[$ - 1].toString(c) ~ "." ~ method.name.toString(c);
678 	}
679 }
680 
681 /**
682  * For classes, typeid is computed at runtime.
683  */
684 class DynamicTypeidExpression : Expression {
685 	Expression argument;
686 
687 	this(Location location, Type type, Expression argument) {
688 		super(location, type);
689 
690 		this.argument = argument;
691 	}
692 
693 	override string toString(const Context c) const {
694 		return "typeid(" ~ argument.toString(c) ~ ")";
695 	}
696 }
697 
698 /**
699  * Used for type identifier = void;
700  */
701 class VoidInitializer : CompileTimeExpression {
702 	this(Location location, Type type) {
703 		super(location, type);
704 	}
705 
706 	override string toString(const Context) const {
707 		return "void";
708 	}
709 }
710 
711 /**
712  * tuples. Also used for struct initialization.
713  */
714 template TupleExpressionImpl(bool isCompileTime = false) {
715 	static if (isCompileTime) {
716 		alias E = CompileTimeExpression;
717 	} else {
718 		alias E = Expression;
719 	}
720 
721 	class TupleExpressionImpl : E {
722 		E[] values;
723 
724 		this(Location location, Type t, E[] values) {
725 			// Implement type tuples.
726 			super(location, t);
727 
728 			this.values = values;
729 		}
730 
731 		override string toString(const Context c) const {
732 			import std.algorithm, std.range;
733 			auto members = values.map!(v => v.toString(c)).join(", ");
734 
735 			// TODO: make this look nice for structs, classes, arrays...
736 			static if (isCompileTime) {
737 				return "ctTuple!(" ~ members ~ ")";
738 			} else {
739 				return "tuple(" ~ members ~ ")";
740 			}
741 		}
742 	}
743 }
744 
745 // XXX: required as long as 0 argument instanciation is not possible.
746 alias TupleExpression = TupleExpressionImpl!false;
747 alias CompileTimeTupleExpression = TupleExpressionImpl!true;