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;