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