1 /** 2 * Perform constant folding. 3 * 4 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d, _optimize.d) 8 * Documentation: https://dlang.org/phobos/dmd_optimize.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/optimize.d 10 */ 11 12 module dmd.optimize; 13 14 import core.stdc.stdio; 15 16 import dmd.astenums; 17 import dmd.constfold; 18 import dmd.ctfeexpr; 19 import dmd.dcast; 20 import dmd.dclass; 21 import dmd.declaration; 22 import dmd.dsymbol; 23 import dmd.dsymbolsem; 24 import dmd.errors; 25 import dmd.expression; 26 import dmd.expressionsem; 27 import dmd.globals; 28 import dmd.hdrgen; 29 import dmd.init; 30 import dmd.location; 31 import dmd.mtype; 32 import dmd.printast; 33 import dmd.root.ctfloat; 34 import dmd.sideeffect; 35 import dmd.tokens; 36 import dmd.visitor; 37 38 /************************************* 39 * If variable has a const initializer, 40 * return that initializer. 41 * Returns: 42 * initializer if there is one, 43 * null if not, 44 * ErrorExp if error 45 */ 46 Expression expandVar(int result, VarDeclaration v) 47 { 48 //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null"); 49 50 /******** 51 * Params: 52 * e = initializer expression 53 */ 54 Expression initializerReturn(Expression e) 55 { 56 if (e.type != v.type) 57 { 58 e = e.castTo(null, v.type); 59 } 60 v.inuse++; 61 e = e.optimize(result); 62 v.inuse--; 63 //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars()); 64 return e; 65 } 66 67 static Expression nullReturn() 68 { 69 return null; 70 } 71 72 static Expression errorReturn() 73 { 74 return ErrorExp.get(); 75 } 76 77 if (!v) 78 return nullReturn(); 79 if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run 80 v.dsymbolSemantic(null); 81 if (v.type && 82 (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest)) 83 { 84 Type tb = v.type.toBasetype(); 85 if (v.storage_class & STC.manifest || 86 tb.isscalar() || 87 ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct))) 88 { 89 if (v._init) 90 { 91 if (v.inuse) 92 { 93 if (v.storage_class & STC.manifest) 94 { 95 .error(v.loc, "%s `%s` recursive initialization of constant", v.kind, v.toPrettyChars); 96 return errorReturn(); 97 } 98 return nullReturn(); 99 } 100 Expression ei = v.getConstInitializer(); 101 if (!ei) 102 { 103 if (v.storage_class & STC.manifest) 104 { 105 .error(v.loc, "%s `%s` enum cannot be initialized with `%s`", v.kind, v.toPrettyChars, dmd.hdrgen.toChars(v._init)); 106 return errorReturn(); 107 } 108 return nullReturn(); 109 } 110 if (ei.op == EXP.construct || ei.op == EXP.blit) 111 { 112 AssignExp ae = cast(AssignExp)ei; 113 ei = ae.e2; 114 if (ei.isConst() == 1) 115 { 116 } 117 else if (ei.op == EXP.string_) 118 { 119 // https://issues.dlang.org/show_bug.cgi?id=14459 120 // Do not constfold the string literal 121 // if it's typed as a C string, because the value expansion 122 // will drop the pointer identity. 123 if (!(result & WANTexpand) && ei.type.toBasetype().ty == Tpointer) 124 return nullReturn(); 125 } 126 else 127 return nullReturn(); 128 if (ei.type == v.type) 129 { 130 // const variable initialized with const expression 131 } 132 else if (ei.implicitConvTo(v.type) >= MATCH.constant) 133 { 134 // const var initialized with non-const expression 135 ei = ei.implicitCastTo(null, v.type); 136 ei = ei.expressionSemantic(null); 137 } 138 else 139 return nullReturn(); 140 } 141 else if (!(v.storage_class & STC.manifest) && 142 ei.isConst() != 1 && 143 ei.op != EXP.string_ && 144 ei.op != EXP.address) 145 { 146 return nullReturn(); 147 } 148 149 if (!ei.type) 150 { 151 return nullReturn(); 152 } 153 else 154 { 155 // Should remove the copy() operation by 156 // making all mods to expressions copy-on-write 157 return initializerReturn(ei.copy()); 158 } 159 } 160 else 161 { 162 // v does not have an initializer 163 version (all) 164 { 165 return nullReturn(); 166 } 167 else 168 { 169 // BUG: what if const is initialized in constructor? 170 auto e = v.type.defaultInit(); 171 e.loc = e1.loc; 172 return initializerReturn(e); 173 } 174 } 175 assert(0); 176 } 177 } 178 return nullReturn(); 179 } 180 181 private Expression fromConstInitializer(int result, Expression e1) 182 { 183 //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars()); 184 //static int xx; if (xx++ == 10) assert(0); 185 Expression e = e1; 186 if (auto ve = e1.isVarExp()) 187 { 188 VarDeclaration v = ve.var.isVarDeclaration(); 189 e = expandVar(result, v); 190 if (e) 191 { 192 // If it is a comma expression involving a declaration, we mustn't 193 // perform a copy -- we'd get two declarations of the same variable. 194 // See https://issues.dlang.org/show_bug.cgi?id=4465. 195 if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp()) 196 e = e1; 197 else if (e.type != e1.type && e1.type && e1.type.ty != Tident) 198 { 199 // Type 'paint' operation 200 e = e.copy(); 201 e.type = e1.type; 202 } 203 e.loc = e1.loc; 204 } 205 else 206 { 207 e = e1; 208 } 209 } 210 return e; 211 } 212 213 /*** 214 * It is possible for constant folding to change an array expression of 215 * unknown length, into one where the length is known. 216 * If the expression 'arr' is a literal, set lengthVar to be its length. 217 * Params: 218 * lengthVar = variable declaration for the `.length` property 219 * arr = String, ArrayLiteral, or of TypeSArray 220 */ 221 package void setLengthVarIfKnown(VarDeclaration lengthVar, Expression arr) 222 { 223 if (!lengthVar) 224 return; 225 if (lengthVar._init && !lengthVar._init.isVoidInitializer()) 226 return; // we have previously calculated the length 227 dinteger_t len; 228 if (auto se = arr.isStringExp()) 229 len = se.len; 230 else if (auto ale = arr.isArrayLiteralExp()) 231 len = ale.elements.length; 232 else 233 { 234 auto tsa = arr.type.toBasetype().isTypeSArray(); 235 if (!tsa) 236 return; // we don't know the length yet 237 len = tsa.dim.toInteger(); 238 } 239 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t); 240 lengthVar._init = new ExpInitializer(Loc.initial, dollar); 241 lengthVar.storage_class |= STC.static_ | STC.const_; 242 } 243 244 /*** 245 * Same as above, but determines the length from 'type'. 246 * Params: 247 * lengthVar = variable declaration for the `.length` property 248 * type = TypeSArray 249 */ 250 package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type) 251 { 252 if (!lengthVar) 253 return; 254 if (lengthVar._init && !lengthVar._init.isVoidInitializer()) 255 return; // we have previously calculated the length 256 auto tsa = type.toBasetype().isTypeSArray(); 257 if (!tsa) 258 return; // we don't know the length yet 259 const len = tsa.dim.toInteger(); 260 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t); 261 lengthVar._init = new ExpInitializer(Loc.initial, dollar); 262 lengthVar.storage_class |= STC.static_ | STC.const_; 263 } 264 265 /********************************* 266 * Constant fold an Expression. 267 * Params: 268 * e = expression to const fold; this may get modified in-place 269 * result = WANTvalue, WANTexpand, or both 270 * keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is 271 * an argument to a `ref` or `out` parameter, or the operand of `&` operator 272 * Returns: 273 * Constant folded version of `e` 274 */ 275 Expression Expression_optimize(Expression e, int result, bool keepLvalue) 276 { 277 //printf("Expression_optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue); 278 Expression ret = e; 279 280 void errorReturn() 281 { 282 ret = ErrorExp.get(); 283 } 284 285 /* Returns: true if error 286 */ 287 bool expOptimize(ref Expression e, int flags, bool keepLvalue = false) 288 { 289 if (!e) 290 return false; 291 Expression ex = Expression_optimize(e, flags, keepLvalue); 292 if (ex.op == EXP.error) 293 { 294 ret = ex; // store error result 295 return true; 296 } 297 else 298 { 299 e = ex; // modify original 300 return false; 301 } 302 } 303 304 bool unaOptimize(UnaExp e, int flags) 305 { 306 return expOptimize(e.e1, flags); 307 } 308 309 bool binOptimize(BinExp e, int flags, bool keepLhsLvalue = false) 310 { 311 return expOptimize(e.e1, flags, keepLhsLvalue) | 312 expOptimize(e.e2, flags); 313 } 314 315 void visitExp(Expression e) 316 { 317 //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars()); 318 } 319 320 void visitVar(VarExp e) 321 { 322 VarDeclaration v = e.var.isVarDeclaration(); 323 324 if (!(keepLvalue && v && !(v.storage_class & STC.manifest))) 325 ret = fromConstInitializer(result, e); 326 327 // if unoptimized, try to optimize the dtor expression 328 // (e.g., might be a LogicalExp with constant lhs) 329 if (ret == e && v && v.edtor) 330 { 331 // prevent infinite recursion (`<var>.~this()`) 332 if (!v.inuse) 333 { 334 v.inuse++; 335 expOptimize(v.edtor, WANTvalue); 336 v.inuse--; 337 } 338 } 339 } 340 341 void visitTuple(TupleExp e) 342 { 343 expOptimize(e.e0, WANTvalue); 344 foreach (ref ex; (*e.exps)[]) 345 { 346 expOptimize(ex, WANTvalue); 347 } 348 } 349 350 void visitArrayLiteral(ArrayLiteralExp e) 351 { 352 if (e.elements) 353 { 354 expOptimize(e.basis, result & WANTexpand); 355 foreach (ref ex; (*e.elements)[]) 356 { 357 expOptimize(ex, result & WANTexpand); 358 } 359 } 360 } 361 362 void visitAssocArrayLiteral(AssocArrayLiteralExp e) 363 { 364 assert(e.keys.length == e.values.length); 365 foreach (i, ref ekey; (*e.keys)[]) 366 { 367 expOptimize(ekey, result & WANTexpand); 368 expOptimize((*e.values)[i], result & WANTexpand); 369 } 370 } 371 372 void visitStructLiteral(StructLiteralExp e) 373 { 374 if (e.stageflags & stageOptimize) 375 return; 376 const old = e.stageflags; 377 e.stageflags |= stageOptimize; 378 if (e.elements) 379 { 380 foreach (ref ex; (*e.elements)[]) 381 { 382 expOptimize(ex, result & WANTexpand); 383 } 384 } 385 e.stageflags = old; 386 } 387 388 void visitUna(UnaExp e) 389 { 390 //printf("UnaExp::optimize() %s\n", e.toChars()); 391 if (unaOptimize(e, result)) 392 return; 393 } 394 395 void visitNeg(NegExp e) 396 { 397 if (unaOptimize(e, result)) 398 return; 399 if (e.e1.isConst() == 1) 400 { 401 ret = Neg(e.type, e.e1).copy(); 402 } 403 } 404 405 void visitCom(ComExp e) 406 { 407 if (unaOptimize(e, result)) 408 return; 409 if (e.e1.isConst() == 1) 410 { 411 ret = Com(e.type, e.e1).copy(); 412 } 413 } 414 415 void visitNop(NotExp e) 416 { 417 if (unaOptimize(e, result)) 418 return; 419 if (e.e1.isConst() == 1) 420 { 421 ret = Not(e.type, e.e1).copy(); 422 } 423 } 424 425 void visitSymOff(SymOffExp e) 426 { 427 assert(e.var); 428 } 429 430 void visitAddr(AddrExp e) 431 { 432 //printf("AddrExp::optimize(result = %d, keepLvalue = %d) %s\n", result, keepLvalue, e.toChars()); 433 /* Rewrite &(a,b) as (a,&b) 434 */ 435 if (auto ce = e.e1.isCommaExp()) 436 { 437 auto ae = new AddrExp(e.loc, ce.e2, e.type); 438 ret = new CommaExp(ce.loc, ce.e1, ae); 439 ret.type = e.type; 440 return; 441 } 442 // Keep lvalue-ness 443 if (expOptimize(e.e1, result, true)) 444 return; // error return 445 446 // Convert &*ex to ex 447 if (auto pe = e.e1.isPtrExp()) 448 { 449 Expression ex = pe.e1; 450 if (e.type.equals(ex.type)) 451 ret = ex; 452 else if (e.type.toBasetype().equivalent(ex.type.toBasetype())) 453 { 454 ret = ex.copy(); 455 ret.type = e.type; 456 } 457 return; 458 } 459 if (auto ve = e.e1.isVarExp()) 460 { 461 if (!ve.var.isReference() && !ve.var.isImportedSymbol()) 462 { 463 ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads); 464 ret.type = e.type; 465 return; 466 } 467 } 468 if (e.e1.isDotVarExp()) 469 { 470 /****************************** 471 * Run down the left side of the a.b.c expression to determine the 472 * leftmost variable being addressed (`a`), and accumulate the offsets of the `.b` and `.c`. 473 * Params: 474 * e = the DotVarExp or VarExp 475 * var = set to the VarExp at the end, or null if doesn't end in VarExp 476 * eint = set to the IntegerExp at the end, or null if doesn't end in IntegerExp 477 * offset = accumulation of all the .var offsets encountered 478 * Returns: true on error 479 */ 480 static bool getVarAndOffset(Expression e, out VarDeclaration var, out IntegerExp eint, ref uint offset) 481 { 482 if (e.type.size() == SIZE_INVALID) // trigger computation of v.offset 483 return true; 484 485 if (auto dve = e.isDotVarExp()) 486 { 487 auto v = dve.var.isVarDeclaration(); 488 if (!v || !v.isField() || v.isBitFieldDeclaration()) 489 return false; 490 491 if (getVarAndOffset(dve.e1, var, eint, offset)) 492 return true; 493 offset += v.offset; 494 } 495 else if (auto ve = e.isVarExp()) 496 { 497 if (!ve.var.isReference() && 498 !ve.var.isImportedSymbol() && 499 ve.var.isDataseg() && 500 ve.var.isCsymbol()) 501 { 502 var = ve.var.isVarDeclaration(); 503 } 504 } 505 else if (auto ep = e.isPtrExp()) 506 { 507 if (auto ei = ep.e1.isIntegerExp()) 508 { 509 eint = ei; 510 } 511 else if (auto se = ep.e1.isSymOffExp()) 512 { 513 if (!se.var.isReference() && 514 !se.var.isImportedSymbol() && 515 se.var.isDataseg()) 516 { 517 var = se.var.isVarDeclaration(); 518 offset += se.offset; 519 } 520 } 521 } 522 else if (auto ei = e.isIndexExp()) 523 { 524 if (auto ve = ei.e1.isVarExp()) 525 { 526 if (!ve.var.isReference() && 527 !ve.var.isImportedSymbol() && 528 ve.var.isDataseg() && 529 ve.var.isCsymbol()) 530 { 531 if (auto ie = ei.e2.isIntegerExp()) 532 { 533 var = ve.var.isVarDeclaration(); 534 offset += ie.toInteger() * ve.type.toBasetype().nextOf().size(); 535 } 536 } 537 } 538 } 539 return false; 540 } 541 542 uint offset; 543 VarDeclaration var; 544 IntegerExp eint; 545 if (getVarAndOffset(e.e1, var, eint, offset)) 546 { 547 ret = ErrorExp.get(); 548 return; 549 } 550 if (var) 551 { 552 ret = new SymOffExp(e.loc, var, offset, false); 553 ret.type = e.type; 554 return; 555 } 556 if (eint) 557 { 558 ret = new IntegerExp(e.loc, eint.toInteger() + offset, e.type); 559 return; 560 } 561 } 562 else if (auto ae = e.e1.isIndexExp()) 563 { 564 if (ae.e2.isIntegerExp() && ae.e1.isIndexExp()) 565 { 566 /* Rewrite `(a[i])[index]` to `(&a[i]) + index*size` 567 */ 568 sinteger_t index = ae.e2.toInteger(); 569 auto ae1 = ae.e1.isIndexExp(); // ae1 is a[i] 570 if (auto ts = ae1.type.isTypeSArray()) 571 { 572 sinteger_t dim = ts.dim.toInteger(); 573 574 if (index < 0 || index > dim) 575 { 576 error(e.loc, "array index %lld is out of bounds `[0..%lld]`", index, dim); 577 return errorReturn(); 578 } 579 580 import core.checkedint : mulu; 581 bool overflow; 582 const offset = mulu(index, ts.nextOf().size(e.loc), overflow); // offset = index*size 583 if (overflow) 584 { 585 error(e.loc, "array offset overflow"); 586 return errorReturn(); 587 } 588 589 Expression ex = new AddrExp(ae1.loc, ae1); // &a[i] 590 ex.type = ae1.type.pointerTo(); 591 592 Expression add = new AddExp(ae.loc, ex, new IntegerExp(ae.e2.loc, offset, ae.e2.type)); 593 add.type = e.type; 594 ret = Expression_optimize(add, result, keepLvalue); 595 return; 596 } 597 } 598 599 // Convert &array[n] to &array+n 600 if (ae.e2.isIntegerExp() && ae.e1.isVarExp()) 601 { 602 sinteger_t index = ae.e2.toInteger(); 603 VarExp ve = ae.e1.isVarExp(); 604 if (ve.type.isTypeSArray() && !ve.var.isImportedSymbol()) 605 { 606 TypeSArray ts = ve.type.isTypeSArray(); 607 sinteger_t dim = ts.dim.toInteger(); 608 if (index < 0 || index >= dim) 609 { 610 /* 0 for C static arrays means size is unknown, no need to check, 611 * and address one past the end is OK, too 612 */ 613 if (!((dim == 0 || dim == index) && ve.var.isCsymbol())) 614 { 615 error(e.loc, "array index %lld is out of bounds `[0..%lld]`", index, dim); 616 return errorReturn(); 617 } 618 } 619 620 import core.checkedint : mulu; 621 bool overflow; 622 const offset = mulu(index, ts.nextOf().size(e.loc), overflow); 623 if (overflow) 624 { 625 error(e.loc, "array offset overflow"); 626 return errorReturn(); 627 } 628 629 ret = new SymOffExp(e.loc, ve.var, offset); 630 ret.type = e.type; 631 return; 632 } 633 } 634 // Convert &((a.b)[index]) to (&a.b)+index*elementsize 635 else if (ae.e2.isIntegerExp() && ae.e1.isDotVarExp()) 636 { 637 sinteger_t index = ae.e2.toInteger(); 638 DotVarExp ve = ae.e1.isDotVarExp(); 639 if (ve.type.isTypeSArray() && ve.var.isField() && ve.e1.isPtrExp()) 640 { 641 TypeSArray ts = ve.type.isTypeSArray(); 642 sinteger_t dim = ts.dim.toInteger(); 643 if (index < 0 || index >= dim) 644 { 645 /* 0 for C static arrays means size is unknown, no need to check, 646 * and address one past the end is OK, too 647 */ 648 if (!((dim == 0 || dim == index) && ve.var.isCsymbol())) 649 { 650 error(e.loc, "array index %lld is out of bounds `[0..%lld]`", index, dim); 651 return errorReturn(); 652 } 653 } 654 655 import core.checkedint : mulu; 656 bool overflow; 657 const offset = mulu(index, ts.nextOf().size(e.loc), overflow); // index*elementsize 658 if (overflow) 659 { 660 error(e.loc, "array offset overflow"); 661 return errorReturn(); 662 } 663 664 auto pe = new AddrExp(e.loc, ve); 665 pe.type = e.type; 666 ret = new AddExp(e.loc, pe, new IntegerExp(e.loc, offset, Type.tsize_t)); 667 ret.type = e.type; 668 return; 669 } 670 } 671 } 672 } 673 674 void visitPtr(PtrExp e) 675 { 676 //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars()); 677 if (expOptimize(e.e1, result)) 678 return; 679 // Convert *&ex to ex 680 // But only if there is no type punning involved 681 if (auto ey = e.e1.isAddrExp()) 682 { 683 Expression ex = ey.e1; 684 if (e.type.equals(ex.type)) 685 ret = ex; 686 else if (e.type.toBasetype().equivalent(ex.type.toBasetype())) 687 { 688 ret = ex.copy(); 689 ret.type = e.type; 690 } 691 } 692 if (keepLvalue) 693 return; 694 // Constant fold *(&structliteral + offset) 695 if (e.e1.op == EXP.add) 696 { 697 Expression ex = Ptr(e.type, e.e1).copy(); 698 if (!CTFEExp.isCantExp(ex)) 699 { 700 ret = ex; 701 return; 702 } 703 } 704 if (auto se = e.e1.isSymOffExp()) 705 { 706 VarDeclaration v = se.var.isVarDeclaration(); 707 Expression ex = expandVar(result, v); 708 if (ex && ex.isStructLiteralExp()) 709 { 710 StructLiteralExp sle = ex.isStructLiteralExp(); 711 ex = sle.getField(e.type, cast(uint)se.offset); 712 if (ex && !CTFEExp.isCantExp(ex)) 713 { 714 ret = ex; 715 return; 716 } 717 } 718 } 719 } 720 721 void visitDotVar(DotVarExp e) 722 { 723 //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars()); 724 if (expOptimize(e.e1, result)) 725 return; 726 if (keepLvalue) 727 return; 728 Expression ex = e.e1; 729 if (auto ve = ex.isVarExp()) 730 { 731 VarDeclaration v = ve.var.isVarDeclaration(); 732 ex = expandVar(result, v); 733 } 734 if (ex && ex.isStructLiteralExp()) 735 { 736 StructLiteralExp sle = ex.isStructLiteralExp(); 737 VarDeclaration vf = e.var.isVarDeclaration(); 738 if (vf && !vf.overlapped) 739 { 740 /* https://issues.dlang.org/show_bug.cgi?id=13021 741 * Prevent optimization if vf has overlapped fields. 742 */ 743 ex = sle.getField(e.type, vf.offset); 744 if (ex && !CTFEExp.isCantExp(ex)) 745 { 746 ret = ex; 747 return; 748 } 749 } 750 } 751 } 752 753 void visitNew(NewExp e) 754 { 755 expOptimize(e.thisexp, WANTvalue); 756 // Optimize parameters 757 if (e.arguments) 758 { 759 foreach (ref arg; (*e.arguments)[]) 760 { 761 expOptimize(arg, WANTvalue); 762 } 763 } 764 } 765 766 void visitCall(CallExp e) 767 { 768 //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars()); 769 // Optimize parameters with keeping lvalue-ness 770 if (expOptimize(e.e1, result)) 771 return; 772 if (e.arguments) 773 { 774 // t1 can apparently be void for __ArrayDtor(T) calls 775 if (auto tf = e.calledFunctionType()) 776 { 777 foreach (i, ref arg; (*e.arguments)[]) 778 { 779 Parameter p = tf.parameterList[i]; 780 bool keep = p && p.isReference(); 781 expOptimize(arg, WANTvalue, keep); 782 } 783 } 784 } 785 } 786 787 void visitCast(CastExp e) 788 { 789 //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars()); 790 //printf("from %s to %s\n", e.type.toChars(), e.to.toChars()); 791 //printf("from %s\n", e.type.toChars()); 792 //printf("e1.type %s\n", e.e1.type.toChars()); 793 //printf("type = %p\n", e.type); 794 assert(e.type); 795 const op1 = e.e1.op; 796 Expression e1old = e.e1; 797 if (expOptimize(e.e1, result, keepLvalue)) 798 return; 799 if (!keepLvalue) 800 e.e1 = fromConstInitializer(result, e.e1); 801 if (e.e1 == e1old && e.e1.op == EXP.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray) 802 { 803 // Casting this will result in the same expression, and 804 // infinite loop because of Expression::implicitCastTo() 805 return; // no change 806 } 807 if ((e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral) && 808 (e.type.ty == Tpointer || e.type.ty == Tarray)) 809 { 810 const esz = e.type.nextOf().size(e.loc); 811 const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc); 812 if (esz == SIZE_INVALID || e1sz == SIZE_INVALID) 813 return errorReturn(); 814 815 if (e1sz == esz) 816 { 817 // https://issues.dlang.org/show_bug.cgi?id=12937 818 // If target type is void array, trying to paint 819 // e.e1 with that type will cause infinite recursive optimization. 820 if (e.type.nextOf().ty == Tvoid) 821 return; 822 ret = e.e1.castTo(null, e.type); 823 //printf(" returning1 %s\n", ret.toChars()); 824 return; 825 } 826 } 827 828 // Returning e.e1 with changing its type 829 void returnE_e1() 830 { 831 ret = (e1old == e.e1 ? e.e1.copy() : e.e1); 832 ret.type = e.type; 833 } 834 835 if (e.e1.op == EXP.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant) 836 { 837 //printf(" returning2 %s\n", e.e1.toChars()); 838 return returnE_e1(); 839 } 840 /* The first test here is to prevent infinite loops 841 */ 842 if (op1 != EXP.arrayLiteral && e.e1.op == EXP.arrayLiteral) 843 { 844 ret = e.e1.castTo(null, e.to); 845 return; 846 } 847 if (e.e1.op == EXP.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray)) 848 { 849 //printf(" returning3 %s\n", e.e1.toChars()); 850 return returnE_e1(); 851 } 852 if (e.type.ty == Tclass && e.e1.type.ty == Tclass) 853 { 854 import dmd.astenums : Sizeok; 855 856 // See if we can remove an unnecessary cast 857 ClassDeclaration cdfrom = e.e1.type.isClassHandle(); 858 ClassDeclaration cdto = e.type.isClassHandle(); 859 if (cdfrom.errors || cdto.errors) 860 return errorReturn(); 861 if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration()) 862 return returnE_e1(); // can always convert a class to Object 863 // Need to determine correct offset before optimizing away the cast. 864 // https://issues.dlang.org/show_bug.cgi?id=16980 865 if (cdfrom.size(e.loc) == SIZE_INVALID) 866 return errorReturn(); 867 assert(cdfrom.sizeok == Sizeok.done); 868 assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null)); 869 int offset; 870 if (cdto.isBaseOf(cdfrom, &offset) && offset == 0) 871 { 872 //printf(" returning4 %s\n", e.e1.toChars()); 873 return returnE_e1(); 874 } 875 } 876 if (e.e1.type.mutableOf().unSharedOf().equals(e.to.mutableOf().unSharedOf())) 877 { 878 //printf(" returning5 %s\n", e.e1.toChars()); 879 return returnE_e1(); 880 } 881 if (e.e1.isConst()) 882 { 883 if (e.e1.op == EXP.symbolOffset) 884 { 885 if (e.type.toBasetype().ty != Tsarray) 886 { 887 const esz = e.type.size(e.loc); 888 const e1sz = e.e1.type.size(e.e1.loc); 889 if (esz == SIZE_INVALID || 890 e1sz == SIZE_INVALID) 891 return errorReturn(); 892 893 if (esz == e1sz) 894 return returnE_e1(); 895 } 896 return; 897 } 898 if (e.to.toBasetype().ty != Tvoid) 899 { 900 if (e.e1.type.equals(e.type) && e.type.equals(e.to)) 901 ret = e.e1; 902 else 903 ret = Cast(e.loc, e.type, e.to, e.e1).copy(); 904 } 905 } 906 //printf(" returning6 %s\n", ret.toChars()); 907 } 908 909 void visitBinAssign(BinAssignExp e) 910 { 911 //printf("BinAssignExp::optimize(result = %d) %s\n", result, e.toChars()); 912 if (binOptimize(e, result, /*keepLhsLvalue*/ true)) 913 return; 914 if (e.op == EXP.leftShiftAssign || e.op == EXP.rightShiftAssign || e.op == EXP.unsignedRightShiftAssign) 915 { 916 if (e.e2.isConst() == 1) 917 { 918 sinteger_t i2 = e.e2.toInteger(); 919 uinteger_t sz = e.e1.type.size(e.e1.loc); 920 assert(sz != SIZE_INVALID); 921 sz *= 8; 922 if (i2 < 0 || i2 >= sz) 923 { 924 error(e.loc, "shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1); 925 return errorReturn(); 926 } 927 } 928 } 929 } 930 931 void visitBin(BinExp e) 932 { 933 //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars()); 934 const keepLhsLvalue = e.op == EXP.construct || e.op == EXP.blit || e.op == EXP.assign 935 || e.op == EXP.plusPlus || e.op == EXP.minusMinus 936 || e.op == EXP.prePlusPlus || e.op == EXP.preMinusMinus; 937 binOptimize(e, result, keepLhsLvalue); 938 } 939 940 void visitAdd(AddExp e) 941 { 942 //printf("AddExp::optimize(%s)\n", e.toChars()); 943 if (binOptimize(e, result)) 944 return; 945 if (e.e1.isConst() && e.e2.isConst()) 946 { 947 if (e.e1.op == EXP.symbolOffset && e.e2.op == EXP.symbolOffset) 948 return; 949 ret = Add(e.loc, e.type, e.e1, e.e2).copy(); 950 } 951 } 952 953 void visitMin(MinExp e) 954 { 955 //printf("MinExp::optimize(%s)\n", e.toChars()); 956 if (binOptimize(e, result)) 957 return; 958 if (e.e1.isConst() && e.e2.isConst()) 959 { 960 if (e.e2.op == EXP.symbolOffset) 961 return; 962 ret = Min(e.loc, e.type, e.e1, e.e2).copy(); 963 } 964 } 965 966 void visitMul(MulExp e) 967 { 968 //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars()); 969 if (binOptimize(e, result)) 970 return; 971 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 972 { 973 ret = Mul(e.loc, e.type, e.e1, e.e2).copy(); 974 } 975 } 976 977 void visitDiv(DivExp e) 978 { 979 //printf("DivExp::optimize(%s)\n", e.toChars()); 980 if (binOptimize(e, result)) 981 return; 982 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 983 { 984 ret = Div(e.loc, e.type, e.e1, e.e2).copy(); 985 } 986 } 987 988 void visitMod(ModExp e) 989 { 990 if (binOptimize(e, result)) 991 return; 992 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 993 { 994 ret = Mod(e.loc, e.type, e.e1, e.e2).copy(); 995 } 996 } 997 998 extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift) 999 { 1000 if (binOptimize(e, result)) 1001 return; 1002 if (e.e2.isConst() == 1) 1003 { 1004 sinteger_t i2 = e.e2.toInteger(); 1005 uinteger_t sz = e.e1.type.size(e.e1.loc); 1006 assert(sz != SIZE_INVALID); 1007 sz *= 8; 1008 if (i2 < 0 || i2 >= sz) 1009 { 1010 error(e.loc, "shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1); 1011 return errorReturn(); 1012 } 1013 if (e.e1.isConst() == 1) 1014 ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy(); 1015 } 1016 } 1017 1018 void visitShl(ShlExp e) 1019 { 1020 //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars()); 1021 shift_optimize(e, &Shl); 1022 } 1023 1024 void visitShr(ShrExp e) 1025 { 1026 //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars()); 1027 shift_optimize(e, &Shr); 1028 } 1029 1030 void visitUshr(UshrExp e) 1031 { 1032 //printf("UshrExp::optimize(result = %d) %s\n", result, toChars()); 1033 shift_optimize(e, &Ushr); 1034 } 1035 1036 void visitAnd(AndExp e) 1037 { 1038 if (binOptimize(e, result)) 1039 return; 1040 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 1041 ret = And(e.loc, e.type, e.e1, e.e2).copy(); 1042 } 1043 1044 void visitOr(OrExp e) 1045 { 1046 if (binOptimize(e, result)) 1047 return; 1048 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 1049 ret = Or(e.loc, e.type, e.e1, e.e2).copy(); 1050 } 1051 1052 void visitXor(XorExp e) 1053 { 1054 if (binOptimize(e, result)) 1055 return; 1056 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 1057 ret = Xor(e.loc, e.type, e.e1, e.e2).copy(); 1058 } 1059 1060 void visitPow(PowExp e) 1061 { 1062 if (binOptimize(e, result)) 1063 return; 1064 // All negative integral powers are illegal. 1065 if (e.e1.type.isintegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0) 1066 { 1067 error(e.loc, "cannot raise `%s` to a negative integer power. Did you mean `(cast(real)%s)^^%s` ?", e.e1.type.toBasetype().toChars(), e.e1.toChars(), e.e2.toChars()); 1068 return errorReturn(); 1069 } 1070 // If e2 *could* have been an integer, make it one. 1071 if (e.e2.op == EXP.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal())) 1072 { 1073 // This only applies to floating point, or positive integral powers. 1074 if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0) 1075 e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64); 1076 } 1077 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 1078 { 1079 Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy(); 1080 if (!CTFEExp.isCantExp(ex)) 1081 { 1082 ret = ex; 1083 return; 1084 } 1085 } 1086 } 1087 1088 void visitComma(CommaExp e) 1089 { 1090 //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars()); 1091 // Comma needs special treatment, because it may 1092 // contain compiler-generated declarations. We can interpret them, but 1093 // otherwise we must NOT attempt to constant-fold them. 1094 // In particular, if the comma returns a temporary variable, it needs 1095 // to be an lvalue (this is particularly important for struct constructors) 1096 expOptimize(e.e1, WANTvalue); 1097 expOptimize(e.e2, result, keepLvalue); 1098 if (ret.op == EXP.error) 1099 return; 1100 if (!e.e1 || e.e1.op == EXP.int64 || e.e1.op == EXP.float64 || !hasSideEffect(e.e1)) 1101 { 1102 ret = e.e2; 1103 if (ret) 1104 ret.type = e.type; 1105 } 1106 //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars()); 1107 } 1108 1109 void visitArrayLength(ArrayLengthExp e) 1110 { 1111 //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars()); 1112 if (unaOptimize(e, WANTexpand)) 1113 return; 1114 // CTFE interpret static immutable arrays (to get better diagnostics) 1115 if (auto ve = e.e1.isVarExp()) 1116 { 1117 VarDeclaration v = ve.var.isVarDeclaration(); 1118 if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init) 1119 { 1120 if (Expression ci = v.getConstInitializer()) 1121 e.e1 = ci; 1122 } 1123 } 1124 if (e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral || e.e1.op == EXP.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray || e.e1.op == EXP.null_) 1125 { 1126 ret = ArrayLength(e.type, e.e1).copy(); 1127 } 1128 } 1129 1130 void visitEqual(EqualExp e) 1131 { 1132 //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars()); 1133 if (binOptimize(e, WANTvalue)) 1134 return; 1135 Expression e1 = fromConstInitializer(result, e.e1); 1136 Expression e2 = fromConstInitializer(result, e.e2); 1137 if (e1.op == EXP.error) 1138 { 1139 ret = e1; 1140 return; 1141 } 1142 if (e2.op == EXP.error) 1143 { 1144 ret = e2; 1145 return; 1146 } 1147 ret = Equal(e.op, e.loc, e.type, e1, e2).copy(); 1148 if (CTFEExp.isCantExp(ret)) 1149 ret = e; 1150 } 1151 1152 void visitIdentity(IdentityExp e) 1153 { 1154 //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars()); 1155 if (binOptimize(e, WANTvalue)) 1156 return; 1157 if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == EXP.null_ && e.e2.op == EXP.null_)) 1158 { 1159 ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy(); 1160 if (CTFEExp.isCantExp(ret)) 1161 ret = e; 1162 } 1163 } 1164 1165 void visitIndex(IndexExp e) 1166 { 1167 //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars()); 1168 if (expOptimize(e.e1, result & WANTexpand)) 1169 return; 1170 Expression ex = fromConstInitializer(result, e.e1); 1171 // We might know $ now 1172 setLengthVarIfKnown(e.lengthVar, ex); 1173 if (expOptimize(e.e2, WANTvalue)) 1174 return; 1175 // Don't optimize to an array literal element directly in case an lvalue is requested 1176 if (keepLvalue && ex.op == EXP.arrayLiteral) 1177 return; 1178 ret = Index(e.type, ex, e.e2, e.indexIsInBounds).copy(); 1179 if (CTFEExp.isCantExp(ret) || (!ret.isErrorExp() && keepLvalue && !ret.isLvalue())) 1180 ret = e; 1181 } 1182 1183 void visitSlice(SliceExp e) 1184 { 1185 //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars()); 1186 if (expOptimize(e.e1, result & WANTexpand)) 1187 return; 1188 if (!e.lwr) 1189 { 1190 if (e.e1.op == EXP.string_) 1191 { 1192 // Convert slice of string literal into dynamic array 1193 Type t = e.e1.type.toBasetype(); 1194 if (Type tn = t.nextOf()) 1195 ret = e.e1.castTo(null, tn.arrayOf()); 1196 } 1197 } 1198 else 1199 { 1200 e.e1 = fromConstInitializer(result, e.e1); 1201 // We might know $ now 1202 setLengthVarIfKnown(e.lengthVar, e.e1); 1203 expOptimize(e.lwr, WANTvalue); 1204 expOptimize(e.upr, WANTvalue); 1205 if (ret.op == EXP.error) 1206 return; 1207 ret = Slice(e.type, e.e1, e.lwr, e.upr).copy(); 1208 if (CTFEExp.isCantExp(ret)) 1209 ret = e; 1210 } 1211 // https://issues.dlang.org/show_bug.cgi?id=14649 1212 // Leave the slice form so it might be 1213 // a part of array operation. 1214 // Assume that the backend codegen will handle the form `e[]` 1215 // as an equal to `e` itself. 1216 if (ret.op == EXP.string_) 1217 { 1218 e.e1 = ret; 1219 e.lwr = null; 1220 e.upr = null; 1221 ret = e; 1222 } 1223 //printf("-SliceExp::optimize() %s\n", ret.toChars()); 1224 } 1225 1226 void visitLogical(LogicalExp e) 1227 { 1228 //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars()); 1229 if (expOptimize(e.e1, WANTvalue)) 1230 return; 1231 const oror = e.op == EXP.orOr; 1232 if (e.e1.toBool().hasValue(oror)) 1233 { 1234 // Replace with (e1, oror) 1235 ret = IntegerExp.createBool(oror); 1236 ret = Expression.combine(e.e1, ret); 1237 if (e.type.toBasetype().ty == Tvoid) 1238 { 1239 ret = new CastExp(e.loc, ret, Type.tvoid); 1240 ret.type = e.type; 1241 } 1242 ret = Expression_optimize(ret, result, false); 1243 return; 1244 } 1245 expOptimize(e.e2, WANTvalue); 1246 if (e.e1.isConst()) 1247 { 1248 const e1Opt = e.e1.toBool(); 1249 if (e.e2.isConst()) 1250 { 1251 bool n1 = e1Opt.get(); 1252 bool n2 = e.e2.toBool().get(); 1253 ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type); 1254 } 1255 else if (e1Opt.hasValue(!oror)) 1256 { 1257 if (e.type.toBasetype().ty == Tvoid) 1258 ret = e.e2; 1259 else 1260 { 1261 ret = new CastExp(e.loc, e.e2, e.type); 1262 ret.type = e.type; 1263 } 1264 } 1265 } 1266 } 1267 1268 void visitCmp(CmpExp e) 1269 { 1270 //printf("CmpExp::optimize() %s\n", e.toChars()); 1271 if (binOptimize(e, WANTvalue)) 1272 return; 1273 Expression e1 = fromConstInitializer(result, e.e1); 1274 Expression e2 = fromConstInitializer(result, e.e2); 1275 ret = Cmp(e.op, e.loc, e.type, e1, e2).copy(); 1276 if (CTFEExp.isCantExp(ret)) 1277 ret = e; 1278 } 1279 1280 void visitCat(CatExp e) 1281 { 1282 //printf("CatExp::optimize(%d) %s\n", result, e.toChars()); 1283 if (binOptimize(e, result)) 1284 return; 1285 1286 if (e.type == Type.tstring) 1287 if (auto ce1 = e.e1.isCatExp()) 1288 { 1289 // https://issues.dlang.org/show_bug.cgi?id=12798 1290 // optimize ((expr ~ str1) ~ str2) 1291 // https://issues.dlang.org/show_bug.cgi?id=24078 1292 // This optimization is only valid if `expr` is a string. 1293 // Otherwise it leads to: 1294 // `["c"] ~ "a" ~ "b"` becoming `["c"] ~ "ab"` 1295 scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2); 1296 cex.type = e.type; 1297 Expression ex = Expression_optimize(cex, result, false); 1298 if (ex != cex) 1299 { 1300 e.e1 = ce1.e1; 1301 e.e2 = ex; 1302 } 1303 } 1304 // optimize "str"[] -> "str" 1305 if (auto se1 = e.e1.isSliceExp()) 1306 { 1307 if (se1.e1.op == EXP.string_ && !se1.lwr) 1308 e.e1 = se1.e1; 1309 } 1310 if (auto se2 = e.e2.isSliceExp()) 1311 { 1312 if (se2.e1.op == EXP.string_ && !se2.lwr) 1313 e.e2 = se2.e1; 1314 } 1315 ret = Cat(e.loc, e.type, e.e1, e.e2).copy(); 1316 if (CTFEExp.isCantExp(ret)) 1317 ret = e; 1318 } 1319 1320 void visitCond(CondExp e) 1321 { 1322 if (expOptimize(e.econd, WANTvalue)) 1323 return; 1324 const opt = e.econd.toBool(); 1325 if (opt.hasValue(true)) 1326 ret = Expression_optimize(e.e1, result, keepLvalue); 1327 else if (opt.hasValue(false)) 1328 ret = Expression_optimize(e.e2, result, keepLvalue); 1329 else 1330 { 1331 expOptimize(e.e1, result, keepLvalue); 1332 expOptimize(e.e2, result, keepLvalue); 1333 } 1334 } 1335 1336 // Optimize the expression until it can no longer be simplified. 1337 size_t b; 1338 while (1) 1339 { 1340 if (b++ == global.recursionLimit) 1341 { 1342 error(e.loc, "infinite loop while optimizing expression"); 1343 fatal(); 1344 } 1345 1346 auto ex = ret; 1347 switch (ex.op) 1348 { 1349 case EXP.variable: visitVar(ex.isVarExp()); break; 1350 case EXP.tuple: visitTuple(ex.isTupleExp()); break; 1351 case EXP.arrayLiteral: visitArrayLiteral(ex.isArrayLiteralExp()); break; 1352 case EXP.assocArrayLiteral: visitAssocArrayLiteral(ex.isAssocArrayLiteralExp()); break; 1353 case EXP.structLiteral: visitStructLiteral(ex.isStructLiteralExp()); break; 1354 1355 case EXP.import_: 1356 case EXP.assert_: 1357 case EXP.dotIdentifier: 1358 case EXP.dotTemplateDeclaration: 1359 case EXP.dotTemplateInstance: 1360 case EXP.delegate_: 1361 case EXP.dotType: 1362 case EXP.uadd: 1363 case EXP.delete_: 1364 case EXP.vector: 1365 case EXP.vectorArray: 1366 case EXP.array: 1367 case EXP.delegatePointer: 1368 case EXP.delegateFunctionPointer: 1369 case EXP.preMinusMinus: 1370 case EXP.prePlusPlus: visitUna(cast(UnaExp)ex); break; 1371 1372 case EXP.negate: visitNeg(ex.isNegExp()); break; 1373 case EXP.tilde: visitCom(ex.isComExp()); break; 1374 case EXP.not: visitNop(ex.isNotExp()); break; 1375 case EXP.symbolOffset: visitSymOff(ex.isSymOffExp()); break; 1376 case EXP.address: visitAddr(ex.isAddrExp()); break; 1377 case EXP.star: visitPtr(ex.isPtrExp()); break; 1378 case EXP.dotVariable: visitDotVar(ex.isDotVarExp()); break; 1379 case EXP.new_: visitNew(ex.isNewExp()); break; 1380 case EXP.call: visitCall(ex.isCallExp()); break; 1381 case EXP.cast_: visitCast(ex.isCastExp()); break; 1382 1383 case EXP.addAssign: 1384 case EXP.minAssign: 1385 case EXP.mulAssign: 1386 case EXP.divAssign: 1387 case EXP.modAssign: 1388 case EXP.andAssign: 1389 case EXP.orAssign: 1390 case EXP.xorAssign: 1391 case EXP.powAssign: 1392 case EXP.leftShiftAssign: 1393 case EXP.rightShiftAssign: 1394 case EXP.unsignedRightShiftAssign: 1395 case EXP.concatenateElemAssign: 1396 case EXP.concatenateDcharAssign: 1397 case EXP.concatenateAssign: visitBinAssign(ex.isBinAssignExp()); break; 1398 1399 case EXP.minusMinus: 1400 case EXP.plusPlus: 1401 case EXP.assign: 1402 case EXP.construct: 1403 case EXP.blit: 1404 case EXP.in_: 1405 case EXP.remove: 1406 case EXP.dot: visitBin(cast(BinExp)ex); break; 1407 1408 case EXP.add: visitAdd(ex.isAddExp()); break; 1409 case EXP.min: visitMin(ex.isMinExp()); break; 1410 case EXP.mul: visitMul(ex.isMulExp()); break; 1411 case EXP.div: visitDiv(ex.isDivExp()); break; 1412 case EXP.mod: visitMod(ex.isModExp()); break; 1413 case EXP.leftShift: visitShl(ex.isShlExp()); break; 1414 case EXP.rightShift: visitShr(ex.isShrExp()); break; 1415 case EXP.unsignedRightShift: visitUshr(ex.isUshrExp()); break; 1416 case EXP.and: visitAnd(ex.isAndExp()); break; 1417 case EXP.or: visitOr(ex.isOrExp()); break; 1418 case EXP.xor: visitXor(ex.isXorExp()); break; 1419 case EXP.pow: visitPow(ex.isPowExp()); break; 1420 case EXP.comma: visitComma(ex.isCommaExp()); break; 1421 case EXP.arrayLength: visitArrayLength(ex.isArrayLengthExp()); break; 1422 case EXP.notEqual: 1423 case EXP.equal: visitEqual(ex.isEqualExp()); break; 1424 case EXP.notIdentity: 1425 case EXP.identity: visitIdentity(ex.isIdentityExp()); break; 1426 case EXP.index: visitIndex(ex.isIndexExp()); break; 1427 case EXP.slice: visitSlice(ex.isSliceExp()); break; 1428 case EXP.andAnd: 1429 case EXP.orOr: visitLogical(ex.isLogicalExp()); break; 1430 case EXP.lessThan: 1431 case EXP.lessOrEqual: 1432 case EXP.greaterThan: 1433 case EXP.greaterOrEqual: visitCmp(cast(CmpExp)ex); break; 1434 case EXP.concatenate: visitCat(ex.isCatExp()); break; 1435 case EXP.question: visitCond(ex.isCondExp()); break; 1436 1437 default: visitExp(ex); break; 1438 } 1439 1440 if (ex == ret) 1441 break; 1442 } 1443 return ret; 1444 }