1 /** 2 * Performs inlining, which is an optimization pass enabled with the `-inline` flag. 3 * 4 * The AST is traversed, and every function call is considered for inlining using `inlinecost.d`. 5 * The function call is then inlined if this cost is below a threshold. 6 * 7 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 8 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 9 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 10 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/inline.d, _inline.d) 11 * Documentation: https://dlang.org/phobos/dmd_inline.html 12 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/inline.d 13 */ 14 15 module dmd.inline; 16 17 import core.stdc.stdio; 18 import core.stdc.string; 19 20 import dmd.aggregate; 21 import dmd.arraytypes; 22 import dmd.astenums; 23 import dmd.attrib; 24 import dmd.declaration; 25 import dmd.dmodule; 26 import dmd.dscope; 27 import dmd.dstruct; 28 import dmd.dsymbol; 29 import dmd.dtemplate; 30 import dmd.expression; 31 import dmd.errors; 32 import dmd.func; 33 import dmd.globals; 34 import dmd.id; 35 import dmd.identifier; 36 import dmd.init; 37 import dmd.initsem; 38 import dmd.location; 39 import dmd.mtype; 40 import dmd.opover; 41 import dmd.printast; 42 import dmd.postordervisitor; 43 import dmd.statement; 44 import dmd.tokens; 45 import dmd.visitor; 46 import dmd.inlinecost; 47 48 /*********************************************************** 49 * Scan function implementations in Module m looking for functions that can be inlined, 50 * and inline them in situ. 51 * 52 * Params: 53 * m = module to scan 54 */ 55 public void inlineScanModule(Module m) 56 { 57 if (m.semanticRun != PASS.semantic3done) 58 return; 59 m.semanticRun = PASS.inline; 60 61 // Note that modules get their own scope, from scratch. 62 // This is so regardless of where in the syntax a module 63 // gets imported, it is unaffected by context. 64 65 //printf("Module = %p\n", m.sc.scopesym); 66 67 foreach (i; 0 .. m.members.length) 68 { 69 Dsymbol s = (*m.members)[i]; 70 //if (global.params.v.verbose) 71 // message("inline scan symbol %s", s.toChars()); 72 inlineScanDsymbol(s); 73 } 74 m.semanticRun = PASS.inlinedone; 75 } 76 77 private void inlineScanDsymbol(Dsymbol s) 78 { 79 scope InlineScanVisitorDsymbol v = new InlineScanVisitorDsymbol(); 80 s.accept(v); 81 } 82 83 /*********************************************************** 84 * Perform the "inline copying" of a default argument for a function parameter. 85 * 86 * Todo: 87 * The hack for https://issues.dlang.org/show_bug.cgi?id=4820 case is still questionable. 88 * Perhaps would have to handle a delegate expression with 'null' context properly in front-end. 89 */ 90 public Expression inlineCopy(Expression e, Scope* sc) 91 { 92 /* See https://issues.dlang.org/show_bug.cgi?id=2935 93 * for explanation of why just a copy() is broken 94 */ 95 //return e.copy(); 96 if (auto de = e.isDelegateExp()) 97 { 98 if (de.func.isNested()) 99 { 100 /* https://issues.dlang.org/show_bug.cgi?id=4820 101 * Defer checking until later if we actually need the 'this' pointer 102 */ 103 return de.copy(); 104 } 105 } 106 int cost = inlineCostExpression(e); 107 if (cost >= COST_MAX) 108 { 109 error(e.loc, "cannot inline default argument `%s`", e.toChars()); 110 return ErrorExp.get(); 111 } 112 scope ids = new InlineDoState(sc.parent, null); 113 return doInlineAs!Expression(e, ids); 114 } 115 116 117 118 119 120 121 private: 122 123 124 125 enum LOG = false; 126 enum CANINLINE_LOG = false; 127 enum EXPANDINLINE_LOG = false; 128 129 130 /*********************************************************** 131 * Represent a context to inline statements and expressions. 132 * 133 * Todo: 134 * It would be better to make foundReturn an instance field of DoInlineAs visitor class, 135 * like as DoInlineAs!Result.result field, because it's one another result of inlining. 136 * The best would be to return a pair of result Expression and a bool value as foundReturn 137 * from doInlineAs function. 138 */ 139 private final class InlineDoState 140 { 141 // inline context 142 VarDeclaration vthis; 143 Dsymbols from; // old Dsymbols 144 Dsymbols to; // parallel array of new Dsymbols 145 Dsymbol parent; // new parent 146 FuncDeclaration fd; // function being inlined (old parent) 147 // inline result 148 bool foundReturn; 149 150 this(Dsymbol parent, FuncDeclaration fd) scope 151 { 152 this.parent = parent; 153 this.fd = fd; 154 } 155 } 156 157 /*********************************************************** 158 * Perform the inlining from (Statement or Expression) to (Statement or Expression). 159 * 160 * Inlining is done by: 161 * - Converting to an Expression 162 * - Copying the trees of the function to be inlined 163 * - Renaming the variables 164 */ 165 private extern (C++) final class DoInlineAs(Result) : Visitor 166 if (is(Result == Statement) || is(Result == Expression)) 167 { 168 alias visit = Visitor.visit; 169 public: 170 InlineDoState ids; 171 Result result; 172 173 enum asStatements = is(Result == Statement); 174 175 extern (D) this(InlineDoState ids) scope 176 { 177 this.ids = ids; 178 } 179 180 // Statement -> (Statement | Expression) 181 182 override void visit(Statement s) 183 { 184 printf("Statement.doInlineAs!%s()\n%s\n", Result.stringof.ptr, s.toChars()); 185 fflush(stdout); 186 assert(0); // default is we can't inline it 187 } 188 189 override void visit(ExpStatement s) 190 { 191 static if (LOG) 192 { 193 if (s.exp) 194 printf("ExpStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp.toChars()); 195 } 196 197 auto exp = doInlineAs!Expression(s.exp, ids); 198 static if (asStatements) 199 result = new ExpStatement(s.loc, exp); 200 else 201 result = exp; 202 } 203 204 override void visit(CompoundStatement s) 205 { 206 //printf("CompoundStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statements.length); 207 static if (asStatements) 208 { 209 auto as = new Statements(); 210 as.reserve(s.statements.length); 211 } 212 213 foreach (i, sx; *s.statements) 214 { 215 if (!sx) 216 continue; 217 static if (asStatements) 218 { 219 as.push(doInlineAs!Statement(sx, ids)); 220 } 221 else 222 { 223 /* Specifically allow: 224 * if (condition) 225 * return exp1; 226 * return exp2; 227 */ 228 IfStatement ifs; 229 Statement s3; 230 if ((ifs = sx.isIfStatement()) !is null && 231 ifs.ifbody && 232 ifs.ifbody.endsWithReturnStatement() && 233 !ifs.elsebody && 234 i + 1 < s.statements.length && 235 (s3 = (*s.statements)[i + 1]) !is null && 236 s3.endsWithReturnStatement() 237 ) 238 { 239 /* Rewrite as ?: 240 */ 241 auto econd = doInlineAs!Expression(ifs.condition, ids); 242 assert(econd); 243 auto e1 = doInlineAs!Expression(ifs.ifbody, ids); 244 assert(ids.foundReturn); 245 auto e2 = doInlineAs!Expression(s3, ids); 246 assert(e2); 247 Expression e = new CondExp(econd.loc, econd, e1, e2); 248 e.type = e1.type; 249 if (e.type.ty == Ttuple) 250 { 251 e1.type = Type.tvoid; 252 e2.type = Type.tvoid; 253 e.type = Type.tvoid; 254 } 255 result = Expression.combine(result, e); 256 } 257 else 258 { 259 ids.foundReturn = false; 260 auto e = doInlineAs!Expression(sx, ids); 261 result = Expression.combine(result, e); 262 } 263 } 264 265 if (ids.foundReturn) 266 break; 267 } 268 269 static if (asStatements) 270 result = new CompoundStatement(s.loc, as); 271 } 272 273 override void visit(UnrolledLoopStatement s) 274 { 275 //printf("UnrolledLoopStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statements.length); 276 static if (asStatements) 277 { 278 auto as = new Statements(); 279 as.reserve(s.statements.length); 280 } 281 282 foreach (sx; *s.statements) 283 { 284 if (!sx) 285 continue; 286 auto r = doInlineAs!Result(sx, ids); 287 static if (asStatements) 288 as.push(r); 289 else 290 result = Expression.combine(result, r); 291 292 if (ids.foundReturn) 293 break; 294 } 295 296 static if (asStatements) 297 result = new UnrolledLoopStatement(s.loc, as); 298 } 299 300 override void visit(ScopeStatement s) 301 { 302 //printf("ScopeStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statement.length); 303 auto r = doInlineAs!Result(s.statement, ids); 304 static if (asStatements) 305 result = new ScopeStatement(s.loc, r, s.endloc); 306 else 307 result = r; 308 } 309 310 override void visit(IfStatement s) 311 { 312 assert(!s.prm); 313 auto econd = doInlineAs!Expression(s.condition, ids); 314 assert(econd); 315 316 auto ifbody = doInlineAs!Result(s.ifbody, ids); 317 bool bodyReturn = ids.foundReturn; 318 319 ids.foundReturn = false; 320 auto elsebody = doInlineAs!Result(s.elsebody, ids); 321 322 static if (asStatements) 323 { 324 result = new IfStatement(s.loc, s.prm, econd, ifbody, elsebody, s.endloc); 325 } 326 else 327 { 328 alias e1 = ifbody; 329 alias e2 = elsebody; 330 if (e1 && e2) 331 { 332 result = new CondExp(econd.loc, econd, e1, e2); 333 result.type = e1.type; 334 if (result.type.ty == Ttuple) 335 { 336 e1.type = Type.tvoid; 337 e2.type = Type.tvoid; 338 result.type = Type.tvoid; 339 } 340 } 341 else if (e1) 342 { 343 result = new LogicalExp(econd.loc, EXP.andAnd, econd, e1); 344 result.type = Type.tvoid; 345 } 346 else if (e2) 347 { 348 result = new LogicalExp(econd.loc, EXP.orOr, econd, e2); 349 result.type = Type.tvoid; 350 } 351 else 352 { 353 result = econd; 354 } 355 } 356 ids.foundReturn = ids.foundReturn && bodyReturn; 357 } 358 359 override void visit(ReturnStatement s) 360 { 361 //printf("ReturnStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp ? s.exp.toChars() : ""); 362 ids.foundReturn = true; 363 364 auto exp = doInlineAs!Expression(s.exp, ids); 365 if (!exp) // https://issues.dlang.org/show_bug.cgi?id=14560 366 // 'return' must not leave in the expand result 367 return; 368 static if (asStatements) 369 { 370 /* Any return statement should be the last statement in the function being 371 * inlined, otherwise things shouldn't have gotten this far. Since the 372 * return value is being ignored (otherwise it wouldn't be inlined as a statement) 373 * we only need to evaluate `exp` for side effects. 374 * Already disallowed this if `exp` produces an object that needs destruction - 375 * an enhancement would be to do the destruction here. 376 */ 377 result = new ExpStatement(s.loc, exp); 378 } 379 else 380 result = exp; 381 } 382 383 override void visit(ImportStatement s) 384 { 385 //printf("ImportStatement.doInlineAs!%s()\n", Result.stringof.ptr); 386 } 387 388 override void visit(ForStatement s) 389 { 390 //printf("ForStatement.doInlineAs!%s()\n", Result.stringof.ptr); 391 static if (asStatements) 392 { 393 auto sinit = doInlineAs!Statement(s._init, ids); 394 auto scond = doInlineAs!Expression(s.condition, ids); 395 auto sincr = doInlineAs!Expression(s.increment, ids); 396 auto sbody = doInlineAs!Statement(s._body, ids); 397 result = new ForStatement(s.loc, sinit, scond, sincr, sbody, s.endloc); 398 } 399 else 400 result = null; // cannot be inlined as an Expression 401 } 402 403 override void visit(ThrowStatement s) 404 { 405 //printf("ThrowStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp.toChars()); 406 static if (asStatements) 407 result = new ThrowStatement(s.loc, doInlineAs!Expression(s.exp, ids)); 408 else 409 result = null; // cannot be inlined as an Expression 410 } 411 412 // Expression -> (Statement | Expression) 413 414 static if (asStatements) 415 { 416 override void visit(Expression e) 417 { 418 result = new ExpStatement(e.loc, doInlineAs!Expression(e, ids)); 419 } 420 } 421 else 422 { 423 /****************************** 424 * Perform doInlineAs() on an array of Expressions. 425 */ 426 Expressions* arrayExpressionDoInline(Expressions* a) 427 { 428 if (!a) 429 return null; 430 431 auto newa = new Expressions(a.length); 432 433 foreach (i; 0 .. a.length) 434 { 435 (*newa)[i] = doInlineAs!Expression((*a)[i], ids); 436 } 437 return newa; 438 } 439 440 override void visit(Expression e) 441 { 442 //printf("Expression.doInlineAs!%s(%s): %s\n", Result.stringof.ptr, EXPtoString(e.op).ptr, e.toChars()); 443 result = e.copy(); 444 } 445 446 override void visit(SymOffExp e) 447 { 448 //printf("SymOffExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars()); 449 foreach (i; 0 .. ids.from.length) 450 { 451 if (e.var != ids.from[i]) 452 continue; 453 auto se = e.copy().isSymOffExp(); 454 se.var = ids.to[i].isDeclaration(); 455 result = se; 456 return; 457 } 458 result = e; 459 } 460 461 override void visit(VarExp e) 462 { 463 //printf("VarExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars()); 464 foreach (i; 0 .. ids.from.length) 465 { 466 if (e.var != ids.from[i]) 467 continue; 468 auto ve = e.copy().isVarExp(); 469 ve.var = ids.to[i].isDeclaration(); 470 result = ve; 471 return; 472 } 473 if (ids.fd && e.var == ids.fd.vthis) 474 { 475 result = new VarExp(e.loc, ids.vthis); 476 if (ids.fd.hasDualContext()) 477 result = new AddrExp(e.loc, result); 478 result.type = e.type; 479 return; 480 } 481 482 /* Inlining context pointer access for nested referenced variables. 483 * For example: 484 * auto fun() { 485 * int i = 40; 486 * auto foo() { 487 * int g = 2; 488 * struct Result { 489 * auto bar() { return i + g; } 490 * } 491 * return Result(); 492 * } 493 * return foo(); 494 * } 495 * auto t = fun(); 496 * 'i' and 'g' are nested referenced variables in Result.bar(), so: 497 * auto x = t.bar(); 498 * should be inlined to: 499 * auto x = *(t.vthis.vthis + i.voffset) + *(t.vthis + g.voffset) 500 */ 501 auto v = e.var.isVarDeclaration(); 502 if (v && v.nestedrefs.length && ids.vthis) 503 { 504 Dsymbol s = ids.fd; 505 auto fdv = v.toParent().isFuncDeclaration(); 506 assert(fdv); 507 result = new VarExp(e.loc, ids.vthis); 508 result.type = ids.vthis.type; 509 if (ids.fd.hasDualContext()) 510 { 511 // &__this 512 result = new AddrExp(e.loc, result); 513 result.type = ids.vthis.type.pointerTo(); 514 } 515 while (s != fdv) 516 { 517 auto f = s.isFuncDeclaration(); 518 AggregateDeclaration ad; 519 if (f && f.hasDualContext()) 520 { 521 if (f.hasNestedFrameRefs()) 522 { 523 result = new DotVarExp(e.loc, result, f.vthis); 524 result.type = f.vthis.type; 525 } 526 // (*__this)[i] 527 uint i = f.followInstantiationContext(fdv); 528 if (i == 1 && f == ids.fd) 529 { 530 auto ve = e.copy().isVarExp(); 531 ve.originalScope = ids.fd; 532 result = ve; 533 return; 534 } 535 result = new PtrExp(e.loc, result); 536 result.type = Type.tvoidptr.sarrayOf(2); 537 auto ie = new IndexExp(e.loc, result, new IntegerExp(i)); 538 ie.indexIsInBounds = true; // no runtime bounds checking 539 result = ie; 540 result.type = Type.tvoidptr; 541 s = f.toParentP(fdv); 542 ad = s.isAggregateDeclaration(); 543 if (ad) 544 goto Lad; 545 continue; 546 } 547 else if ((ad = s.isThis()) !is null) 548 { 549 Lad: 550 while (ad) 551 { 552 assert(ad.vthis); 553 bool i = ad.followInstantiationContext(fdv); 554 auto vthis = i ? ad.vthis2 : ad.vthis; 555 result = new DotVarExp(e.loc, result, vthis); 556 result.type = vthis.type; 557 s = ad.toParentP(fdv); 558 ad = s.isAggregateDeclaration(); 559 } 560 } 561 else if (f && f.isNested()) 562 { 563 assert(f.vthis); 564 if (f.hasNestedFrameRefs()) 565 { 566 result = new DotVarExp(e.loc, result, f.vthis); 567 result.type = f.vthis.type; 568 } 569 s = f.toParent2(); 570 } 571 else 572 assert(0); 573 assert(s); 574 } 575 result = new DotVarExp(e.loc, result, v); 576 result.type = v.type; 577 //printf("\t==> result = %s, type = %s\n", result.toChars(), result.type.toChars()); 578 return; 579 } 580 else if (v && v.nestedrefs.length) 581 { 582 auto ve = e.copy().isVarExp(); 583 ve.originalScope = ids.fd; 584 result = ve; 585 return; 586 } 587 588 result = e; 589 } 590 591 override void visit(ThisExp e) 592 { 593 //if (!ids.vthis) 594 // e.error("no `this` when inlining `%s`", ids.parent.toChars()); 595 if (!ids.vthis) 596 { 597 result = e; 598 return; 599 } 600 result = new VarExp(e.loc, ids.vthis); 601 if (ids.fd.hasDualContext()) 602 { 603 // __this[0] 604 result.type = ids.vthis.type; 605 auto ie = new IndexExp(e.loc, result, IntegerExp.literal!0); 606 ie.indexIsInBounds = true; // no runtime bounds checking 607 result = ie; 608 if (e.type.ty == Tstruct) 609 { 610 result.type = e.type.pointerTo(); 611 result = new PtrExp(e.loc, result); 612 } 613 } 614 result.type = e.type; 615 } 616 617 override void visit(SuperExp e) 618 { 619 assert(ids.vthis); 620 result = new VarExp(e.loc, ids.vthis); 621 if (ids.fd.hasDualContext()) 622 { 623 // __this[0] 624 result.type = ids.vthis.type; 625 auto ie = new IndexExp(e.loc, result, IntegerExp.literal!0); 626 ie.indexIsInBounds = true; // no runtime bounds checking 627 result = ie; 628 } 629 result.type = e.type; 630 } 631 632 override void visit(DeclarationExp e) 633 { 634 //printf("DeclarationExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars()); 635 if (auto vd = e.declaration.isVarDeclaration()) 636 { 637 version (none) 638 { 639 // Need to figure this out before inlining can work for tuples 640 if (auto tup = vd.toAlias().isTupleDeclaration()) 641 { 642 tup.foreachVar((s) { s; }); 643 result = st.objects.length; 644 return; 645 } 646 } 647 if (vd.isStatic()) 648 return; 649 650 if (ids.fd && vd == ids.fd.nrvo_var) 651 { 652 foreach (i; 0 .. ids.from.length) 653 { 654 if (vd != ids.from[i]) 655 continue; 656 if (vd._init && !vd._init.isVoidInitializer()) 657 { 658 result = vd._init.initializerToExpression(); 659 assert(result); 660 result = doInlineAs!Expression(result, ids); 661 } 662 else 663 result = IntegerExp.literal!0; 664 return; 665 } 666 } 667 668 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init); 669 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration)); 670 vto.parent = ids.parent; 671 vto.csym = null; 672 vto.isym = null; 673 674 ids.from.push(vd); 675 ids.to.push(vto); 676 677 if (vd._init) 678 { 679 if (vd._init.isVoidInitializer()) 680 { 681 vto._init = new VoidInitializer(vd._init.loc); 682 } 683 else 684 { 685 auto ei = vd._init.initializerToExpression(); 686 assert(ei); 687 vto._init = new ExpInitializer(ei.loc, doInlineAs!Expression(ei, ids)); 688 } 689 } 690 if (vd.edtor) 691 { 692 vto.edtor = doInlineAs!Expression(vd.edtor, ids); 693 } 694 auto de = e.copy().isDeclarationExp(); 695 de.declaration = vto; 696 result = de; 697 return; 698 } 699 700 // Prevent the copy of the aggregates allowed in inlineable funcs 701 if (isInlinableNestedAggregate(e)) 702 return; 703 704 /* This needs work, like DeclarationExp.toElem(), if we are 705 * to handle TemplateMixin's. For now, we just don't inline them. 706 */ 707 visit(cast(Expression)e); 708 } 709 710 override void visit(TypeidExp e) 711 { 712 //printf("TypeidExp.doInlineAs!%s(): %s\n", Result.stringof.ptr, e.toChars()); 713 auto te = e.copy().isTypeidExp(); 714 if (auto ex = isExpression(te.obj)) 715 { 716 te.obj = doInlineAs!Expression(ex, ids); 717 } 718 else 719 assert(isType(te.obj)); 720 result = te; 721 } 722 723 override void visit(NewExp e) 724 { 725 //printf("NewExp.doInlineAs!%s(): %s\n", Result.stringof.ptr, e.toChars()); 726 auto ne = e.copy().isNewExp(); 727 auto lowering = ne.lowering; 728 if (lowering) 729 if (auto ce = lowering.isCallExp()) 730 if (ce.f.ident == Id._d_newarrayT) 731 { 732 ne.lowering = doInlineAs!Expression(lowering, ids); 733 goto LhasLowering; 734 } 735 736 ne.thisexp = doInlineAs!Expression(e.thisexp, ids); 737 ne.argprefix = doInlineAs!Expression(e.argprefix, ids); 738 ne.arguments = arrayExpressionDoInline(e.arguments); 739 740 LhasLowering: 741 result = ne; 742 743 semanticTypeInfo(null, e.type); 744 } 745 746 override void visit(UnaExp e) 747 { 748 auto ue = cast(UnaExp)e.copy(); 749 ue.e1 = doInlineAs!Expression(e.e1, ids); 750 result = ue; 751 } 752 753 override void visit(AssertExp e) 754 { 755 auto ae = e.copy().isAssertExp(); 756 ae.e1 = doInlineAs!Expression(e.e1, ids); 757 ae.msg = doInlineAs!Expression(e.msg, ids); 758 result = ae; 759 } 760 761 override void visit(CatExp e) 762 { 763 auto ce = e.copy().isCatExp(); 764 765 if (auto lowering = ce.lowering) 766 ce.lowering = doInlineAs!Expression(lowering, ids); 767 else 768 { 769 ce.e1 = doInlineAs!Expression(e.e1, ids); 770 ce.e2 = doInlineAs!Expression(e.e2, ids); 771 } 772 773 result = ce; 774 } 775 776 override void visit(BinExp e) 777 { 778 auto be = cast(BinExp)e.copy(); 779 be.e1 = doInlineAs!Expression(e.e1, ids); 780 be.e2 = doInlineAs!Expression(e.e2, ids); 781 result = be; 782 } 783 784 override void visit(CallExp e) 785 { 786 auto ce = e.copy().isCallExp(); 787 ce.e1 = doInlineAs!Expression(e.e1, ids); 788 ce.arguments = arrayExpressionDoInline(e.arguments); 789 result = ce; 790 } 791 792 override void visit(AssignExp e) 793 { 794 visit(cast(BinExp)e); 795 } 796 797 override void visit(LoweredAssignExp e) 798 { 799 result = doInlineAs!Expression(e.lowering, ids); 800 } 801 802 override void visit(EqualExp e) 803 { 804 visit(cast(BinExp)e); 805 806 Type t1 = e.e1.type.toBasetype(); 807 if (t1.ty == Tarray || t1.ty == Tsarray) 808 { 809 Type t = t1.nextOf().toBasetype(); 810 while (t.toBasetype().nextOf()) 811 t = t.nextOf().toBasetype(); 812 if (t.ty == Tstruct) 813 semanticTypeInfo(null, t); 814 } 815 else if (t1.ty == Taarray) 816 { 817 semanticTypeInfo(null, t1); 818 } 819 } 820 821 override void visit(IndexExp e) 822 { 823 auto are = e.copy().isIndexExp(); 824 are.e1 = doInlineAs!Expression(e.e1, ids); 825 if (e.lengthVar) 826 { 827 //printf("lengthVar\n"); 828 auto vd = e.lengthVar; 829 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init); 830 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration)); 831 vto.parent = ids.parent; 832 vto.csym = null; 833 vto.isym = null; 834 835 ids.from.push(vd); 836 ids.to.push(vto); 837 838 if (vd._init && !vd._init.isVoidInitializer()) 839 { 840 auto ie = vd._init.isExpInitializer(); 841 assert(ie); 842 vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids)); 843 } 844 are.lengthVar = vto; 845 } 846 are.e2 = doInlineAs!Expression(e.e2, ids); 847 result = are; 848 } 849 850 override void visit(SliceExp e) 851 { 852 auto are = e.copy().isSliceExp(); 853 are.e1 = doInlineAs!Expression(e.e1, ids); 854 if (e.lengthVar) 855 { 856 //printf("lengthVar\n"); 857 auto vd = e.lengthVar; 858 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init); 859 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration)); 860 vto.parent = ids.parent; 861 vto.csym = null; 862 vto.isym = null; 863 864 ids.from.push(vd); 865 ids.to.push(vto); 866 867 if (vd._init && !vd._init.isVoidInitializer()) 868 { 869 auto ie = vd._init.isExpInitializer(); 870 assert(ie); 871 vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids)); 872 } 873 874 are.lengthVar = vto; 875 } 876 are.lwr = doInlineAs!Expression(e.lwr, ids); 877 are.upr = doInlineAs!Expression(e.upr, ids); 878 result = are; 879 } 880 881 override void visit(TupleExp e) 882 { 883 auto ce = e.copy().isTupleExp(); 884 ce.e0 = doInlineAs!Expression(e.e0, ids); 885 ce.exps = arrayExpressionDoInline(e.exps); 886 result = ce; 887 } 888 889 override void visit(ArrayLiteralExp e) 890 { 891 auto ce = e.copy().isArrayLiteralExp(); 892 ce.basis = doInlineAs!Expression(e.basis, ids); 893 ce.elements = arrayExpressionDoInline(e.elements); 894 result = ce; 895 896 semanticTypeInfo(null, e.type); 897 } 898 899 override void visit(AssocArrayLiteralExp e) 900 { 901 auto ce = e.copy().isAssocArrayLiteralExp(); 902 ce.keys = arrayExpressionDoInline(e.keys); 903 ce.values = arrayExpressionDoInline(e.values); 904 result = ce; 905 906 semanticTypeInfo(null, e.type); 907 } 908 909 override void visit(StructLiteralExp e) 910 { 911 if (e.inlinecopy) 912 { 913 result = e.inlinecopy; 914 return; 915 } 916 auto ce = e.copy().isStructLiteralExp(); 917 e.inlinecopy = ce; 918 ce.elements = arrayExpressionDoInline(e.elements); 919 e.inlinecopy = null; 920 result = ce; 921 } 922 923 override void visit(ArrayExp e) 924 { 925 assert(0); // this should have been lowered to something else 926 } 927 928 override void visit(CondExp e) 929 { 930 auto ce = e.copy().isCondExp(); 931 ce.econd = doInlineAs!Expression(e.econd, ids); 932 ce.e1 = doInlineAs!Expression(e.e1, ids); 933 ce.e2 = doInlineAs!Expression(e.e2, ids); 934 result = ce; 935 } 936 } 937 } 938 939 /// ditto 940 private Result doInlineAs(Result)(Statement s, InlineDoState ids) 941 { 942 if (!s) 943 return null; 944 945 scope DoInlineAs!Result v = new DoInlineAs!Result(ids); 946 s.accept(v); 947 return v.result; 948 } 949 950 /// ditto 951 private Result doInlineAs(Result)(Expression e, InlineDoState ids) 952 { 953 if (!e) 954 return null; 955 956 scope DoInlineAs!Result v = new DoInlineAs!Result(ids); 957 e.accept(v); 958 return v.result; 959 } 960 961 /*********************************************************** 962 * Walk the trees, looking for functions to inline. 963 * Inline any that can be. 964 */ 965 private extern (C++) final class InlineScanVisitor : Visitor 966 { 967 alias visit = Visitor.visit; 968 public: 969 FuncDeclaration parent; // function being scanned 970 // As the visit method cannot return a value, these variables 971 // are used to pass the result from 'visit' back to 'inlineScan' 972 Statement sresult; 973 Expression eresult; 974 bool again; 975 976 extern (D) this() scope @safe 977 { 978 } 979 980 override void visit(Statement s) 981 { 982 } 983 984 override void visit(ExpStatement s) 985 { 986 static if (LOG) 987 { 988 printf("ExpStatement.inlineScan(%s)\n", s.toChars()); 989 } 990 if (!s.exp) 991 return; 992 993 Statement inlineScanExpAsStatement(ref Expression exp) 994 { 995 /* If there's a EXP.call at the top, then it may fail to inline 996 * as an Expression. Try to inline as a Statement instead. 997 */ 998 if (auto ce = exp.isCallExp()) 999 { 1000 visitCallExp(ce, null, true); 1001 if (eresult) 1002 exp = eresult; 1003 auto s = sresult; 1004 sresult = null; 1005 eresult = null; 1006 return s; 1007 } 1008 1009 /* If there's a CondExp or CommaExp at the top, then its 1010 * sub-expressions may be inlined as statements. 1011 */ 1012 if (auto e = exp.isCondExp()) 1013 { 1014 inlineScan(e.econd); 1015 auto s1 = inlineScanExpAsStatement(e.e1); 1016 auto s2 = inlineScanExpAsStatement(e.e2); 1017 if (!s1 && !s2) 1018 return null; 1019 auto ifbody = !s1 ? new ExpStatement(e.e1.loc, e.e1) : s1; 1020 auto elsebody = !s2 ? new ExpStatement(e.e2.loc, e.e2) : s2; 1021 return new IfStatement(exp.loc, null, e.econd, ifbody, elsebody, exp.loc); 1022 } 1023 if (auto e = exp.isCommaExp()) 1024 { 1025 /* If expression declares temporaries which have to be destructed 1026 * at the end of the scope then it is better handled as an expression. 1027 */ 1028 if (expNeedsDtor(e.e1)) 1029 { 1030 inlineScan(exp); 1031 return null; 1032 } 1033 1034 auto s1 = inlineScanExpAsStatement(e.e1); 1035 auto s2 = inlineScanExpAsStatement(e.e2); 1036 if (!s1 && !s2) 1037 return null; 1038 auto a = new Statements(); 1039 a.push(!s1 ? new ExpStatement(e.e1.loc, e.e1) : s1); 1040 a.push(!s2 ? new ExpStatement(e.e2.loc, e.e2) : s2); 1041 return new CompoundStatement(exp.loc, a); 1042 } 1043 1044 // inline as an expression 1045 inlineScan(exp); 1046 return null; 1047 } 1048 1049 sresult = inlineScanExpAsStatement(s.exp); 1050 } 1051 1052 override void visit(CompoundStatement s) 1053 { 1054 foreach (i; 0 .. s.statements.length) 1055 { 1056 inlineScan((*s.statements)[i]); 1057 } 1058 } 1059 1060 override void visit(UnrolledLoopStatement s) 1061 { 1062 foreach (i; 0 .. s.statements.length) 1063 { 1064 inlineScan((*s.statements)[i]); 1065 } 1066 } 1067 1068 override void visit(ScopeStatement s) 1069 { 1070 inlineScan(s.statement); 1071 } 1072 1073 override void visit(WhileStatement s) 1074 { 1075 inlineScan(s.condition); 1076 inlineScan(s._body); 1077 } 1078 1079 override void visit(DoStatement s) 1080 { 1081 inlineScan(s._body); 1082 inlineScan(s.condition); 1083 } 1084 1085 override void visit(ForStatement s) 1086 { 1087 inlineScan(s._init); 1088 inlineScan(s.condition); 1089 inlineScan(s.increment); 1090 inlineScan(s._body); 1091 } 1092 1093 override void visit(ForeachStatement s) 1094 { 1095 inlineScan(s.aggr); 1096 inlineScan(s._body); 1097 } 1098 1099 override void visit(ForeachRangeStatement s) 1100 { 1101 inlineScan(s.lwr); 1102 inlineScan(s.upr); 1103 inlineScan(s._body); 1104 } 1105 1106 override void visit(IfStatement s) 1107 { 1108 inlineScan(s.condition); 1109 inlineScan(s.ifbody); 1110 inlineScan(s.elsebody); 1111 } 1112 1113 override void visit(SwitchStatement s) 1114 { 1115 //printf("SwitchStatement.inlineScan()\n"); 1116 inlineScan(s.condition); 1117 inlineScan(s._body); 1118 Statement sdefault = s.sdefault; 1119 inlineScan(sdefault); 1120 s.sdefault = cast(DefaultStatement)sdefault; 1121 if (s.cases) 1122 { 1123 foreach (i; 0 .. s.cases.length) 1124 { 1125 Statement scase = (*s.cases)[i]; 1126 inlineScan(scase); 1127 (*s.cases)[i] = cast(CaseStatement)scase; 1128 } 1129 } 1130 } 1131 1132 override void visit(CaseStatement s) 1133 { 1134 //printf("CaseStatement.inlineScan()\n"); 1135 inlineScan(s.exp); 1136 inlineScan(s.statement); 1137 } 1138 1139 override void visit(DefaultStatement s) 1140 { 1141 inlineScan(s.statement); 1142 } 1143 1144 override void visit(ReturnStatement s) 1145 { 1146 //printf("ReturnStatement.inlineScan()\n"); 1147 inlineScan(s.exp); 1148 } 1149 1150 override void visit(SynchronizedStatement s) 1151 { 1152 inlineScan(s.exp); 1153 inlineScan(s._body); 1154 } 1155 1156 override void visit(WithStatement s) 1157 { 1158 inlineScan(s.exp); 1159 inlineScan(s._body); 1160 } 1161 1162 override void visit(TryCatchStatement s) 1163 { 1164 inlineScan(s._body); 1165 if (s.catches) 1166 { 1167 foreach (c; *s.catches) 1168 { 1169 inlineScan(c.handler); 1170 } 1171 } 1172 } 1173 1174 override void visit(TryFinallyStatement s) 1175 { 1176 inlineScan(s._body); 1177 inlineScan(s.finalbody); 1178 } 1179 1180 override void visit(ThrowStatement s) 1181 { 1182 inlineScan(s.exp); 1183 } 1184 1185 override void visit(LabelStatement s) 1186 { 1187 inlineScan(s.statement); 1188 } 1189 1190 /******************************** 1191 * Scan Statement s for inlining opportunities, 1192 * and if found replace s with an inlined one. 1193 * Params: 1194 * s = Statement to be scanned and updated 1195 */ 1196 void inlineScan(ref Statement s) 1197 { 1198 if (!s) 1199 return; 1200 assert(sresult is null); 1201 s.accept(this); 1202 if (sresult) 1203 { 1204 s = sresult; 1205 sresult = null; 1206 } 1207 } 1208 1209 /* -------------------------- */ 1210 void arrayInlineScan(Expressions* arguments) 1211 { 1212 if (arguments) 1213 { 1214 foreach (i; 0 .. arguments.length) 1215 { 1216 inlineScan((*arguments)[i]); 1217 } 1218 } 1219 } 1220 1221 override void visit(Expression e) 1222 { 1223 } 1224 1225 void scanVar(Dsymbol s) 1226 { 1227 //printf("scanVar(%s %s)\n", s.kind(), s.toPrettyChars()); 1228 VarDeclaration vd = s.isVarDeclaration(); 1229 if (vd) 1230 { 1231 TupleDeclaration td = vd.toAlias().isTupleDeclaration(); 1232 if (td) 1233 { 1234 td.foreachVar((s) 1235 { 1236 scanVar(s); // TODO 1237 }); 1238 } 1239 else if (vd._init) 1240 { 1241 if (ExpInitializer ie = vd._init.isExpInitializer()) 1242 { 1243 inlineScan(ie.exp); 1244 } 1245 } 1246 } 1247 else 1248 { 1249 inlineScanDsymbol(s); 1250 } 1251 } 1252 1253 override void visit(DeclarationExp e) 1254 { 1255 //printf("DeclarationExp.inlineScan() %s\n", e.toChars()); 1256 scanVar(e.declaration); 1257 } 1258 1259 override void visit(UnaExp e) 1260 { 1261 inlineScan(e.e1); 1262 } 1263 1264 override void visit(AssertExp e) 1265 { 1266 inlineScan(e.e1); 1267 inlineScan(e.msg); 1268 } 1269 1270 override void visit(CatExp e) 1271 { 1272 if (auto lowering = e.lowering) 1273 { 1274 inlineScan(lowering); 1275 return; 1276 } 1277 1278 inlineScan(e.e1); 1279 inlineScan(e.e2); 1280 } 1281 1282 override void visit(BinExp e) 1283 { 1284 inlineScan(e.e1); 1285 inlineScan(e.e2); 1286 } 1287 1288 override void visit(AssignExp e) 1289 { 1290 // Look for NRVO, as inlining NRVO function returns require special handling 1291 if (e.op == EXP.construct && e.e2.op == EXP.call) 1292 { 1293 auto ce = e.e2.isCallExp(); 1294 if (ce.f && ce.f.isNRVO() && ce.f.nrvo_var) // NRVO 1295 { 1296 if (auto ve = e.e1.isVarExp()) 1297 { 1298 /* Inlining: 1299 * S s = foo(); // initializing by rvalue 1300 * S s = S(1); // constructor call 1301 */ 1302 Declaration d = ve.var; 1303 if (d.storage_class & (STC.out_ | STC.ref_)) // refinit 1304 goto L1; 1305 } 1306 else 1307 { 1308 /* Inlining: 1309 * this.field = foo(); // inside constructor 1310 */ 1311 inlineScan(e.e1); 1312 } 1313 1314 visitCallExp(ce, e.e1, false); 1315 if (eresult) 1316 { 1317 //printf("call with nrvo: %s ==> %s\n", e.toChars(), eresult.toChars()); 1318 return; 1319 } 1320 } 1321 } 1322 L1: 1323 visit(cast(BinExp)e); 1324 } 1325 1326 override void visit(LoweredAssignExp e) 1327 { 1328 inlineScan(e.lowering); 1329 } 1330 1331 override void visit(CallExp e) 1332 { 1333 //printf("CallExp.inlineScan() %s\n", e.toChars()); 1334 visitCallExp(e, null, false); 1335 } 1336 1337 /************************************** 1338 * Check function call to see if can be inlined, 1339 * and then inline it if it can. 1340 * Params: 1341 * e = the function call 1342 * eret = if !null, then this is the lvalue of the nrvo function result 1343 * asStatements = if inline as statements rather than as an Expression 1344 * Returns: 1345 * this.eresult if asStatements == false 1346 * this.sresult if asStatements == true 1347 */ 1348 void visitCallExp(CallExp e, Expression eret, bool asStatements) 1349 { 1350 inlineScan(e.e1); 1351 arrayInlineScan(e.arguments); 1352 1353 //printf("visitCallExp() %s\n", e.toChars()); 1354 FuncDeclaration fd; 1355 1356 void inlineFd() 1357 { 1358 if (!fd || fd == parent) 1359 return; 1360 1361 /* If the arguments generate temporaries that need destruction, the destruction 1362 * must be done after the function body is executed. 1363 * The easiest way to accomplish that is to do the inlining as an Expression. 1364 * https://issues.dlang.org/show_bug.cgi?id=16652 1365 */ 1366 bool asStates = asStatements; 1367 if (asStates) 1368 { 1369 if (fd.inlineStatusExp == ILS.yes) 1370 asStates = false; // inline as expressions 1371 // so no need to recompute argumentsNeedDtors() 1372 else if (argumentsNeedDtors(e.arguments)) 1373 asStates = false; 1374 } 1375 1376 if (canInline(fd, false, false, asStates)) 1377 { 1378 expandInline(e.loc, fd, parent, eret, null, e.arguments, asStates, e.vthis2, eresult, sresult, again); 1379 if (asStatements && eresult) 1380 { 1381 sresult = new ExpStatement(eresult.loc, eresult); 1382 eresult = null; 1383 } 1384 } 1385 } 1386 1387 /* Pattern match various ASTs looking for indirect function calls, delegate calls, 1388 * function literal calls, delegate literal calls, and dot member calls. 1389 * If so, and that is only assigned its _init. 1390 * If so, do 'copy propagation' of the _init value and try to inline it. 1391 */ 1392 if (auto ve = e.e1.isVarExp()) 1393 { 1394 fd = ve.var.isFuncDeclaration(); 1395 if (fd) 1396 // delegate call 1397 inlineFd(); 1398 else 1399 { 1400 // delegate literal call 1401 auto v = ve.var.isVarDeclaration(); 1402 if (v && v._init && v.type.ty == Tdelegate && onlyOneAssign(v, parent)) 1403 { 1404 //printf("init: %s\n", v._init.toChars()); 1405 auto ei = v._init.isExpInitializer(); 1406 if (ei && ei.exp.op == EXP.blit) 1407 { 1408 Expression e2 = (cast(AssignExp)ei.exp).e2; 1409 if (auto fe = e2.isFuncExp()) 1410 { 1411 auto fld = fe.fd; 1412 assert(fld.tok == TOK.delegate_); 1413 fd = fld; 1414 inlineFd(); 1415 } 1416 else if (auto de = e2.isDelegateExp()) 1417 { 1418 if (auto ve2 = de.e1.isVarExp()) 1419 { 1420 fd = ve2.var.isFuncDeclaration(); 1421 inlineFd(); 1422 } 1423 } 1424 } 1425 } 1426 } 1427 } 1428 else if (auto dve = e.e1.isDotVarExp()) 1429 { 1430 fd = dve.var.isFuncDeclaration(); 1431 if (fd && fd != parent && canInline(fd, true, false, asStatements)) 1432 { 1433 if (dve.e1.op == EXP.call && dve.e1.type.toBasetype().ty == Tstruct) 1434 { 1435 /* To create ethis, we'll need to take the address 1436 * of dve.e1, but this won't work if dve.e1 is 1437 * a function call. 1438 */ 1439 } 1440 else 1441 { 1442 expandInline(e.loc, fd, parent, eret, dve.e1, e.arguments, asStatements, e.vthis2, eresult, sresult, again); 1443 } 1444 } 1445 } 1446 else if (e.e1.op == EXP.star && 1447 (cast(PtrExp)e.e1).e1.op == EXP.variable) 1448 { 1449 auto ve = e.e1.isPtrExp().e1.isVarExp(); 1450 VarDeclaration v = ve.var.isVarDeclaration(); 1451 if (v && v._init && onlyOneAssign(v, parent)) 1452 { 1453 //printf("init: %s\n", v._init.toChars()); 1454 auto ei = v._init.isExpInitializer(); 1455 if (ei && ei.exp.op == EXP.blit) 1456 { 1457 Expression e2 = (cast(AssignExp)ei.exp).e2; 1458 // function pointer call 1459 if (auto se = e2.isSymOffExp()) 1460 { 1461 fd = se.var.isFuncDeclaration(); 1462 inlineFd(); 1463 } 1464 // function literal call 1465 else if (auto fe = e2.isFuncExp()) 1466 { 1467 auto fld = fe.fd; 1468 assert(fld.tok == TOK.function_); 1469 fd = fld; 1470 inlineFd(); 1471 } 1472 } 1473 } 1474 } 1475 else if (auto fe = e.e1.isFuncExp()) 1476 { 1477 if (fe.fd) 1478 { 1479 fd = fe.fd; 1480 inlineFd(); 1481 } 1482 else 1483 return; 1484 } 1485 else 1486 { 1487 return; 1488 } 1489 1490 if (global.params.v.verbose && (eresult || sresult)) 1491 message("inlined %s =>\n %s", fd.toPrettyChars(), parent.toPrettyChars()); 1492 1493 if (eresult && e.type.ty != Tvoid) 1494 { 1495 Expression ex = eresult; 1496 while (ex.op == EXP.comma) 1497 { 1498 ex.type = e.type; 1499 ex = ex.isCommaExp().e2; 1500 } 1501 ex.type = e.type; 1502 } 1503 } 1504 1505 override void visit(SliceExp e) 1506 { 1507 inlineScan(e.e1); 1508 inlineScan(e.lwr); 1509 inlineScan(e.upr); 1510 } 1511 1512 override void visit(TupleExp e) 1513 { 1514 //printf("TupleExp.inlineScan()\n"); 1515 inlineScan(e.e0); 1516 arrayInlineScan(e.exps); 1517 } 1518 1519 override void visit(ArrayLiteralExp e) 1520 { 1521 //printf("ArrayLiteralExp.inlineScan()\n"); 1522 inlineScan(e.basis); 1523 arrayInlineScan(e.elements); 1524 } 1525 1526 override void visit(AssocArrayLiteralExp e) 1527 { 1528 //printf("AssocArrayLiteralExp.inlineScan()\n"); 1529 arrayInlineScan(e.keys); 1530 arrayInlineScan(e.values); 1531 } 1532 1533 override void visit(StructLiteralExp e) 1534 { 1535 //printf("StructLiteralExp.inlineScan()\n"); 1536 if (e.stageflags & stageInlineScan) 1537 return; 1538 const old = e.stageflags; 1539 e.stageflags |= stageInlineScan; 1540 arrayInlineScan(e.elements); 1541 e.stageflags = old; 1542 } 1543 1544 override void visit(ArrayExp e) 1545 { 1546 //printf("ArrayExp.inlineScan()\n"); 1547 inlineScan(e.e1); 1548 arrayInlineScan(e.arguments); 1549 } 1550 1551 override void visit(CondExp e) 1552 { 1553 inlineScan(e.econd); 1554 inlineScan(e.e1); 1555 inlineScan(e.e2); 1556 } 1557 1558 /******************************** 1559 * Scan Expression e for inlining opportunities, 1560 * and if found replace e with an inlined one. 1561 * Params: 1562 * e = Expression to be scanned and updated 1563 */ 1564 void inlineScan(ref Expression e) 1565 { 1566 if (!e) 1567 return; 1568 assert(eresult is null); 1569 e.accept(this); 1570 if (eresult) 1571 { 1572 e = eresult; 1573 eresult = null; 1574 } 1575 } 1576 } 1577 1578 /*********************************************************** 1579 * Walk the trees, looking for functions to inline. 1580 * Inline any that can be. 1581 */ 1582 private extern (C++) final class InlineScanVisitorDsymbol : Visitor 1583 { 1584 alias visit = Visitor.visit; 1585 public: 1586 1587 extern (D) this() scope @safe 1588 { 1589 } 1590 1591 /************************************* 1592 * Look for function inlining possibilities. 1593 */ 1594 override void visit(Dsymbol d) 1595 { 1596 // Most Dsymbols aren't functions 1597 } 1598 1599 override void visit(FuncDeclaration fd) 1600 { 1601 static if (LOG) 1602 { 1603 printf("FuncDeclaration.inlineScan('%s')\n", fd.toPrettyChars()); 1604 } 1605 if (!(global.params.useInline || fd.hasAlwaysInlines)) 1606 return; 1607 if (fd.isUnitTestDeclaration() && !global.params.useUnitTests || fd.inlineScanned) 1608 return; 1609 if (fd.fbody && !fd.isNaked()) 1610 { 1611 while (1) 1612 { 1613 fd.inlineNest++; 1614 fd.inlineScanned = true; 1615 1616 scope InlineScanVisitor v = new InlineScanVisitor(); 1617 v.parent = fd; 1618 v.inlineScan(fd.fbody); 1619 bool again = v.again; 1620 1621 fd.inlineNest--; 1622 if (!again) 1623 break; 1624 } 1625 } 1626 } 1627 1628 override void visit(AttribDeclaration d) 1629 { 1630 Dsymbols* decls = d.include(null); 1631 if (decls) 1632 { 1633 foreach (i; 0 .. decls.length) 1634 { 1635 Dsymbol s = (*decls)[i]; 1636 //printf("AttribDeclaration.inlineScan %s\n", s.toChars()); 1637 s.accept(this); 1638 } 1639 } 1640 } 1641 1642 override void visit(AggregateDeclaration ad) 1643 { 1644 //printf("AggregateDeclaration.inlineScan(%s)\n", toChars()); 1645 if (ad.members) 1646 { 1647 foreach (i; 0 .. ad.members.length) 1648 { 1649 Dsymbol s = (*ad.members)[i]; 1650 //printf("inline scan aggregate symbol '%s'\n", s.toChars()); 1651 s.accept(this); 1652 } 1653 } 1654 } 1655 1656 override void visit(TemplateInstance ti) 1657 { 1658 static if (LOG) 1659 { 1660 printf("TemplateInstance.inlineScan('%s')\n", ti.toChars()); 1661 } 1662 if (!ti.errors && ti.members) 1663 { 1664 foreach (i; 0 .. ti.members.length) 1665 { 1666 Dsymbol s = (*ti.members)[i]; 1667 s.accept(this); 1668 } 1669 } 1670 } 1671 } 1672 1673 /*********************************************************** 1674 * Test that `fd` can be inlined. 1675 * 1676 * Params: 1677 * hasthis = `true` if the function call has explicit 'this' expression. 1678 * hdrscan = `true` if the inline scan is for 'D header' content. 1679 * statementsToo = `true` if the function call is placed on ExpStatement. 1680 * It means more code-block dependent statements in fd body - ForStatement, 1681 * ThrowStatement, etc. can be inlined. 1682 * 1683 * Returns: 1684 * true if the function body can be expanded. 1685 * 1686 * Todo: 1687 * - Would be able to eliminate `hasthis` parameter, because semantic analysis 1688 * no longer accepts calls of contextful function without valid 'this'. 1689 * - Would be able to eliminate `hdrscan` parameter, because it's always false. 1690 */ 1691 private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo) 1692 { 1693 int cost; 1694 1695 static if (CANINLINE_LOG) 1696 { 1697 printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n", 1698 hasthis, statementsToo, fd.toPrettyChars()); 1699 } 1700 1701 if (fd.needThis() && !hasthis) 1702 return false; 1703 1704 if (fd.inlineNest) 1705 { 1706 static if (CANINLINE_LOG) 1707 { 1708 printf("\t1: no, inlineNest = %d, semanticRun = %d\n", fd.inlineNest, fd.semanticRun); 1709 } 1710 return false; 1711 } 1712 1713 if (fd.semanticRun < PASS.semantic3 && !hdrscan) 1714 { 1715 if (!fd.fbody) 1716 return false; 1717 if (!fd.functionSemantic3()) 1718 return false; 1719 Module.runDeferredSemantic3(); 1720 if (global.errors) 1721 return false; 1722 assert(fd.semanticRun >= PASS.semantic3done); 1723 } 1724 1725 if (fd.skipCodegen) 1726 return false; 1727 1728 final switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp) 1729 { 1730 case ILS.yes: 1731 static if (CANINLINE_LOG) 1732 { 1733 printf("\t1: yes %s\n", fd.toChars()); 1734 } 1735 return true; 1736 case ILS.no: 1737 static if (CANINLINE_LOG) 1738 { 1739 printf("\t1: no %s\n", fd.toChars()); 1740 } 1741 return false; 1742 case ILS.uninitialized: 1743 break; 1744 } 1745 1746 final switch (fd.inlining) 1747 { 1748 case PINLINE.default_: 1749 if (!global.params.useInline) 1750 return false; 1751 break; 1752 case PINLINE.always: 1753 break; 1754 case PINLINE.never: 1755 return false; 1756 } 1757 1758 if (fd.type) 1759 { 1760 TypeFunction tf = fd.type.isTypeFunction(); 1761 1762 // no variadic parameter lists 1763 if (tf.parameterList.varargs == VarArg.variadic || 1764 tf.parameterList.varargs == VarArg.KRvariadic) 1765 goto Lno; 1766 1767 /* No lazy parameters when inlining by statement, as the inliner tries to 1768 * operate on the created delegate itself rather than the return value. 1769 * Discussion: https://github.com/dlang/dmd/pull/6815 1770 */ 1771 if (statementsToo && fd.parameters) 1772 { 1773 foreach (param; *fd.parameters) 1774 { 1775 if (param.storage_class & STC.lazy_) 1776 goto Lno; 1777 } 1778 } 1779 1780 static bool hasDtor(Type t) 1781 { 1782 auto tv = t.baseElemOf(); 1783 return tv.ty == Tstruct || tv.ty == Tclass; // for now assume these might have a destructor 1784 } 1785 1786 /* Don't inline a function that returns non-void, but has 1787 * no or multiple return expression. 1788 * When inlining as a statement: 1789 * 1. don't inline array operations, because the order the arguments 1790 * get evaluated gets reversed. This is the same issue that e2ir.callfunc() 1791 * has with them 1792 * 2. don't inline when the return value has a destructor, as it doesn't 1793 * get handled properly 1794 */ 1795 if (tf.next && tf.next.ty != Tvoid && 1796 (!(fd.hasReturnExp & 1) || 1797 statementsToo && hasDtor(tf.next)) && 1798 !hdrscan) 1799 { 1800 static if (CANINLINE_LOG) 1801 { 1802 printf("\t3: no %s\n", fd.toChars()); 1803 } 1804 goto Lno; 1805 } 1806 1807 /* https://issues.dlang.org/show_bug.cgi?id=14560 1808 * If fd returns void, all explicit `return;`s 1809 * must not appear in the expanded result. 1810 * See also ReturnStatement.doInlineAs!Statement(). 1811 */ 1812 } 1813 1814 // cannot inline constructor calls because we need to convert: 1815 // return; 1816 // to: 1817 // return this; 1818 // ensure() has magic properties the inliner loses 1819 // require() has magic properties too 1820 // see bug 7699 1821 // no nested references to this frame 1822 if (!fd.fbody || 1823 fd.ident == Id.ensure || 1824 (fd.ident == Id.require && 1825 fd.toParent().isFuncDeclaration() && 1826 fd.toParent().isFuncDeclaration().needThis()) || 1827 !hdrscan && (fd.isSynchronized() || 1828 fd.isImportedSymbol() || 1829 fd.hasNestedFrameRefs() || 1830 (fd.isVirtual() && !fd.isFinalFunc()))) 1831 { 1832 static if (CANINLINE_LOG) 1833 { 1834 printf("\t4: no %s\n", fd.toChars()); 1835 } 1836 goto Lno; 1837 } 1838 1839 // cannot inline functions as statement if they have multiple 1840 // return statements 1841 if ((fd.hasReturnExp & 16) && statementsToo) 1842 { 1843 static if (CANINLINE_LOG) 1844 { 1845 printf("\t5: no %s\n", fd.toChars()); 1846 } 1847 goto Lno; 1848 } 1849 1850 { 1851 cost = inlineCostFunction(fd, hasthis, hdrscan); 1852 } 1853 static if (CANINLINE_LOG) 1854 { 1855 printf("\tcost = %d for %s\n", cost, fd.toChars()); 1856 } 1857 1858 if (tooCostly(cost)) 1859 goto Lno; 1860 if (!statementsToo && cost > COST_MAX) 1861 goto Lno; 1862 1863 if (!hdrscan) 1864 { 1865 // Don't modify inlineStatus for header content scan 1866 if (statementsToo) 1867 fd.inlineStatusStmt = ILS.yes; 1868 else 1869 fd.inlineStatusExp = ILS.yes; 1870 1871 inlineScanDsymbol(fd); // Don't scan recursively for header content scan 1872 1873 if (fd.inlineStatusExp == ILS.uninitialized) 1874 { 1875 // Need to redo cost computation, as some statements or expressions have been inlined 1876 cost = inlineCostFunction(fd, hasthis, hdrscan); 1877 static if (CANINLINE_LOG) 1878 { 1879 printf("recomputed cost = %d for %s\n", cost, fd.toChars()); 1880 } 1881 1882 if (tooCostly(cost)) 1883 goto Lno; 1884 if (!statementsToo && cost > COST_MAX) 1885 goto Lno; 1886 1887 if (statementsToo) 1888 fd.inlineStatusStmt = ILS.yes; 1889 else 1890 fd.inlineStatusExp = ILS.yes; 1891 } 1892 } 1893 static if (CANINLINE_LOG) 1894 { 1895 printf("\t2: yes %s\n", fd.toChars()); 1896 } 1897 return true; 1898 1899 Lno: 1900 if (fd.inlining == PINLINE.always && global.params.warnings == DiagnosticReporting.inform) 1901 warning(fd.loc, "cannot inline function `%s`", fd.toPrettyChars()); 1902 1903 if (!hdrscan) // Don't modify inlineStatus for header content scan 1904 { 1905 if (statementsToo) 1906 fd.inlineStatusStmt = ILS.no; 1907 else 1908 fd.inlineStatusExp = ILS.no; 1909 } 1910 static if (CANINLINE_LOG) 1911 { 1912 printf("\t2: no %s\n", fd.toChars()); 1913 } 1914 return false; 1915 } 1916 1917 /*********************************************************** 1918 * Expand a function call inline, 1919 * ethis.fd(arguments) 1920 * 1921 * Params: 1922 * callLoc = location of CallExp 1923 * fd = function to expand 1924 * parent = function that the call to fd is being expanded into 1925 * eret = if !null then the lvalue of where the nrvo return value goes 1926 * ethis = 'this' reference 1927 * arguments = arguments passed to fd 1928 * asStatements = expand to Statements rather than Expressions 1929 * eresult = if expanding to an expression, this is where the expression is written to 1930 * sresult = if expanding to a statement, this is where the statement is written to 1931 * again = if true, then fd can be inline scanned again because there may be 1932 * more opportunities for inlining 1933 */ 1934 private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration parent, Expression eret, 1935 Expression ethis, Expressions* arguments, bool asStatements, VarDeclaration vthis2, 1936 out Expression eresult, out Statement sresult, out bool again) 1937 { 1938 auto tf = fd.type.isTypeFunction(); 1939 static if (LOG || CANINLINE_LOG || EXPANDINLINE_LOG) 1940 printf("FuncDeclaration.expandInline('%s', %d)\n", fd.toChars(), asStatements); 1941 static if (EXPANDINLINE_LOG) 1942 { 1943 if (eret) printf("\teret = %s\n", eret.toChars()); 1944 if (ethis) printf("\tethis = %s\n", ethis.toChars()); 1945 } 1946 scope ids = new InlineDoState(parent, fd); 1947 1948 if (fd.isNested()) 1949 { 1950 if (!parent.inlinedNestedCallees) 1951 parent.inlinedNestedCallees = new FuncDeclarations(); 1952 parent.inlinedNestedCallees.push(fd); 1953 } 1954 1955 VarDeclaration vret; // will be set the function call result 1956 if (eret) 1957 { 1958 if (auto ve = eret.isVarExp()) 1959 { 1960 vret = ve.var.isVarDeclaration(); 1961 assert(!(vret.storage_class & (STC.out_ | STC.ref_))); 1962 eret = null; 1963 } 1964 else 1965 { 1966 /* Inlining: 1967 * this.field = foo(); // inside constructor 1968 */ 1969 auto ei = new ExpInitializer(callLoc, null); 1970 auto tmp = Identifier.generateId("__retvar"); 1971 vret = new VarDeclaration(fd.loc, eret.type, tmp, ei); 1972 vret.storage_class |= STC.temp | STC.ref_; 1973 vret._linkage = LINK.d; 1974 vret.parent = parent; 1975 1976 ei.exp = new ConstructExp(fd.loc, vret, eret); 1977 ei.exp.type = vret.type; 1978 1979 auto de = new DeclarationExp(fd.loc, vret); 1980 de.type = Type.tvoid; 1981 eret = de; 1982 } 1983 1984 if (!asStatements && fd.nrvo_var) 1985 { 1986 ids.from.push(fd.nrvo_var); 1987 ids.to.push(vret); 1988 } 1989 } 1990 else 1991 { 1992 if (!asStatements && fd.nrvo_var) 1993 { 1994 auto tmp = Identifier.generateId("__retvar"); 1995 vret = new VarDeclaration(fd.loc, fd.nrvo_var.type, tmp, new VoidInitializer(fd.loc)); 1996 assert(!tf.isref); 1997 vret.storage_class = STC.temp | STC.rvalue; 1998 vret._linkage = tf.linkage; 1999 vret.parent = parent; 2000 2001 auto de = new DeclarationExp(fd.loc, vret); 2002 de.type = Type.tvoid; 2003 eret = de; 2004 2005 ids.from.push(fd.nrvo_var); 2006 ids.to.push(vret); 2007 } 2008 } 2009 2010 // Set up vthis 2011 VarDeclaration vthis; 2012 if (ethis) 2013 { 2014 Expression e0; 2015 ethis = Expression.extractLast(ethis, e0); 2016 assert(vthis2 || !fd.hasDualContext()); 2017 if (vthis2) 2018 { 2019 // void*[2] __this = [ethis, this] 2020 if (ethis.type.ty == Tstruct) 2021 { 2022 // ðis 2023 Type t = ethis.type.pointerTo(); 2024 ethis = new AddrExp(ethis.loc, ethis); 2025 ethis.type = t; 2026 } 2027 auto elements = new Expressions(2); 2028 (*elements)[0] = ethis; 2029 (*elements)[1] = new NullExp(Loc.initial, Type.tvoidptr); 2030 Expression ae = new ArrayLiteralExp(vthis2.loc, vthis2.type, elements); 2031 Expression ce = new ConstructExp(vthis2.loc, vthis2, ae); 2032 ce.type = vthis2.type; 2033 vthis2._init = new ExpInitializer(vthis2.loc, ce); 2034 vthis = vthis2; 2035 } 2036 else if (auto ve = ethis.isVarExp()) 2037 { 2038 vthis = ve.var.isVarDeclaration(); 2039 } 2040 else 2041 { 2042 //assert(ethis.type.ty != Tpointer); 2043 if (ethis.type.ty == Tpointer) 2044 { 2045 Type t = ethis.type.nextOf(); 2046 ethis = new PtrExp(ethis.loc, ethis); 2047 ethis.type = t; 2048 } 2049 2050 auto ei = new ExpInitializer(fd.loc, ethis); 2051 vthis = new VarDeclaration(fd.loc, ethis.type, Id.This, ei); 2052 if (ethis.type.ty != Tclass) 2053 vthis.storage_class = STC.ref_; 2054 else 2055 vthis.storage_class = STC.in_; 2056 vthis._linkage = LINK.d; 2057 vthis.parent = parent; 2058 2059 ei.exp = new ConstructExp(fd.loc, vthis, ethis); 2060 ei.exp.type = vthis.type; 2061 2062 auto de = new DeclarationExp(fd.loc, vthis); 2063 de.type = Type.tvoid; 2064 e0 = Expression.combine(e0, de); 2065 } 2066 ethis = e0; 2067 2068 ids.vthis = vthis; 2069 } 2070 2071 // Set up parameters 2072 Expression eparams; 2073 if (arguments && arguments.length) 2074 { 2075 assert(fd.parameters.length == arguments.length); 2076 foreach (i; 0 .. arguments.length) 2077 { 2078 auto vfrom = (*fd.parameters)[i]; 2079 auto arg = (*arguments)[i]; 2080 2081 auto ei = new ExpInitializer(vfrom.loc, arg); 2082 auto vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei); 2083 vto.storage_class |= vfrom.storage_class & (STC.temp | STC.IOR | STC.lazy_ | STC.nodtor); 2084 vto._linkage = vfrom._linkage; 2085 vto.parent = parent; 2086 //printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class); 2087 //printf("vto.parent = '%s'\n", parent.toChars()); 2088 2089 if (VarExp ve = arg.isVarExp()) 2090 { 2091 VarDeclaration va = ve.var.isVarDeclaration(); 2092 if (va && va.isArgDtorVar) 2093 { 2094 assert(vto.storage_class & STC.nodtor); 2095 // The destructor is called on va so take it by ref 2096 vto.storage_class |= STC.ref_; 2097 } 2098 } 2099 2100 // Even if vto is STC.lazy_, `vto = arg` is handled correctly in glue layer. 2101 ei.exp = new BlitExp(vto.loc, vto, arg); 2102 ei.exp.type = vto.type; 2103 2104 ids.from.push(vfrom); 2105 ids.to.push(vto); 2106 2107 auto de = new DeclarationExp(vto.loc, vto); 2108 de.type = Type.tvoid; 2109 eparams = Expression.combine(eparams, de); 2110 2111 /* If function pointer or delegate parameters are present, 2112 * inline scan again because if they are initialized to a symbol, 2113 * any calls to the fp or dg can be inlined. 2114 */ 2115 if (vfrom.type.ty == Tdelegate || 2116 vfrom.type.isPtrToFunction()) 2117 { 2118 if (auto ve = arg.isVarExp()) 2119 { 2120 if (ve.var.isFuncDeclaration()) 2121 again = true; 2122 } 2123 else if (auto se = arg.isSymOffExp()) 2124 { 2125 if (se.var.isFuncDeclaration()) 2126 again = true; 2127 } 2128 else if (arg.op == EXP.function_ || arg.op == EXP.delegate_) 2129 again = true; 2130 } 2131 } 2132 } 2133 2134 if (asStatements) 2135 { 2136 /* Construct: 2137 * { eret; ethis; eparams; fd.fbody; } 2138 * or: 2139 * { eret; ethis; try { eparams; fd.fbody; } finally { vthis.edtor; } } 2140 */ 2141 2142 auto as = new Statements(); 2143 if (eret) 2144 as.push(new ExpStatement(callLoc, eret)); 2145 if (ethis) 2146 as.push(new ExpStatement(callLoc, ethis)); 2147 2148 auto as2 = as; 2149 if (vthis && !vthis.isDataseg()) 2150 { 2151 if (vthis.needsScopeDtor()) 2152 { 2153 // same with ExpStatement.scopeCode() 2154 as2 = new Statements(); 2155 vthis.storage_class |= STC.nodtor; 2156 } 2157 } 2158 2159 if (eparams) 2160 as2.push(new ExpStatement(callLoc, eparams)); 2161 2162 fd.inlineNest++; 2163 Statement s = doInlineAs!Statement(fd.fbody, ids); 2164 fd.inlineNest--; 2165 as2.push(s); 2166 2167 if (as2 != as) 2168 { 2169 as.push(new TryFinallyStatement(callLoc, 2170 new CompoundStatement(callLoc, as2), 2171 new DtorExpStatement(callLoc, vthis.edtor, vthis))); 2172 } 2173 2174 sresult = new ScopeStatement(callLoc, new CompoundStatement(callLoc, as), callLoc); 2175 2176 static if (EXPANDINLINE_LOG) 2177 printf("\n[%s] %s expandInline sresult =\n%s\n", 2178 callLoc.toChars(), fd.toPrettyChars(), sresult.toChars()); 2179 } 2180 else 2181 { 2182 /* Construct: 2183 * (eret, ethis, eparams, fd.fbody) 2184 */ 2185 2186 fd.inlineNest++; 2187 auto e = doInlineAs!Expression(fd.fbody, ids); 2188 fd.inlineNest--; 2189 2190 // https://issues.dlang.org/show_bug.cgi?id=11322 2191 if (tf.isref) 2192 e = e.toLvalue(null, null); 2193 2194 /* If the inlined function returns a copy of a struct, 2195 * and then the return value is used subsequently as an 2196 * lvalue, as in a struct return that is then used as a 'this'. 2197 * Taking the address of the return value will be taking the address 2198 * of the original, not the copy. Fix this by assigning the return value to 2199 * a temporary, then returning the temporary. If the temporary is used as an 2200 * lvalue, it will work. 2201 * This only happens with struct returns. 2202 * See https://issues.dlang.org/show_bug.cgi?id=2127 for an example. 2203 * 2204 * On constructor call making __inlineretval is merely redundant, because 2205 * the returned reference is exactly same as vthis, and the 'this' variable 2206 * already exists at the caller side. 2207 */ 2208 if (tf.next.ty == Tstruct && !fd.nrvo_var && !fd.isCtorDeclaration() && 2209 !isConstruction(e)) 2210 { 2211 /* Generate a new variable to hold the result and initialize it with the 2212 * inlined body of the function: 2213 * tret __inlineretval = e; 2214 */ 2215 auto ei = new ExpInitializer(callLoc, e); 2216 auto tmp = Identifier.generateId("__inlineretval"); 2217 auto vd = new VarDeclaration(callLoc, tf.next, tmp, ei); 2218 vd.storage_class = STC.temp | (tf.isref ? STC.ref_ : STC.rvalue); 2219 vd._linkage = tf.linkage; 2220 vd.parent = parent; 2221 2222 ei.exp = new ConstructExp(callLoc, vd, e); 2223 ei.exp.type = vd.type; 2224 2225 auto de = new DeclarationExp(callLoc, vd); 2226 de.type = Type.tvoid; 2227 2228 // Chain the two together: 2229 // ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval 2230 e = Expression.combine(de, new VarExp(callLoc, vd)); 2231 } 2232 2233 // https://issues.dlang.org/show_bug.cgi?id=15210 2234 if (tf.next.ty == Tvoid && e && e.type.ty != Tvoid) 2235 { 2236 e = new CastExp(callLoc, e, Type.tvoid); 2237 e.type = Type.tvoid; 2238 } 2239 2240 eresult = Expression.combine(eresult, eret, ethis, eparams); 2241 eresult = Expression.combine(eresult, e); 2242 2243 static if (EXPANDINLINE_LOG) 2244 printf("\n[%s] %s expandInline eresult = %s\n", 2245 callLoc.toChars(), fd.toPrettyChars(), eresult.toChars()); 2246 } 2247 2248 // Need to reevaluate whether parent can now be inlined 2249 // in expressions, as we might have inlined statements 2250 parent.inlineStatusExp = ILS.uninitialized; 2251 } 2252 2253 /**************************************************** 2254 * Determine if the value of `e` is the result of construction. 2255 * 2256 * Params: 2257 * e = expression to check 2258 * Returns: 2259 * true for value generated by a constructor or struct literal 2260 */ 2261 private bool isConstruction(Expression e) 2262 { 2263 e = lastComma(e); 2264 2265 if (e.op == EXP.structLiteral) 2266 { 2267 return true; 2268 } 2269 /* Detect: 2270 * structliteral.ctor(args) 2271 */ 2272 else if (e.op == EXP.call) 2273 { 2274 auto ce = cast(CallExp)e; 2275 if (ce.e1.op == EXP.dotVariable) 2276 { 2277 auto dve = cast(DotVarExp)ce.e1; 2278 auto fd = dve.var.isFuncDeclaration(); 2279 if (fd && fd.isCtorDeclaration()) 2280 { 2281 if (dve.e1.op == EXP.structLiteral) 2282 { 2283 return true; 2284 } 2285 } 2286 } 2287 } 2288 return false; 2289 } 2290 2291 2292 /*********************************************************** 2293 * Determine if v is 'head const', meaning 2294 * that once it is initialized it is not changed 2295 * again. 2296 * 2297 * This is done using a primitive flow analysis. 2298 * 2299 * v is head const if v is const or immutable. 2300 * Otherwise, v is assumed to be head const unless one of the 2301 * following is true: 2302 * 1. v is a `ref` or `out` variable 2303 * 2. v is a parameter and fd is a variadic function 2304 * 3. v is assigned to again 2305 * 4. the address of v is taken 2306 * 5. v is referred to by a function nested within fd 2307 * 6. v is ever assigned to a `ref` or `out` variable 2308 * 7. v is ever passed to another function as `ref` or `out` 2309 * 2310 * Params: 2311 * v variable to check 2312 * fd function that v is local to 2313 * Returns: 2314 * true if v's initializer is the only value assigned to v 2315 */ 2316 private bool onlyOneAssign(VarDeclaration v, FuncDeclaration fd) @safe 2317 { 2318 if (!v.type.isMutable()) 2319 return true; // currently the only case handled atm 2320 return false; 2321 } 2322 2323 /************************************************************ 2324 * See if arguments to a function are creating temporaries that 2325 * will need destruction after the function is executed. 2326 * Params: 2327 * arguments = arguments to function 2328 * Returns: 2329 * true if temporaries need destruction 2330 */ 2331 2332 private bool argumentsNeedDtors(Expressions* arguments) 2333 { 2334 if (arguments) 2335 { 2336 foreach (arg; *arguments) 2337 { 2338 if (expNeedsDtor(arg)) 2339 return true; 2340 } 2341 } 2342 return false; 2343 } 2344 2345 /************************************************************ 2346 * See if expression is creating temporaries that 2347 * will need destruction at the end of the scope. 2348 * Params: 2349 * exp = expression 2350 * Returns: 2351 * true if temporaries need destruction 2352 */ 2353 2354 private bool expNeedsDtor(Expression exp) 2355 { 2356 extern (C++) final class NeedsDtor : StoppableVisitor 2357 { 2358 alias visit = typeof(super).visit; 2359 Expression exp; 2360 2361 public: 2362 extern (D) this(Expression exp) scope @safe 2363 { 2364 this.exp = exp; 2365 } 2366 2367 override void visit(Expression) 2368 { 2369 } 2370 2371 override void visit(DeclarationExp de) 2372 { 2373 Dsymbol_needsDtor(de.declaration); 2374 } 2375 2376 void Dsymbol_needsDtor(Dsymbol s) 2377 { 2378 /* This mirrors logic of Dsymbol_toElem() in e2ir.d 2379 * perhaps they can be combined. 2380 */ 2381 2382 void symbolDg(Dsymbol s) 2383 { 2384 Dsymbol_needsDtor(s); 2385 } 2386 2387 if (auto vd = s.isVarDeclaration()) 2388 { 2389 s = s.toAlias(); 2390 if (s != vd) 2391 return Dsymbol_needsDtor(s); 2392 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared | STC.manifest)) 2393 return; 2394 if (vd.needsScopeDtor()) 2395 { 2396 stop = true; 2397 } 2398 } 2399 else if (auto tm = s.isTemplateMixin()) 2400 { 2401 tm.members.foreachDsymbol(&symbolDg); 2402 } 2403 else if (auto ad = s.isAttribDeclaration()) 2404 { 2405 ad.include(null).foreachDsymbol(&symbolDg); 2406 } 2407 else if (auto td = s.isTupleDeclaration()) 2408 { 2409 td.foreachVar(&symbolDg); 2410 } 2411 2412 2413 } 2414 } 2415 2416 scope NeedsDtor ct = new NeedsDtor(exp); 2417 return walkPostorder(exp, ct); 2418 }