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