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.apply; 22 import dmd.arraytypes; 23 import dmd.astenums; 24 import dmd.attrib; 25 import dmd.declaration; 26 import dmd.dmodule; 27 import dmd.dscope; 28 import dmd.dstruct; 29 import dmd.dsymbol; 30 import dmd.dtemplate; 31 import dmd.expression; 32 import dmd.errors; 33 import dmd.func; 34 import dmd.globals; 35 import dmd.id; 36 import dmd.identifier; 37 import dmd.init; 38 import dmd.initsem; 39 import dmd.location; 40 import dmd.mtype; 41 import dmd.opover; 42 import dmd.printast; 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.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 bugzilla 4820 case is still questionable. Perhaps would have to 88 * 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 e.error("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 ne.thisexp = doInlineAs!Expression(e.thisexp, ids); 728 ne.argprefix = doInlineAs!Expression(e.argprefix, ids); 729 ne.arguments = arrayExpressionDoInline(e.arguments); 730 result = ne; 731 732 semanticTypeInfo(null, e.type); 733 } 734 735 override void visit(UnaExp e) 736 { 737 auto ue = cast(UnaExp)e.copy(); 738 ue.e1 = doInlineAs!Expression(e.e1, ids); 739 result = ue; 740 } 741 742 override void visit(AssertExp e) 743 { 744 auto ae = e.copy().isAssertExp(); 745 ae.e1 = doInlineAs!Expression(e.e1, ids); 746 ae.msg = doInlineAs!Expression(e.msg, ids); 747 result = ae; 748 } 749 750 override void visit(CatExp e) 751 { 752 auto ce = e.copy().isCatExp(); 753 754 if (auto lowering = ce.lowering) 755 ce.lowering = doInlineAs!Expression(lowering, ids); 756 else 757 { 758 ce.e1 = doInlineAs!Expression(e.e1, ids); 759 ce.e2 = doInlineAs!Expression(e.e2, ids); 760 } 761 762 result = ce; 763 } 764 765 override void visit(BinExp e) 766 { 767 auto be = cast(BinExp)e.copy(); 768 be.e1 = doInlineAs!Expression(e.e1, ids); 769 be.e2 = doInlineAs!Expression(e.e2, ids); 770 result = be; 771 } 772 773 override void visit(CallExp e) 774 { 775 auto ce = e.copy().isCallExp(); 776 ce.e1 = doInlineAs!Expression(e.e1, ids); 777 ce.arguments = arrayExpressionDoInline(e.arguments); 778 result = ce; 779 } 780 781 override void visit(AssignExp e) 782 { 783 visit(cast(BinExp)e); 784 } 785 786 override void visit(LoweredAssignExp e) 787 { 788 result = doInlineAs!Expression(e.lowering, ids); 789 } 790 791 override void visit(EqualExp e) 792 { 793 visit(cast(BinExp)e); 794 795 Type t1 = e.e1.type.toBasetype(); 796 if (t1.ty == Tarray || t1.ty == Tsarray) 797 { 798 Type t = t1.nextOf().toBasetype(); 799 while (t.toBasetype().nextOf()) 800 t = t.nextOf().toBasetype(); 801 if (t.ty == Tstruct) 802 semanticTypeInfo(null, t); 803 } 804 else if (t1.ty == Taarray) 805 { 806 semanticTypeInfo(null, t1); 807 } 808 } 809 810 override void visit(IndexExp e) 811 { 812 auto are = e.copy().isIndexExp(); 813 are.e1 = doInlineAs!Expression(e.e1, ids); 814 if (e.lengthVar) 815 { 816 //printf("lengthVar\n"); 817 auto vd = e.lengthVar; 818 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init); 819 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration)); 820 vto.parent = ids.parent; 821 vto.csym = null; 822 vto.isym = null; 823 824 ids.from.push(vd); 825 ids.to.push(vto); 826 827 if (vd._init && !vd._init.isVoidInitializer()) 828 { 829 auto ie = vd._init.isExpInitializer(); 830 assert(ie); 831 vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids)); 832 } 833 are.lengthVar = vto; 834 } 835 are.e2 = doInlineAs!Expression(e.e2, ids); 836 result = are; 837 } 838 839 override void visit(SliceExp e) 840 { 841 auto are = e.copy().isSliceExp(); 842 are.e1 = doInlineAs!Expression(e.e1, ids); 843 if (e.lengthVar) 844 { 845 //printf("lengthVar\n"); 846 auto vd = e.lengthVar; 847 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init); 848 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration)); 849 vto.parent = ids.parent; 850 vto.csym = null; 851 vto.isym = null; 852 853 ids.from.push(vd); 854 ids.to.push(vto); 855 856 if (vd._init && !vd._init.isVoidInitializer()) 857 { 858 auto ie = vd._init.isExpInitializer(); 859 assert(ie); 860 vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids)); 861 } 862 863 are.lengthVar = vto; 864 } 865 are.lwr = doInlineAs!Expression(e.lwr, ids); 866 are.upr = doInlineAs!Expression(e.upr, ids); 867 result = are; 868 } 869 870 override void visit(TupleExp e) 871 { 872 auto ce = e.copy().isTupleExp(); 873 ce.e0 = doInlineAs!Expression(e.e0, ids); 874 ce.exps = arrayExpressionDoInline(e.exps); 875 result = ce; 876 } 877 878 override void visit(ArrayLiteralExp e) 879 { 880 auto ce = e.copy().isArrayLiteralExp(); 881 ce.basis = doInlineAs!Expression(e.basis, ids); 882 ce.elements = arrayExpressionDoInline(e.elements); 883 result = ce; 884 885 semanticTypeInfo(null, e.type); 886 } 887 888 override void visit(AssocArrayLiteralExp e) 889 { 890 auto ce = e.copy().isAssocArrayLiteralExp(); 891 ce.keys = arrayExpressionDoInline(e.keys); 892 ce.values = arrayExpressionDoInline(e.values); 893 result = ce; 894 895 semanticTypeInfo(null, e.type); 896 } 897 898 override void visit(StructLiteralExp e) 899 { 900 if (e.inlinecopy) 901 { 902 result = e.inlinecopy; 903 return; 904 } 905 auto ce = e.copy().isStructLiteralExp(); 906 e.inlinecopy = ce; 907 ce.elements = arrayExpressionDoInline(e.elements); 908 e.inlinecopy = null; 909 result = ce; 910 } 911 912 override void visit(ArrayExp e) 913 { 914 assert(0); // this should have been lowered to something else 915 } 916 917 override void visit(CondExp e) 918 { 919 auto ce = e.copy().isCondExp(); 920 ce.econd = doInlineAs!Expression(e.econd, ids); 921 ce.e1 = doInlineAs!Expression(e.e1, ids); 922 ce.e2 = doInlineAs!Expression(e.e2, ids); 923 result = ce; 924 } 925 } 926 } 927 928 /// ditto 929 private Result doInlineAs(Result)(Statement s, InlineDoState ids) 930 { 931 if (!s) 932 return null; 933 934 scope DoInlineAs!Result v = new DoInlineAs!Result(ids); 935 s.accept(v); 936 return v.result; 937 } 938 939 /// ditto 940 private Result doInlineAs(Result)(Expression e, InlineDoState ids) 941 { 942 if (!e) 943 return null; 944 945 scope DoInlineAs!Result v = new DoInlineAs!Result(ids); 946 e.accept(v); 947 return v.result; 948 } 949 950 /*********************************************************** 951 * Walk the trees, looking for functions to inline. 952 * Inline any that can be. 953 */ 954 private extern (C++) final class InlineScanVisitor : Visitor 955 { 956 alias visit = Visitor.visit; 957 public: 958 FuncDeclaration parent; // function being scanned 959 // As the visit method cannot return a value, these variables 960 // are used to pass the result from 'visit' back to 'inlineScan' 961 Statement sresult; 962 Expression eresult; 963 bool again; 964 965 extern (D) this() scope 966 { 967 } 968 969 override void visit(Statement s) 970 { 971 } 972 973 override void visit(ExpStatement s) 974 { 975 static if (LOG) 976 { 977 printf("ExpStatement.inlineScan(%s)\n", s.toChars()); 978 } 979 if (!s.exp) 980 return; 981 982 Statement inlineScanExpAsStatement(ref Expression exp) 983 { 984 /* If there's a EXP.call at the top, then it may fail to inline 985 * as an Expression. Try to inline as a Statement instead. 986 */ 987 if (auto ce = exp.isCallExp()) 988 { 989 visitCallExp(ce, null, true); 990 if (eresult) 991 exp = eresult; 992 auto s = sresult; 993 sresult = null; 994 eresult = null; 995 return s; 996 } 997 998 /* If there's a CondExp or CommaExp at the top, then its 999 * sub-expressions may be inlined as statements. 1000 */ 1001 if (auto e = exp.isCondExp()) 1002 { 1003 inlineScan(e.econd); 1004 auto s1 = inlineScanExpAsStatement(e.e1); 1005 auto s2 = inlineScanExpAsStatement(e.e2); 1006 if (!s1 && !s2) 1007 return null; 1008 auto ifbody = !s1 ? new ExpStatement(e.e1.loc, e.e1) : s1; 1009 auto elsebody = !s2 ? new ExpStatement(e.e2.loc, e.e2) : s2; 1010 return new IfStatement(exp.loc, null, e.econd, ifbody, elsebody, exp.loc); 1011 } 1012 if (auto e = exp.isCommaExp()) 1013 { 1014 /* If expression declares temporaries which have to be destructed 1015 * at the end of the scope then it is better handled as an expression. 1016 */ 1017 if (expNeedsDtor(e.e1)) 1018 { 1019 inlineScan(exp); 1020 return null; 1021 } 1022 1023 auto s1 = inlineScanExpAsStatement(e.e1); 1024 auto s2 = inlineScanExpAsStatement(e.e2); 1025 if (!s1 && !s2) 1026 return null; 1027 auto a = new Statements(); 1028 a.push(!s1 ? new ExpStatement(e.e1.loc, e.e1) : s1); 1029 a.push(!s2 ? new ExpStatement(e.e2.loc, e.e2) : s2); 1030 return new CompoundStatement(exp.loc, a); 1031 } 1032 1033 // inline as an expression 1034 inlineScan(exp); 1035 return null; 1036 } 1037 1038 sresult = inlineScanExpAsStatement(s.exp); 1039 } 1040 1041 override void visit(CompoundStatement s) 1042 { 1043 foreach (i; 0 .. s.statements.length) 1044 { 1045 inlineScan((*s.statements)[i]); 1046 } 1047 } 1048 1049 override void visit(UnrolledLoopStatement s) 1050 { 1051 foreach (i; 0 .. s.statements.length) 1052 { 1053 inlineScan((*s.statements)[i]); 1054 } 1055 } 1056 1057 override void visit(ScopeStatement s) 1058 { 1059 inlineScan(s.statement); 1060 } 1061 1062 override void visit(WhileStatement s) 1063 { 1064 inlineScan(s.condition); 1065 inlineScan(s._body); 1066 } 1067 1068 override void visit(DoStatement s) 1069 { 1070 inlineScan(s._body); 1071 inlineScan(s.condition); 1072 } 1073 1074 override void visit(ForStatement s) 1075 { 1076 inlineScan(s._init); 1077 inlineScan(s.condition); 1078 inlineScan(s.increment); 1079 inlineScan(s._body); 1080 } 1081 1082 override void visit(ForeachStatement s) 1083 { 1084 inlineScan(s.aggr); 1085 inlineScan(s._body); 1086 } 1087 1088 override void visit(ForeachRangeStatement s) 1089 { 1090 inlineScan(s.lwr); 1091 inlineScan(s.upr); 1092 inlineScan(s._body); 1093 } 1094 1095 override void visit(IfStatement s) 1096 { 1097 inlineScan(s.condition); 1098 inlineScan(s.ifbody); 1099 inlineScan(s.elsebody); 1100 } 1101 1102 override void visit(SwitchStatement s) 1103 { 1104 //printf("SwitchStatement.inlineScan()\n"); 1105 inlineScan(s.condition); 1106 inlineScan(s._body); 1107 Statement sdefault = s.sdefault; 1108 inlineScan(sdefault); 1109 s.sdefault = cast(DefaultStatement)sdefault; 1110 if (s.cases) 1111 { 1112 foreach (i; 0 .. s.cases.length) 1113 { 1114 Statement scase = (*s.cases)[i]; 1115 inlineScan(scase); 1116 (*s.cases)[i] = cast(CaseStatement)scase; 1117 } 1118 } 1119 } 1120 1121 override void visit(CaseStatement s) 1122 { 1123 //printf("CaseStatement.inlineScan()\n"); 1124 inlineScan(s.exp); 1125 inlineScan(s.statement); 1126 } 1127 1128 override void visit(DefaultStatement s) 1129 { 1130 inlineScan(s.statement); 1131 } 1132 1133 override void visit(ReturnStatement s) 1134 { 1135 //printf("ReturnStatement.inlineScan()\n"); 1136 inlineScan(s.exp); 1137 } 1138 1139 override void visit(SynchronizedStatement s) 1140 { 1141 inlineScan(s.exp); 1142 inlineScan(s._body); 1143 } 1144 1145 override void visit(WithStatement s) 1146 { 1147 inlineScan(s.exp); 1148 inlineScan(s._body); 1149 } 1150 1151 override void visit(TryCatchStatement s) 1152 { 1153 inlineScan(s._body); 1154 if (s.catches) 1155 { 1156 foreach (c; *s.catches) 1157 { 1158 inlineScan(c.handler); 1159 } 1160 } 1161 } 1162 1163 override void visit(TryFinallyStatement s) 1164 { 1165 inlineScan(s._body); 1166 inlineScan(s.finalbody); 1167 } 1168 1169 override void visit(ThrowStatement s) 1170 { 1171 inlineScan(s.exp); 1172 } 1173 1174 override void visit(LabelStatement s) 1175 { 1176 inlineScan(s.statement); 1177 } 1178 1179 /******************************** 1180 * Scan Statement s for inlining opportunities, 1181 * and if found replace s with an inlined one. 1182 * Params: 1183 * s = Statement to be scanned and updated 1184 */ 1185 void inlineScan(ref Statement s) 1186 { 1187 if (!s) 1188 return; 1189 assert(sresult is null); 1190 s.accept(this); 1191 if (sresult) 1192 { 1193 s = sresult; 1194 sresult = null; 1195 } 1196 } 1197 1198 /* -------------------------- */ 1199 void arrayInlineScan(Expressions* arguments) 1200 { 1201 if (arguments) 1202 { 1203 foreach (i; 0 .. arguments.length) 1204 { 1205 inlineScan((*arguments)[i]); 1206 } 1207 } 1208 } 1209 1210 override void visit(Expression e) 1211 { 1212 } 1213 1214 void scanVar(Dsymbol s) 1215 { 1216 //printf("scanVar(%s %s)\n", s.kind(), s.toPrettyChars()); 1217 VarDeclaration vd = s.isVarDeclaration(); 1218 if (vd) 1219 { 1220 TupleDeclaration td = vd.toAlias().isTupleDeclaration(); 1221 if (td) 1222 { 1223 td.foreachVar((s) 1224 { 1225 scanVar(s); // TODO 1226 }); 1227 } 1228 else if (vd._init) 1229 { 1230 if (ExpInitializer ie = vd._init.isExpInitializer()) 1231 { 1232 inlineScan(ie.exp); 1233 } 1234 } 1235 } 1236 else 1237 { 1238 inlineScanDsymbol(s); 1239 } 1240 } 1241 1242 override void visit(DeclarationExp e) 1243 { 1244 //printf("DeclarationExp.inlineScan() %s\n", e.toChars()); 1245 scanVar(e.declaration); 1246 } 1247 1248 override void visit(UnaExp e) 1249 { 1250 inlineScan(e.e1); 1251 } 1252 1253 override void visit(AssertExp e) 1254 { 1255 inlineScan(e.e1); 1256 inlineScan(e.msg); 1257 } 1258 1259 override void visit(CatExp e) 1260 { 1261 if (auto lowering = e.lowering) 1262 { 1263 inlineScan(lowering); 1264 return; 1265 } 1266 1267 inlineScan(e.e1); 1268 inlineScan(e.e2); 1269 } 1270 1271 override void visit(BinExp e) 1272 { 1273 inlineScan(e.e1); 1274 inlineScan(e.e2); 1275 } 1276 1277 override void visit(AssignExp e) 1278 { 1279 // Look for NRVO, as inlining NRVO function returns require special handling 1280 if (e.op == EXP.construct && e.e2.op == EXP.call) 1281 { 1282 auto ce = e.e2.isCallExp(); 1283 if (ce.f && ce.f.isNRVO() && ce.f.nrvo_var) // NRVO 1284 { 1285 if (auto ve = e.e1.isVarExp()) 1286 { 1287 /* Inlining: 1288 * S s = foo(); // initializing by rvalue 1289 * S s = S(1); // constructor call 1290 */ 1291 Declaration d = ve.var; 1292 if (d.storage_class & (STC.out_ | STC.ref_)) // refinit 1293 goto L1; 1294 } 1295 else 1296 { 1297 /* Inlining: 1298 * this.field = foo(); // inside constructor 1299 */ 1300 inlineScan(e.e1); 1301 } 1302 1303 visitCallExp(ce, e.e1, false); 1304 if (eresult) 1305 { 1306 //printf("call with nrvo: %s ==> %s\n", e.toChars(), eresult.toChars()); 1307 return; 1308 } 1309 } 1310 } 1311 L1: 1312 visit(cast(BinExp)e); 1313 } 1314 1315 override void visit(LoweredAssignExp e) 1316 { 1317 inlineScan(e.lowering); 1318 } 1319 1320 override void visit(CallExp e) 1321 { 1322 //printf("CallExp.inlineScan() %s\n", e.toChars()); 1323 visitCallExp(e, null, false); 1324 } 1325 1326 /************************************** 1327 * Check function call to see if can be inlined, 1328 * and then inline it if it can. 1329 * Params: 1330 * e = the function call 1331 * eret = if !null, then this is the lvalue of the nrvo function result 1332 * asStatements = if inline as statements rather than as an Expression 1333 * Returns: 1334 * this.eresult if asStatements == false 1335 * this.sresult if asStatements == true 1336 */ 1337 void visitCallExp(CallExp e, Expression eret, bool asStatements) 1338 { 1339 inlineScan(e.e1); 1340 arrayInlineScan(e.arguments); 1341 1342 //printf("visitCallExp() %s\n", e.toChars()); 1343 FuncDeclaration fd; 1344 1345 void inlineFd() 1346 { 1347 if (!fd || fd == parent) 1348 return; 1349 1350 /* If the arguments generate temporaries that need destruction, the destruction 1351 * must be done after the function body is executed. 1352 * The easiest way to accomplish that is to do the inlining as an Expression. 1353 * https://issues.dlang.org/show_bug.cgi?id=16652 1354 */ 1355 bool asStates = asStatements; 1356 if (asStates) 1357 { 1358 if (fd.inlineStatusExp == ILS.yes) 1359 asStates = false; // inline as expressions 1360 // so no need to recompute argumentsNeedDtors() 1361 else if (argumentsNeedDtors(e.arguments)) 1362 asStates = false; 1363 } 1364 1365 if (canInline(fd, false, false, asStates)) 1366 { 1367 expandInline(e.loc, fd, parent, eret, null, e.arguments, asStates, e.vthis2, eresult, sresult, again); 1368 if (asStatements && eresult) 1369 { 1370 sresult = new ExpStatement(eresult.loc, eresult); 1371 eresult = null; 1372 } 1373 } 1374 } 1375 1376 /* Pattern match various ASTs looking for indirect function calls, delegate calls, 1377 * function literal calls, delegate literal calls, and dot member calls. 1378 * If so, and that is only assigned its _init. 1379 * If so, do 'copy propagation' of the _init value and try to inline it. 1380 */ 1381 if (auto ve = e.e1.isVarExp()) 1382 { 1383 fd = ve.var.isFuncDeclaration(); 1384 if (fd) 1385 // delegate call 1386 inlineFd(); 1387 else 1388 { 1389 // delegate literal call 1390 auto v = ve.var.isVarDeclaration(); 1391 if (v && v._init && v.type.ty == Tdelegate && onlyOneAssign(v, parent)) 1392 { 1393 //printf("init: %s\n", v._init.toChars()); 1394 auto ei = v._init.isExpInitializer(); 1395 if (ei && ei.exp.op == EXP.blit) 1396 { 1397 Expression e2 = (cast(AssignExp)ei.exp).e2; 1398 if (auto fe = e2.isFuncExp()) 1399 { 1400 auto fld = fe.fd; 1401 assert(fld.tok == TOK.delegate_); 1402 fd = fld; 1403 inlineFd(); 1404 } 1405 else if (auto de = e2.isDelegateExp()) 1406 { 1407 if (auto ve2 = de.e1.isVarExp()) 1408 { 1409 fd = ve2.var.isFuncDeclaration(); 1410 inlineFd(); 1411 } 1412 } 1413 } 1414 } 1415 } 1416 } 1417 else if (auto dve = e.e1.isDotVarExp()) 1418 { 1419 fd = dve.var.isFuncDeclaration(); 1420 if (fd && fd != parent && canInline(fd, true, false, asStatements)) 1421 { 1422 if (dve.e1.op == EXP.call && dve.e1.type.toBasetype().ty == Tstruct) 1423 { 1424 /* To create ethis, we'll need to take the address 1425 * of dve.e1, but this won't work if dve.e1 is 1426 * a function call. 1427 */ 1428 } 1429 else 1430 { 1431 expandInline(e.loc, fd, parent, eret, dve.e1, e.arguments, asStatements, e.vthis2, eresult, sresult, again); 1432 } 1433 } 1434 } 1435 else if (e.e1.op == EXP.star && 1436 (cast(PtrExp)e.e1).e1.op == EXP.variable) 1437 { 1438 auto ve = e.e1.isPtrExp().e1.isVarExp(); 1439 VarDeclaration v = ve.var.isVarDeclaration(); 1440 if (v && v._init && onlyOneAssign(v, parent)) 1441 { 1442 //printf("init: %s\n", v._init.toChars()); 1443 auto ei = v._init.isExpInitializer(); 1444 if (ei && ei.exp.op == EXP.blit) 1445 { 1446 Expression e2 = (cast(AssignExp)ei.exp).e2; 1447 // function pointer call 1448 if (auto se = e2.isSymOffExp()) 1449 { 1450 fd = se.var.isFuncDeclaration(); 1451 inlineFd(); 1452 } 1453 // function literal call 1454 else if (auto fe = e2.isFuncExp()) 1455 { 1456 auto fld = fe.fd; 1457 assert(fld.tok == TOK.function_); 1458 fd = fld; 1459 inlineFd(); 1460 } 1461 } 1462 } 1463 } 1464 else if (auto fe = e.e1.isFuncExp()) 1465 { 1466 if (fe.fd) 1467 { 1468 fd = fe.fd; 1469 inlineFd(); 1470 } 1471 else 1472 return; 1473 } 1474 else 1475 { 1476 return; 1477 } 1478 1479 if (global.params.verbose && (eresult || sresult)) 1480 message("inlined %s =>\n %s", fd.toPrettyChars(), parent.toPrettyChars()); 1481 1482 if (eresult && e.type.ty != Tvoid) 1483 { 1484 Expression ex = eresult; 1485 while (ex.op == EXP.comma) 1486 { 1487 ex.type = e.type; 1488 ex = ex.isCommaExp().e2; 1489 } 1490 ex.type = e.type; 1491 } 1492 } 1493 1494 override void visit(SliceExp e) 1495 { 1496 inlineScan(e.e1); 1497 inlineScan(e.lwr); 1498 inlineScan(e.upr); 1499 } 1500 1501 override void visit(TupleExp e) 1502 { 1503 //printf("TupleExp.inlineScan()\n"); 1504 inlineScan(e.e0); 1505 arrayInlineScan(e.exps); 1506 } 1507 1508 override void visit(ArrayLiteralExp e) 1509 { 1510 //printf("ArrayLiteralExp.inlineScan()\n"); 1511 inlineScan(e.basis); 1512 arrayInlineScan(e.elements); 1513 } 1514 1515 override void visit(AssocArrayLiteralExp e) 1516 { 1517 //printf("AssocArrayLiteralExp.inlineScan()\n"); 1518 arrayInlineScan(e.keys); 1519 arrayInlineScan(e.values); 1520 } 1521 1522 override void visit(StructLiteralExp e) 1523 { 1524 //printf("StructLiteralExp.inlineScan()\n"); 1525 if (e.stageflags & stageInlineScan) 1526 return; 1527 const old = e.stageflags; 1528 e.stageflags |= stageInlineScan; 1529 arrayInlineScan(e.elements); 1530 e.stageflags = old; 1531 } 1532 1533 override void visit(ArrayExp e) 1534 { 1535 //printf("ArrayExp.inlineScan()\n"); 1536 inlineScan(e.e1); 1537 arrayInlineScan(e.arguments); 1538 } 1539 1540 override void visit(CondExp e) 1541 { 1542 inlineScan(e.econd); 1543 inlineScan(e.e1); 1544 inlineScan(e.e2); 1545 } 1546 1547 /******************************** 1548 * Scan Expression e for inlining opportunities, 1549 * and if found replace e with an inlined one. 1550 * Params: 1551 * e = Expression to be scanned and updated 1552 */ 1553 void inlineScan(ref Expression e) 1554 { 1555 if (!e) 1556 return; 1557 assert(eresult is null); 1558 e.accept(this); 1559 if (eresult) 1560 { 1561 e = eresult; 1562 eresult = null; 1563 } 1564 } 1565 } 1566 1567 /*********************************************************** 1568 * Walk the trees, looking for functions to inline. 1569 * Inline any that can be. 1570 */ 1571 private extern (C++) final class InlineScanVisitorDsymbol : Visitor 1572 { 1573 alias visit = Visitor.visit; 1574 public: 1575 1576 extern (D) this() scope 1577 { 1578 } 1579 1580 /************************************* 1581 * Look for function inlining possibilities. 1582 */ 1583 override void visit(Dsymbol d) 1584 { 1585 // Most Dsymbols aren't functions 1586 } 1587 1588 override void visit(FuncDeclaration fd) 1589 { 1590 static if (LOG) 1591 { 1592 printf("FuncDeclaration.inlineScan('%s')\n", fd.toPrettyChars()); 1593 } 1594 if (!(global.params.useInline || fd.hasAlwaysInlines)) 1595 return; 1596 if (fd.isUnitTestDeclaration() && !global.params.useUnitTests || fd.inlineScanned) 1597 return; 1598 if (fd.fbody && !fd.isNaked()) 1599 { 1600 while (1) 1601 { 1602 fd.inlineNest++; 1603 fd.inlineScanned = true; 1604 1605 scope InlineScanVisitor v = new InlineScanVisitor(); 1606 v.parent = fd; 1607 v.inlineScan(fd.fbody); 1608 bool again = v.again; 1609 1610 fd.inlineNest--; 1611 if (!again) 1612 break; 1613 } 1614 } 1615 } 1616 1617 override void visit(AttribDeclaration d) 1618 { 1619 Dsymbols* decls = d.include(null); 1620 if (decls) 1621 { 1622 foreach (i; 0 .. decls.length) 1623 { 1624 Dsymbol s = (*decls)[i]; 1625 //printf("AttribDeclaration.inlineScan %s\n", s.toChars()); 1626 s.accept(this); 1627 } 1628 } 1629 } 1630 1631 override void visit(AggregateDeclaration ad) 1632 { 1633 //printf("AggregateDeclaration.inlineScan(%s)\n", toChars()); 1634 if (ad.members) 1635 { 1636 foreach (i; 0 .. ad.members.length) 1637 { 1638 Dsymbol s = (*ad.members)[i]; 1639 //printf("inline scan aggregate symbol '%s'\n", s.toChars()); 1640 s.accept(this); 1641 } 1642 } 1643 } 1644 1645 override void visit(TemplateInstance ti) 1646 { 1647 static if (LOG) 1648 { 1649 printf("TemplateInstance.inlineScan('%s')\n", ti.toChars()); 1650 } 1651 if (!ti.errors && ti.members) 1652 { 1653 foreach (i; 0 .. ti.members.length) 1654 { 1655 Dsymbol s = (*ti.members)[i]; 1656 s.accept(this); 1657 } 1658 } 1659 } 1660 } 1661 1662 /*********************************************************** 1663 * Test that `fd` can be inlined. 1664 * 1665 * Params: 1666 * hasthis = `true` if the function call has explicit 'this' expression. 1667 * hdrscan = `true` if the inline scan is for 'D header' content. 1668 * statementsToo = `true` if the function call is placed on ExpStatement. 1669 * It means more code-block dependent statements in fd body - ForStatement, 1670 * ThrowStatement, etc. can be inlined. 1671 * 1672 * Returns: 1673 * true if the function body can be expanded. 1674 * 1675 * Todo: 1676 * - Would be able to eliminate `hasthis` parameter, because semantic analysis 1677 * no longer accepts calls of contextful function without valid 'this'. 1678 * - Would be able to eliminate `hdrscan` parameter, because it's always false. 1679 */ 1680 private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo) 1681 { 1682 int cost; 1683 1684 static if (CANINLINE_LOG) 1685 { 1686 printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n", 1687 hasthis, statementsToo, fd.toPrettyChars()); 1688 } 1689 1690 if (fd.needThis() && !hasthis) 1691 return false; 1692 1693 if (fd.inlineNest) 1694 { 1695 static if (CANINLINE_LOG) 1696 { 1697 printf("\t1: no, inlineNest = %d, semanticRun = %d\n", fd.inlineNest, fd.semanticRun); 1698 } 1699 return false; 1700 } 1701 1702 if (fd.semanticRun < PASS.semantic3 && !hdrscan) 1703 { 1704 if (!fd.fbody) 1705 return false; 1706 if (!fd.functionSemantic3()) 1707 return false; 1708 Module.runDeferredSemantic3(); 1709 if (global.errors) 1710 return false; 1711 assert(fd.semanticRun >= PASS.semantic3done); 1712 } 1713 1714 if (fd.skipCodegen) 1715 return false; 1716 1717 final switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp) 1718 { 1719 case ILS.yes: 1720 static if (CANINLINE_LOG) 1721 { 1722 printf("\t1: yes %s\n", fd.toChars()); 1723 } 1724 return true; 1725 case ILS.no: 1726 static if (CANINLINE_LOG) 1727 { 1728 printf("\t1: no %s\n", fd.toChars()); 1729 } 1730 return false; 1731 case ILS.uninitialized: 1732 break; 1733 } 1734 1735 final switch (fd.inlining) 1736 { 1737 case PINLINE.default_: 1738 if (!global.params.useInline) 1739 return false; 1740 break; 1741 case PINLINE.always: 1742 break; 1743 case PINLINE.never: 1744 return false; 1745 } 1746 1747 if (fd.type) 1748 { 1749 TypeFunction tf = fd.type.isTypeFunction(); 1750 1751 // no variadic parameter lists 1752 if (tf.parameterList.varargs == VarArg.variadic || 1753 tf.parameterList.varargs == VarArg.KRvariadic) 1754 goto Lno; 1755 1756 /* No lazy parameters when inlining by statement, as the inliner tries to 1757 * operate on the created delegate itself rather than the return value. 1758 * Discussion: https://github.com/dlang/dmd/pull/6815 1759 */ 1760 if (statementsToo && fd.parameters) 1761 { 1762 foreach (param; *fd.parameters) 1763 { 1764 if (param.storage_class & STC.lazy_) 1765 goto Lno; 1766 } 1767 } 1768 1769 static bool hasDtor(Type t) 1770 { 1771 auto tv = t.baseElemOf(); 1772 return tv.ty == Tstruct || tv.ty == Tclass; // for now assume these might have a destructor 1773 } 1774 1775 /* Don't inline a function that returns non-void, but has 1776 * no or multiple return expression. 1777 * When inlining as a statement: 1778 * 1. don't inline array operations, because the order the arguments 1779 * get evaluated gets reversed. This is the same issue that e2ir.callfunc() 1780 * has with them 1781 * 2. don't inline when the return value has a destructor, as it doesn't 1782 * get handled properly 1783 */ 1784 if (tf.next && tf.next.ty != Tvoid && 1785 (!(fd.hasReturnExp & 1) || 1786 statementsToo && hasDtor(tf.next)) && 1787 !hdrscan) 1788 { 1789 static if (CANINLINE_LOG) 1790 { 1791 printf("\t3: no %s\n", fd.toChars()); 1792 } 1793 goto Lno; 1794 } 1795 1796 /* https://issues.dlang.org/show_bug.cgi?id=14560 1797 * If fd returns void, all explicit `return;`s 1798 * must not appear in the expanded result. 1799 * See also ReturnStatement.doInlineAs!Statement(). 1800 */ 1801 } 1802 1803 // cannot inline constructor calls because we need to convert: 1804 // return; 1805 // to: 1806 // return this; 1807 // ensure() has magic properties the inliner loses 1808 // require() has magic properties too 1809 // see bug 7699 1810 // no nested references to this frame 1811 if (!fd.fbody || 1812 fd.ident == Id.ensure || 1813 (fd.ident == Id.require && 1814 fd.toParent().isFuncDeclaration() && 1815 fd.toParent().isFuncDeclaration().needThis()) || 1816 !hdrscan && (fd.isSynchronized() || 1817 fd.isImportedSymbol() || 1818 fd.hasNestedFrameRefs() || 1819 (fd.isVirtual() && !fd.isFinalFunc()))) 1820 { 1821 static if (CANINLINE_LOG) 1822 { 1823 printf("\t4: no %s\n", fd.toChars()); 1824 } 1825 goto Lno; 1826 } 1827 1828 // cannot inline functions as statement if they have multiple 1829 // return statements 1830 if ((fd.hasReturnExp & 16) && statementsToo) 1831 { 1832 static if (CANINLINE_LOG) 1833 { 1834 printf("\t5: no %s\n", fd.toChars()); 1835 } 1836 goto Lno; 1837 } 1838 1839 { 1840 cost = inlineCostFunction(fd, hasthis, hdrscan); 1841 } 1842 static if (CANINLINE_LOG) 1843 { 1844 printf("\tcost = %d for %s\n", cost, fd.toChars()); 1845 } 1846 1847 if (tooCostly(cost)) 1848 goto Lno; 1849 if (!statementsToo && cost > COST_MAX) 1850 goto Lno; 1851 1852 if (!hdrscan) 1853 { 1854 // Don't modify inlineStatus for header content scan 1855 if (statementsToo) 1856 fd.inlineStatusStmt = ILS.yes; 1857 else 1858 fd.inlineStatusExp = ILS.yes; 1859 1860 inlineScanDsymbol(fd); // Don't scan recursively for header content scan 1861 1862 if (fd.inlineStatusExp == ILS.uninitialized) 1863 { 1864 // Need to redo cost computation, as some statements or expressions have been inlined 1865 cost = inlineCostFunction(fd, hasthis, hdrscan); 1866 static if (CANINLINE_LOG) 1867 { 1868 printf("recomputed cost = %d for %s\n", cost, fd.toChars()); 1869 } 1870 1871 if (tooCostly(cost)) 1872 goto Lno; 1873 if (!statementsToo && cost > COST_MAX) 1874 goto Lno; 1875 1876 if (statementsToo) 1877 fd.inlineStatusStmt = ILS.yes; 1878 else 1879 fd.inlineStatusExp = ILS.yes; 1880 } 1881 } 1882 static if (CANINLINE_LOG) 1883 { 1884 printf("\t2: yes %s\n", fd.toChars()); 1885 } 1886 return true; 1887 1888 Lno: 1889 if (fd.inlining == PINLINE.always && global.params.warnings == DiagnosticReporting.inform) 1890 warning(fd.loc, "cannot inline function `%s`", fd.toPrettyChars()); 1891 1892 if (!hdrscan) // Don't modify inlineStatus for header content scan 1893 { 1894 if (statementsToo) 1895 fd.inlineStatusStmt = ILS.no; 1896 else 1897 fd.inlineStatusExp = ILS.no; 1898 } 1899 static if (CANINLINE_LOG) 1900 { 1901 printf("\t2: no %s\n", fd.toChars()); 1902 } 1903 return false; 1904 } 1905 1906 /*********************************************************** 1907 * Expand a function call inline, 1908 * ethis.fd(arguments) 1909 * 1910 * Params: 1911 * callLoc = location of CallExp 1912 * fd = function to expand 1913 * parent = function that the call to fd is being expanded into 1914 * eret = if !null then the lvalue of where the nrvo return value goes 1915 * ethis = 'this' reference 1916 * arguments = arguments passed to fd 1917 * asStatements = expand to Statements rather than Expressions 1918 * eresult = if expanding to an expression, this is where the expression is written to 1919 * sresult = if expanding to a statement, this is where the statement is written to 1920 * again = if true, then fd can be inline scanned again because there may be 1921 * more opportunities for inlining 1922 */ 1923 private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration parent, Expression eret, 1924 Expression ethis, Expressions* arguments, bool asStatements, VarDeclaration vthis2, 1925 out Expression eresult, out Statement sresult, out bool again) 1926 { 1927 auto tf = fd.type.isTypeFunction(); 1928 static if (LOG || CANINLINE_LOG || EXPANDINLINE_LOG) 1929 printf("FuncDeclaration.expandInline('%s', %d)\n", fd.toChars(), asStatements); 1930 static if (EXPANDINLINE_LOG) 1931 { 1932 if (eret) printf("\teret = %s\n", eret.toChars()); 1933 if (ethis) printf("\tethis = %s\n", ethis.toChars()); 1934 } 1935 scope ids = new InlineDoState(parent, fd); 1936 1937 if (fd.isNested()) 1938 { 1939 if (!parent.inlinedNestedCallees) 1940 parent.inlinedNestedCallees = new FuncDeclarations(); 1941 parent.inlinedNestedCallees.push(fd); 1942 } 1943 1944 VarDeclaration vret; // will be set the function call result 1945 if (eret) 1946 { 1947 if (auto ve = eret.isVarExp()) 1948 { 1949 vret = ve.var.isVarDeclaration(); 1950 assert(!(vret.storage_class & (STC.out_ | STC.ref_))); 1951 eret = null; 1952 } 1953 else 1954 { 1955 /* Inlining: 1956 * this.field = foo(); // inside constructor 1957 */ 1958 auto ei = new ExpInitializer(callLoc, null); 1959 auto tmp = Identifier.generateId("__retvar"); 1960 vret = new VarDeclaration(fd.loc, eret.type, tmp, ei); 1961 vret.storage_class |= STC.temp | STC.ref_; 1962 vret._linkage = LINK.d; 1963 vret.parent = parent; 1964 1965 ei.exp = new ConstructExp(fd.loc, vret, eret); 1966 ei.exp.type = vret.type; 1967 1968 auto de = new DeclarationExp(fd.loc, vret); 1969 de.type = Type.tvoid; 1970 eret = de; 1971 } 1972 1973 if (!asStatements && fd.nrvo_var) 1974 { 1975 ids.from.push(fd.nrvo_var); 1976 ids.to.push(vret); 1977 } 1978 } 1979 else 1980 { 1981 if (!asStatements && fd.nrvo_var) 1982 { 1983 auto tmp = Identifier.generateId("__retvar"); 1984 vret = new VarDeclaration(fd.loc, fd.nrvo_var.type, tmp, new VoidInitializer(fd.loc)); 1985 assert(!tf.isref); 1986 vret.storage_class = STC.temp | STC.rvalue; 1987 vret._linkage = tf.linkage; 1988 vret.parent = parent; 1989 1990 auto de = new DeclarationExp(fd.loc, vret); 1991 de.type = Type.tvoid; 1992 eret = de; 1993 1994 ids.from.push(fd.nrvo_var); 1995 ids.to.push(vret); 1996 } 1997 } 1998 1999 // Set up vthis 2000 VarDeclaration vthis; 2001 if (ethis) 2002 { 2003 Expression e0; 2004 ethis = Expression.extractLast(ethis, e0); 2005 assert(vthis2 || !fd.hasDualContext()); 2006 if (vthis2) 2007 { 2008 // void*[2] __this = [ethis, this] 2009 if (ethis.type.ty == Tstruct) 2010 { 2011 // ðis 2012 Type t = ethis.type.pointerTo(); 2013 ethis = new AddrExp(ethis.loc, ethis); 2014 ethis.type = t; 2015 } 2016 auto elements = new Expressions(2); 2017 (*elements)[0] = ethis; 2018 (*elements)[1] = new NullExp(Loc.initial, Type.tvoidptr); 2019 Expression ae = new ArrayLiteralExp(vthis2.loc, vthis2.type, elements); 2020 Expression ce = new ConstructExp(vthis2.loc, vthis2, ae); 2021 ce.type = vthis2.type; 2022 vthis2._init = new ExpInitializer(vthis2.loc, ce); 2023 vthis = vthis2; 2024 } 2025 else if (auto ve = ethis.isVarExp()) 2026 { 2027 vthis = ve.var.isVarDeclaration(); 2028 } 2029 else 2030 { 2031 //assert(ethis.type.ty != Tpointer); 2032 if (ethis.type.ty == Tpointer) 2033 { 2034 Type t = ethis.type.nextOf(); 2035 ethis = new PtrExp(ethis.loc, ethis); 2036 ethis.type = t; 2037 } 2038 2039 auto ei = new ExpInitializer(fd.loc, ethis); 2040 vthis = new VarDeclaration(fd.loc, ethis.type, Id.This, ei); 2041 if (ethis.type.ty != Tclass) 2042 vthis.storage_class = STC.ref_; 2043 else 2044 vthis.storage_class = STC.in_; 2045 vthis._linkage = LINK.d; 2046 vthis.parent = parent; 2047 2048 ei.exp = new ConstructExp(fd.loc, vthis, ethis); 2049 ei.exp.type = vthis.type; 2050 2051 auto de = new DeclarationExp(fd.loc, vthis); 2052 de.type = Type.tvoid; 2053 e0 = Expression.combine(e0, de); 2054 } 2055 ethis = e0; 2056 2057 ids.vthis = vthis; 2058 } 2059 2060 // Set up parameters 2061 Expression eparams; 2062 if (arguments && arguments.length) 2063 { 2064 assert(fd.parameters.length == arguments.length); 2065 foreach (i; 0 .. arguments.length) 2066 { 2067 auto vfrom = (*fd.parameters)[i]; 2068 auto arg = (*arguments)[i]; 2069 2070 auto ei = new ExpInitializer(vfrom.loc, arg); 2071 auto vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei); 2072 vto.storage_class |= vfrom.storage_class & (STC.temp | STC.IOR | STC.lazy_ | STC.nodtor); 2073 vto._linkage = vfrom._linkage; 2074 vto.parent = parent; 2075 //printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class); 2076 //printf("vto.parent = '%s'\n", parent.toChars()); 2077 2078 if (VarExp ve = arg.isVarExp()) 2079 { 2080 VarDeclaration va = ve.var.isVarDeclaration(); 2081 if (va && va.isArgDtorVar) 2082 { 2083 assert(vto.storage_class & STC.nodtor); 2084 // The destructor is called on va so take it by ref 2085 vto.storage_class |= STC.ref_; 2086 } 2087 } 2088 2089 // Even if vto is STC.lazy_, `vto = arg` is handled correctly in glue layer. 2090 ei.exp = new BlitExp(vto.loc, vto, arg); 2091 ei.exp.type = vto.type; 2092 2093 ids.from.push(vfrom); 2094 ids.to.push(vto); 2095 2096 auto de = new DeclarationExp(vto.loc, vto); 2097 de.type = Type.tvoid; 2098 eparams = Expression.combine(eparams, de); 2099 2100 /* If function pointer or delegate parameters are present, 2101 * inline scan again because if they are initialized to a symbol, 2102 * any calls to the fp or dg can be inlined. 2103 */ 2104 if (vfrom.type.ty == Tdelegate || 2105 vfrom.type.isPtrToFunction()) 2106 { 2107 if (auto ve = arg.isVarExp()) 2108 { 2109 if (ve.var.isFuncDeclaration()) 2110 again = true; 2111 } 2112 else if (auto se = arg.isSymOffExp()) 2113 { 2114 if (se.var.isFuncDeclaration()) 2115 again = true; 2116 } 2117 else if (arg.op == EXP.function_ || arg.op == EXP.delegate_) 2118 again = true; 2119 } 2120 } 2121 } 2122 2123 if (asStatements) 2124 { 2125 /* Construct: 2126 * { eret; ethis; eparams; fd.fbody; } 2127 * or: 2128 * { eret; ethis; try { eparams; fd.fbody; } finally { vthis.edtor; } } 2129 */ 2130 2131 auto as = new Statements(); 2132 if (eret) 2133 as.push(new ExpStatement(callLoc, eret)); 2134 if (ethis) 2135 as.push(new ExpStatement(callLoc, ethis)); 2136 2137 auto as2 = as; 2138 if (vthis && !vthis.isDataseg()) 2139 { 2140 if (vthis.needsScopeDtor()) 2141 { 2142 // same with ExpStatement.scopeCode() 2143 as2 = new Statements(); 2144 vthis.storage_class |= STC.nodtor; 2145 } 2146 } 2147 2148 if (eparams) 2149 as2.push(new ExpStatement(callLoc, eparams)); 2150 2151 fd.inlineNest++; 2152 Statement s = doInlineAs!Statement(fd.fbody, ids); 2153 fd.inlineNest--; 2154 as2.push(s); 2155 2156 if (as2 != as) 2157 { 2158 as.push(new TryFinallyStatement(callLoc, 2159 new CompoundStatement(callLoc, as2), 2160 new DtorExpStatement(callLoc, vthis.edtor, vthis))); 2161 } 2162 2163 sresult = new ScopeStatement(callLoc, new CompoundStatement(callLoc, as), callLoc); 2164 2165 static if (EXPANDINLINE_LOG) 2166 printf("\n[%s] %s expandInline sresult =\n%s\n", 2167 callLoc.toChars(), fd.toPrettyChars(), sresult.toChars()); 2168 } 2169 else 2170 { 2171 /* Construct: 2172 * (eret, ethis, eparams, fd.fbody) 2173 */ 2174 2175 fd.inlineNest++; 2176 auto e = doInlineAs!Expression(fd.fbody, ids); 2177 fd.inlineNest--; 2178 2179 // https://issues.dlang.org/show_bug.cgi?id=11322 2180 if (tf.isref) 2181 e = e.toLvalue(null, null); 2182 2183 /* If the inlined function returns a copy of a struct, 2184 * and then the return value is used subsequently as an 2185 * lvalue, as in a struct return that is then used as a 'this'. 2186 * Taking the address of the return value will be taking the address 2187 * of the original, not the copy. Fix this by assigning the return value to 2188 * a temporary, then returning the temporary. If the temporary is used as an 2189 * lvalue, it will work. 2190 * This only happens with struct returns. 2191 * See https://issues.dlang.org/show_bug.cgi?id=2127 for an example. 2192 * 2193 * On constructor call making __inlineretval is merely redundant, because 2194 * the returned reference is exactly same as vthis, and the 'this' variable 2195 * already exists at the caller side. 2196 */ 2197 if (tf.next.ty == Tstruct && !fd.nrvo_var && !fd.isCtorDeclaration() && 2198 !isConstruction(e)) 2199 { 2200 /* Generate a new variable to hold the result and initialize it with the 2201 * inlined body of the function: 2202 * tret __inlineretval = e; 2203 */ 2204 auto ei = new ExpInitializer(callLoc, e); 2205 auto tmp = Identifier.generateId("__inlineretval"); 2206 auto vd = new VarDeclaration(callLoc, tf.next, tmp, ei); 2207 vd.storage_class = STC.temp | (tf.isref ? STC.ref_ : STC.rvalue); 2208 vd._linkage = tf.linkage; 2209 vd.parent = parent; 2210 2211 ei.exp = new ConstructExp(callLoc, vd, e); 2212 ei.exp.type = vd.type; 2213 2214 auto de = new DeclarationExp(callLoc, vd); 2215 de.type = Type.tvoid; 2216 2217 // Chain the two together: 2218 // ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval 2219 e = Expression.combine(de, new VarExp(callLoc, vd)); 2220 } 2221 2222 // https://issues.dlang.org/show_bug.cgi?id=15210 2223 if (tf.next.ty == Tvoid && e && e.type.ty != Tvoid) 2224 { 2225 e = new CastExp(callLoc, e, Type.tvoid); 2226 e.type = Type.tvoid; 2227 } 2228 2229 eresult = Expression.combine(eresult, eret, ethis, eparams); 2230 eresult = Expression.combine(eresult, e); 2231 2232 static if (EXPANDINLINE_LOG) 2233 printf("\n[%s] %s expandInline eresult = %s\n", 2234 callLoc.toChars(), fd.toPrettyChars(), eresult.toChars()); 2235 } 2236 2237 // Need to reevaluate whether parent can now be inlined 2238 // in expressions, as we might have inlined statements 2239 parent.inlineStatusExp = ILS.uninitialized; 2240 } 2241 2242 /**************************************************** 2243 * Determine if the value of `e` is the result of construction. 2244 * 2245 * Params: 2246 * e = expression to check 2247 * Returns: 2248 * true for value generated by a constructor or struct literal 2249 */ 2250 private bool isConstruction(Expression e) 2251 { 2252 e = lastComma(e); 2253 2254 if (e.op == EXP.structLiteral) 2255 { 2256 return true; 2257 } 2258 /* Detect: 2259 * structliteral.ctor(args) 2260 */ 2261 else if (e.op == EXP.call) 2262 { 2263 auto ce = cast(CallExp)e; 2264 if (ce.e1.op == EXP.dotVariable) 2265 { 2266 auto dve = cast(DotVarExp)ce.e1; 2267 auto fd = dve.var.isFuncDeclaration(); 2268 if (fd && fd.isCtorDeclaration()) 2269 { 2270 if (dve.e1.op == EXP.structLiteral) 2271 { 2272 return true; 2273 } 2274 } 2275 } 2276 } 2277 return false; 2278 } 2279 2280 2281 /*********************************************************** 2282 * Determine if v is 'head const', meaning 2283 * that once it is initialized it is not changed 2284 * again. 2285 * 2286 * This is done using a primitive flow analysis. 2287 * 2288 * v is head const if v is const or immutable. 2289 * Otherwise, v is assumed to be head const unless one of the 2290 * following is true: 2291 * 1. v is a `ref` or `out` variable 2292 * 2. v is a parameter and fd is a variadic function 2293 * 3. v is assigned to again 2294 * 4. the address of v is taken 2295 * 5. v is referred to by a function nested within fd 2296 * 6. v is ever assigned to a `ref` or `out` variable 2297 * 7. v is ever passed to another function as `ref` or `out` 2298 * 2299 * Params: 2300 * v variable to check 2301 * fd function that v is local to 2302 * Returns: 2303 * true if v's initializer is the only value assigned to v 2304 */ 2305 private bool onlyOneAssign(VarDeclaration v, FuncDeclaration fd) 2306 { 2307 if (!v.type.isMutable()) 2308 return true; // currently the only case handled atm 2309 return false; 2310 } 2311 2312 /************************************************************ 2313 * See if arguments to a function are creating temporaries that 2314 * will need destruction after the function is executed. 2315 * Params: 2316 * arguments = arguments to function 2317 * Returns: 2318 * true if temporaries need destruction 2319 */ 2320 2321 private bool argumentsNeedDtors(Expressions* arguments) 2322 { 2323 if (arguments) 2324 { 2325 foreach (arg; *arguments) 2326 { 2327 if (expNeedsDtor(arg)) 2328 return true; 2329 } 2330 } 2331 return false; 2332 } 2333 2334 /************************************************************ 2335 * See if expression is creating temporaries that 2336 * will need destruction at the end of the scope. 2337 * Params: 2338 * exp = expression 2339 * Returns: 2340 * true if temporaries need destruction 2341 */ 2342 2343 private bool expNeedsDtor(Expression exp) 2344 { 2345 extern (C++) final class NeedsDtor : StoppableVisitor 2346 { 2347 alias visit = typeof(super).visit; 2348 Expression exp; 2349 2350 public: 2351 extern (D) this(Expression exp) scope 2352 { 2353 this.exp = exp; 2354 } 2355 2356 override void visit(Expression) 2357 { 2358 } 2359 2360 override void visit(DeclarationExp de) 2361 { 2362 Dsymbol_needsDtor(de.declaration); 2363 } 2364 2365 void Dsymbol_needsDtor(Dsymbol s) 2366 { 2367 /* This mirrors logic of Dsymbol_toElem() in e2ir.d 2368 * perhaps they can be combined. 2369 */ 2370 2371 void symbolDg(Dsymbol s) 2372 { 2373 Dsymbol_needsDtor(s); 2374 } 2375 2376 if (auto vd = s.isVarDeclaration()) 2377 { 2378 s = s.toAlias(); 2379 if (s != vd) 2380 return Dsymbol_needsDtor(s); 2381 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared | STC.manifest)) 2382 return; 2383 if (vd.needsScopeDtor()) 2384 { 2385 stop = true; 2386 } 2387 } 2388 else if (auto tm = s.isTemplateMixin()) 2389 { 2390 tm.members.foreachDsymbol(&symbolDg); 2391 } 2392 else if (auto ad = s.isAttribDeclaration()) 2393 { 2394 ad.include(null).foreachDsymbol(&symbolDg); 2395 } 2396 else if (auto td = s.isTupleDeclaration()) 2397 { 2398 td.foreachVar(&symbolDg); 2399 } 2400 2401 2402 } 2403 } 2404 2405 scope NeedsDtor ct = new NeedsDtor(exp); 2406 return walkPostorder(exp, ct); 2407 }