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