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;