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