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 new ErrorExp(); 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 = new IntegerExp(vd._init.loc, 0, Type.tint32); 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 (fd.isUnitTestDeclaration() && !global.params.useUnitTests || 1547 fd.flags & FUNCFLAG.inlineScanned) 1548 return; 1549 if (fd.fbody && !fd.naked) 1550 { 1551 auto againsave = again; 1552 auto parentsave = parent; 1553 parent = fd; 1554 do 1555 { 1556 again = false; 1557 fd.inlineNest++; 1558 fd.flags |= FUNCFLAG.inlineScanned; 1559 inlineScan(fd.fbody); 1560 fd.inlineNest--; 1561 } 1562 while (again); 1563 again = againsave; 1564 parent = parentsave; 1565 } 1566 } 1567 1568 override void visit(AttribDeclaration d) 1569 { 1570 Dsymbols* decls = d.include(null); 1571 if (decls) 1572 { 1573 foreach (i; 0 .. decls.dim) 1574 { 1575 Dsymbol s = (*decls)[i]; 1576 //printf("AttribDeclaration.inlineScan %s\n", s.toChars()); 1577 s.accept(this); 1578 } 1579 } 1580 } 1581 1582 override void visit(AggregateDeclaration ad) 1583 { 1584 //printf("AggregateDeclaration.inlineScan(%s)\n", toChars()); 1585 if (ad.members) 1586 { 1587 foreach (i; 0 .. ad.members.dim) 1588 { 1589 Dsymbol s = (*ad.members)[i]; 1590 //printf("inline scan aggregate symbol '%s'\n", s.toChars()); 1591 s.accept(this); 1592 } 1593 } 1594 } 1595 1596 override void visit(TemplateInstance ti) 1597 { 1598 static if (LOG) 1599 { 1600 printf("TemplateInstance.inlineScan('%s')\n", ti.toChars()); 1601 } 1602 if (!ti.errors && ti.members) 1603 { 1604 foreach (i; 0 .. ti.members.dim) 1605 { 1606 Dsymbol s = (*ti.members)[i]; 1607 s.accept(this); 1608 } 1609 } 1610 } 1611 } 1612 1613 /*********************************************************** 1614 * Test that `fd` can be inlined. 1615 * 1616 * Params: 1617 * hasthis = `true` if the function call has explicit 'this' expression. 1618 * hdrscan = `true` if the inline scan is for 'D header' content. 1619 * statementsToo = `true` if the function call is placed on ExpStatement. 1620 * It means more code-block dependent statements in fd body - ForStatement, 1621 * ThrowStatement, etc. can be inlined. 1622 * 1623 * Returns: 1624 * true if the function body can be expanded. 1625 * 1626 * Todo: 1627 * - Would be able to eliminate `hasthis` parameter, because semantic analysis 1628 * no longer accepts calls of contextful function without valid 'this'. 1629 * - Would be able to eliminate `hdrscan` parameter, because it's always false. 1630 */ 1631 private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo) 1632 { 1633 int cost; 1634 1635 static if (CANINLINE_LOG) 1636 { 1637 printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n", 1638 hasthis, statementsToo, fd.toPrettyChars()); 1639 } 1640 1641 if (fd.needThis() && !hasthis) 1642 return false; 1643 1644 if (fd.inlineNest) 1645 { 1646 static if (CANINLINE_LOG) 1647 { 1648 printf("\t1: no, inlineNest = %d, semanticRun = %d\n", fd.inlineNest, fd.semanticRun); 1649 } 1650 return false; 1651 } 1652 1653 if (fd.semanticRun < PASS.semantic3 && !hdrscan) 1654 { 1655 if (!fd.fbody) 1656 return false; 1657 if (!fd.functionSemantic3()) 1658 return false; 1659 Module.runDeferredSemantic3(); 1660 if (global.errors) 1661 return false; 1662 assert(fd.semanticRun >= PASS.semantic3done); 1663 } 1664 1665 final switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp) 1666 { 1667 case ILS.yes: 1668 static if (CANINLINE_LOG) 1669 { 1670 printf("\t1: yes %s\n", fd.toChars()); 1671 } 1672 return true; 1673 case ILS.no: 1674 static if (CANINLINE_LOG) 1675 { 1676 printf("\t1: no %s\n", fd.toChars()); 1677 } 1678 return false; 1679 case ILS.uninitialized: 1680 break; 1681 } 1682 1683 final switch (fd.inlining) 1684 { 1685 case PINLINE.default_: 1686 break; 1687 case PINLINE.always: 1688 break; 1689 case PINLINE.never: 1690 return false; 1691 } 1692 1693 if (fd.type) 1694 { 1695 TypeFunction tf = fd.type.isTypeFunction(); 1696 1697 // no variadic parameter lists 1698 if (tf.parameterList.varargs == VarArg.variadic) 1699 goto Lno; 1700 1701 /* No lazy parameters when inlining by statement, as the inliner tries to 1702 * operate on the created delegate itself rather than the return value. 1703 * Discussion: https://github.com/dlang/dmd/pull/6815 1704 */ 1705 if (statementsToo && fd.parameters) 1706 { 1707 foreach (param; *fd.parameters) 1708 { 1709 if (param.storage_class & STC.lazy_) 1710 goto Lno; 1711 } 1712 } 1713 1714 static bool hasDtor(Type t) 1715 { 1716 auto tv = t.baseElemOf(); 1717 return tv.ty == Tstruct || tv.ty == Tclass; // for now assume these might have a destructor 1718 } 1719 1720 /* Don't inline a function that returns non-void, but has 1721 * no or multiple return expression. 1722 * When inlining as a statement: 1723 * 1. don't inline array operations, because the order the arguments 1724 * get evaluated gets reversed. This is the same issue that e2ir.callfunc() 1725 * has with them 1726 * 2. don't inline when the return value has a destructor, as it doesn't 1727 * get handled properly 1728 */ 1729 if (tf.next && tf.next.ty != Tvoid && 1730 (!(fd.hasReturnExp & 1) || 1731 statementsToo && (fd.isArrayOp || hasDtor(tf.next))) && 1732 !hdrscan) 1733 { 1734 static if (CANINLINE_LOG) 1735 { 1736 printf("\t3: no %s\n", fd.toChars()); 1737 } 1738 goto Lno; 1739 } 1740 1741 /* https://issues.dlang.org/show_bug.cgi?id=14560 1742 * If fd returns void, all explicit `return;`s 1743 * must not appear in the expanded result. 1744 * See also ReturnStatement.doInlineAs!Statement(). 1745 */ 1746 } 1747 1748 // cannot inline constructor calls because we need to convert: 1749 // return; 1750 // to: 1751 // return this; 1752 // ensure() has magic properties the inliner loses 1753 // require() has magic properties too 1754 // see bug 7699 1755 // no nested references to this frame 1756 if (!fd.fbody || 1757 fd.ident == Id.ensure || 1758 (fd.ident == Id.require && 1759 fd.toParent().isFuncDeclaration() && 1760 fd.toParent().isFuncDeclaration().needThis()) || 1761 !hdrscan && (fd.isSynchronized() || 1762 fd.isImportedSymbol() || 1763 fd.hasNestedFrameRefs() || 1764 (fd.isVirtual() && !fd.isFinalFunc()))) 1765 { 1766 static if (CANINLINE_LOG) 1767 { 1768 printf("\t4: no %s\n", fd.toChars()); 1769 } 1770 goto Lno; 1771 } 1772 1773 // cannot inline functions as statement if they have multiple 1774 // return statements 1775 if ((fd.hasReturnExp & 16) && statementsToo) 1776 { 1777 static if (CANINLINE_LOG) 1778 { 1779 printf("\t5: no %s\n", fd.toChars()); 1780 } 1781 goto Lno; 1782 } 1783 1784 { 1785 cost = inlineCostFunction(fd, hasthis, hdrscan); 1786 } 1787 static if (CANINLINE_LOG) 1788 { 1789 printf("\tcost = %d for %s\n", cost, fd.toChars()); 1790 } 1791 1792 if (tooCostly(cost)) 1793 goto Lno; 1794 if (!statementsToo && cost > COST_MAX) 1795 goto Lno; 1796 1797 if (!hdrscan) 1798 { 1799 // Don't modify inlineStatus for header content scan 1800 if (statementsToo) 1801 fd.inlineStatusStmt = ILS.yes; 1802 else 1803 fd.inlineStatusExp = ILS.yes; 1804 1805 scope InlineScanVisitor v = new InlineScanVisitor(); 1806 fd.accept(v); // Don't scan recursively for header content scan 1807 1808 if (fd.inlineStatusExp == ILS.uninitialized) 1809 { 1810 // Need to redo cost computation, as some statements or expressions have been inlined 1811 cost = inlineCostFunction(fd, hasthis, hdrscan); 1812 static if (CANINLINE_LOG) 1813 { 1814 printf("recomputed cost = %d for %s\n", cost, fd.toChars()); 1815 } 1816 1817 if (tooCostly(cost)) 1818 goto Lno; 1819 if (!statementsToo && cost > COST_MAX) 1820 goto Lno; 1821 1822 if (statementsToo) 1823 fd.inlineStatusStmt = ILS.yes; 1824 else 1825 fd.inlineStatusExp = ILS.yes; 1826 } 1827 } 1828 static if (CANINLINE_LOG) 1829 { 1830 printf("\t2: yes %s\n", fd.toChars()); 1831 } 1832 return true; 1833 1834 Lno: 1835 if (fd.inlining == PINLINE.always) 1836 fd.error("cannot inline function"); 1837 1838 if (!hdrscan) // Don't modify inlineStatus for header content scan 1839 { 1840 if (statementsToo) 1841 fd.inlineStatusStmt = ILS.no; 1842 else 1843 fd.inlineStatusExp = ILS.no; 1844 } 1845 static if (CANINLINE_LOG) 1846 { 1847 printf("\t2: no %s\n", fd.toChars()); 1848 } 1849 return false; 1850 } 1851 1852 /*********************************************************** 1853 * Expand a function call inline, 1854 * ethis.fd(arguments) 1855 * 1856 * Params: 1857 * callLoc = location of CallExp 1858 * fd = function to expand 1859 * parent = function that the call to fd is being expanded into 1860 * eret = if !null then the lvalue of where the nrvo return value goes 1861 * ethis = 'this' reference 1862 * arguments = arguments passed to fd 1863 * asStatements = expand to Statements rather than Expressions 1864 * eresult = if expanding to an expression, this is where the expression is written to 1865 * sresult = if expanding to a statement, this is where the statement is written to 1866 * again = if true, then fd can be inline scanned again because there may be 1867 * more opportunities for inlining 1868 */ 1869 private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration parent, Expression eret, 1870 Expression ethis, Expressions* arguments, bool asStatements, VarDeclaration vthis2, 1871 out Expression eresult, out Statement sresult, out bool again) 1872 { 1873 auto tf = fd.type.isTypeFunction(); 1874 static if (LOG || CANINLINE_LOG || EXPANDINLINE_LOG) 1875 printf("FuncDeclaration.expandInline('%s', %d)\n", fd.toChars(), asStatements); 1876 static if (EXPANDINLINE_LOG) 1877 { 1878 if (eret) printf("\teret = %s\n", eret.toChars()); 1879 if (ethis) printf("\tethis = %s\n", ethis.toChars()); 1880 } 1881 scope ids = new InlineDoState(parent, fd); 1882 1883 if (fd.isNested()) 1884 { 1885 if (!parent.inlinedNestedCallees) 1886 parent.inlinedNestedCallees = new FuncDeclarations(); 1887 parent.inlinedNestedCallees.push(fd); 1888 } 1889 1890 VarDeclaration vret; // will be set the function call result 1891 if (eret) 1892 { 1893 if (auto ve = eret.isVarExp()) 1894 { 1895 vret = ve.var.isVarDeclaration(); 1896 assert(!(vret.storage_class & (STC.out_ | STC.ref_))); 1897 eret = null; 1898 } 1899 else 1900 { 1901 /* Inlining: 1902 * this.field = foo(); // inside constructor 1903 */ 1904 auto ei = new ExpInitializer(callLoc, null); 1905 auto tmp = Identifier.generateId("__retvar"); 1906 vret = new VarDeclaration(fd.loc, eret.type, tmp, ei); 1907 vret.storage_class |= STC.temp | STC.ref_; 1908 vret.linkage = LINK.d; 1909 vret.parent = parent; 1910 1911 ei.exp = new ConstructExp(fd.loc, vret, eret); 1912 ei.exp.type = vret.type; 1913 1914 auto de = new DeclarationExp(fd.loc, vret); 1915 de.type = Type.tvoid; 1916 eret = de; 1917 } 1918 1919 if (!asStatements && fd.nrvo_var) 1920 { 1921 ids.from.push(fd.nrvo_var); 1922 ids.to.push(vret); 1923 } 1924 } 1925 else 1926 { 1927 if (!asStatements && fd.nrvo_var) 1928 { 1929 auto tmp = Identifier.generateId("__retvar"); 1930 vret = new VarDeclaration(fd.loc, fd.nrvo_var.type, tmp, new VoidInitializer(fd.loc)); 1931 assert(!tf.isref); 1932 vret.storage_class = STC.temp | STC.rvalue; 1933 vret.linkage = tf.linkage; 1934 vret.parent = parent; 1935 1936 auto de = new DeclarationExp(fd.loc, vret); 1937 de.type = Type.tvoid; 1938 eret = de; 1939 1940 ids.from.push(fd.nrvo_var); 1941 ids.to.push(vret); 1942 } 1943 } 1944 1945 // Set up vthis 1946 VarDeclaration vthis; 1947 if (ethis) 1948 { 1949 Expression e0; 1950 ethis = Expression.extractLast(ethis, e0); 1951 assert(vthis2 || !fd.isThis2); 1952 if (vthis2) 1953 { 1954 // void*[2] __this = [ethis, this] 1955 if (ethis.type.ty == Tstruct) 1956 { 1957 // ðis 1958 Type t = ethis.type.pointerTo(); 1959 ethis = new AddrExp(ethis.loc, ethis); 1960 ethis.type = t; 1961 } 1962 auto elements = new Expressions(2); 1963 (*elements)[0] = ethis; 1964 (*elements)[1] = new NullExp(Loc.initial, Type.tvoidptr); 1965 Expression ae = new ArrayLiteralExp(vthis2.loc, vthis2.type, elements); 1966 Expression ce = new ConstructExp(vthis2.loc, vthis2, ae); 1967 ce.type = vthis2.type; 1968 vthis2._init = new ExpInitializer(vthis2.loc, ce); 1969 vthis = vthis2; 1970 } 1971 else if (auto ve = ethis.isVarExp()) 1972 { 1973 vthis = ve.var.isVarDeclaration(); 1974 } 1975 else 1976 { 1977 //assert(ethis.type.ty != Tpointer); 1978 if (ethis.type.ty == Tpointer) 1979 { 1980 Type t = ethis.type.nextOf(); 1981 ethis = new PtrExp(ethis.loc, ethis); 1982 ethis.type = t; 1983 } 1984 1985 auto ei = new ExpInitializer(fd.loc, ethis); 1986 vthis = new VarDeclaration(fd.loc, ethis.type, Id.This, ei); 1987 if (ethis.type.ty != Tclass) 1988 vthis.storage_class = STC.ref_; 1989 else 1990 vthis.storage_class = STC.in_; 1991 vthis.linkage = LINK.d; 1992 vthis.parent = parent; 1993 1994 ei.exp = new ConstructExp(fd.loc, vthis, ethis); 1995 ei.exp.type = vthis.type; 1996 1997 auto de = new DeclarationExp(fd.loc, vthis); 1998 de.type = Type.tvoid; 1999 e0 = Expression.combine(e0, de); 2000 } 2001 ethis = e0; 2002 2003 ids.vthis = vthis; 2004 } 2005 2006 // Set up parameters 2007 Expression eparams; 2008 if (arguments && arguments.dim) 2009 { 2010 assert(fd.parameters.dim == arguments.dim); 2011 foreach (i; 0 .. arguments.dim) 2012 { 2013 auto vfrom = (*fd.parameters)[i]; 2014 auto arg = (*arguments)[i]; 2015 2016 auto ei = new ExpInitializer(vfrom.loc, arg); 2017 auto vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei); 2018 vto.storage_class |= vfrom.storage_class & (STC.temp | STC.in_ | STC.out_ | STC.lazy_ | STC.ref_); 2019 vto.linkage = vfrom.linkage; 2020 vto.parent = parent; 2021 //printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class); 2022 //printf("vto.parent = '%s'\n", parent.toChars()); 2023 2024 // Even if vto is STC.lazy_, `vto = arg` is handled correctly in glue layer. 2025 ei.exp = new BlitExp(vto.loc, vto, arg); 2026 ei.exp.type = vto.type; 2027 2028 ids.from.push(vfrom); 2029 ids.to.push(vto); 2030 2031 auto de = new DeclarationExp(vto.loc, vto); 2032 de.type = Type.tvoid; 2033 eparams = Expression.combine(eparams, de); 2034 2035 /* If function pointer or delegate parameters are present, 2036 * inline scan again because if they are initialized to a symbol, 2037 * any calls to the fp or dg can be inlined. 2038 */ 2039 if (vfrom.type.ty == Tdelegate || 2040 vfrom.type.ty == Tpointer && vfrom.type.nextOf().ty == Tfunction) 2041 { 2042 if (auto ve = arg.isVarExp()) 2043 { 2044 if (ve.var.isFuncDeclaration()) 2045 again = true; 2046 } 2047 else if (auto se = arg.isSymOffExp()) 2048 { 2049 if (se.var.isFuncDeclaration()) 2050 again = true; 2051 } 2052 else if (arg.op == TOK.function_ || arg.op == TOK.delegate_) 2053 again = true; 2054 } 2055 } 2056 } 2057 2058 if (asStatements) 2059 { 2060 /* Construct: 2061 * { eret; ethis; eparams; fd.fbody; } 2062 * or: 2063 * { eret; ethis; try { eparams; fd.fbody; } finally { vthis.edtor; } } 2064 */ 2065 2066 auto as = new Statements(); 2067 if (eret) 2068 as.push(new ExpStatement(callLoc, eret)); 2069 if (ethis) 2070 as.push(new ExpStatement(callLoc, ethis)); 2071 2072 auto as2 = as; 2073 if (vthis && !vthis.isDataseg()) 2074 { 2075 if (vthis.needsScopeDtor()) 2076 { 2077 // same with ExpStatement.scopeCode() 2078 as2 = new Statements(); 2079 vthis.storage_class |= STC.nodtor; 2080 } 2081 } 2082 2083 if (eparams) 2084 as2.push(new ExpStatement(callLoc, eparams)); 2085 2086 fd.inlineNest++; 2087 Statement s = doInlineAs!Statement(fd.fbody, ids); 2088 fd.inlineNest--; 2089 as2.push(s); 2090 2091 if (as2 != as) 2092 { 2093 as.push(new TryFinallyStatement(callLoc, 2094 new CompoundStatement(callLoc, as2), 2095 new DtorExpStatement(callLoc, vthis.edtor, vthis))); 2096 } 2097 2098 sresult = new ScopeStatement(callLoc, new CompoundStatement(callLoc, as), callLoc); 2099 2100 static if (EXPANDINLINE_LOG) 2101 printf("\n[%s] %s expandInline sresult =\n%s\n", 2102 callLoc.toChars(), fd.toPrettyChars(), sresult.toChars()); 2103 } 2104 else 2105 { 2106 /* Construct: 2107 * (eret, ethis, eparams, fd.fbody) 2108 */ 2109 2110 fd.inlineNest++; 2111 auto e = doInlineAs!Expression(fd.fbody, ids); 2112 fd.inlineNest--; 2113 2114 // https://issues.dlang.org/show_bug.cgi?id=11322 2115 if (tf.isref) 2116 e = e.toLvalue(null, null); 2117 2118 /* If the inlined function returns a copy of a struct, 2119 * and then the return value is used subsequently as an 2120 * lvalue, as in a struct return that is then used as a 'this'. 2121 * Taking the address of the return value will be taking the address 2122 * of the original, not the copy. Fix this by assigning the return value to 2123 * a temporary, then returning the temporary. If the temporary is used as an 2124 * lvalue, it will work. 2125 * This only happens with struct returns. 2126 * See https://issues.dlang.org/show_bug.cgi?id=2127 for an example. 2127 * 2128 * On constructor call making __inlineretval is merely redundant, because 2129 * the returned reference is exactly same as vthis, and the 'this' variable 2130 * already exists at the caller side. 2131 */ 2132 if (tf.next.ty == Tstruct && !fd.nrvo_var && !fd.isCtorDeclaration() && 2133 !isConstruction(e)) 2134 { 2135 /* Generate a new variable to hold the result and initialize it with the 2136 * inlined body of the function: 2137 * tret __inlineretval = e; 2138 */ 2139 auto ei = new ExpInitializer(callLoc, e); 2140 auto tmp = Identifier.generateId("__inlineretval"); 2141 auto vd = new VarDeclaration(callLoc, tf.next, tmp, ei); 2142 vd.storage_class = STC.temp | (tf.isref ? STC.ref_ : STC.rvalue); 2143 vd.linkage = tf.linkage; 2144 vd.parent = parent; 2145 2146 ei.exp = new ConstructExp(callLoc, vd, e); 2147 ei.exp.type = vd.type; 2148 2149 auto de = new DeclarationExp(callLoc, vd); 2150 de.type = Type.tvoid; 2151 2152 // Chain the two together: 2153 // ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval 2154 e = Expression.combine(de, new VarExp(callLoc, vd)); 2155 } 2156 2157 // https://issues.dlang.org/show_bug.cgi?id=15210 2158 if (tf.next.ty == Tvoid && e && e.type.ty != Tvoid) 2159 { 2160 e = new CastExp(callLoc, e, Type.tvoid); 2161 e.type = Type.tvoid; 2162 } 2163 2164 eresult = Expression.combine(eresult, eret, ethis, eparams); 2165 eresult = Expression.combine(eresult, e); 2166 2167 static if (EXPANDINLINE_LOG) 2168 printf("\n[%s] %s expandInline eresult = %s\n", 2169 callLoc.toChars(), fd.toPrettyChars(), eresult.toChars()); 2170 } 2171 2172 // Need to reevaluate whether parent can now be inlined 2173 // in expressions, as we might have inlined statements 2174 parent.inlineStatusExp = ILS.uninitialized; 2175 } 2176 2177 /**************************************************** 2178 * Determine if the value of `e` is the result of construction. 2179 * 2180 * Params: 2181 * e = expression to check 2182 * Returns: 2183 * true for value generated by a constructor or struct literal 2184 */ 2185 private bool isConstruction(Expression e) 2186 { 2187 e = lastComma(e); 2188 2189 if (e.op == TOK.structLiteral) 2190 { 2191 return true; 2192 } 2193 /* Detect: 2194 * structliteral.ctor(args) 2195 */ 2196 else if (e.op == TOK.call) 2197 { 2198 auto ce = cast(CallExp)e; 2199 if (ce.e1.op == TOK.dotVariable) 2200 { 2201 auto dve = cast(DotVarExp)ce.e1; 2202 auto fd = dve.var.isFuncDeclaration(); 2203 if (fd && fd.isCtorDeclaration()) 2204 { 2205 if (dve.e1.op == TOK.structLiteral) 2206 { 2207 return true; 2208 } 2209 } 2210 } 2211 } 2212 return false; 2213 } 2214 2215 2216 /*********************************************************** 2217 * Determine if v is 'head const', meaning 2218 * that once it is initialized it is not changed 2219 * again. 2220 * 2221 * This is done using a primitive flow analysis. 2222 * 2223 * v is head const if v is const or immutable. 2224 * Otherwise, v is assumed to be head const unless one of the 2225 * following is true: 2226 * 1. v is a `ref` or `out` variable 2227 * 2. v is a parameter and fd is a variadic function 2228 * 3. v is assigned to again 2229 * 4. the address of v is taken 2230 * 5. v is referred to by a function nested within fd 2231 * 6. v is ever assigned to a `ref` or `out` variable 2232 * 7. v is ever passed to another function as `ref` or `out` 2233 * 2234 * Params: 2235 * v variable to check 2236 * fd function that v is local to 2237 * Returns: 2238 * true if v's initializer is the only value assigned to v 2239 */ 2240 private bool onlyOneAssign(VarDeclaration v, FuncDeclaration fd) 2241 { 2242 if (!v.type.isMutable()) 2243 return true; // currently the only case handled atm 2244 return false; 2245 } 2246 2247 /************************************************************ 2248 * See if arguments to a function are creating temporaries that 2249 * will need destruction after the function is executed. 2250 * Params: 2251 * arguments = arguments to function 2252 * Returns: 2253 * true if temporaries need destruction 2254 */ 2255 2256 private bool argumentsNeedDtors(Expressions* arguments) 2257 { 2258 if (arguments) 2259 { 2260 foreach (arg; *arguments) 2261 { 2262 if (argNeedsDtor(arg)) 2263 return true; 2264 } 2265 } 2266 return false; 2267 } 2268 2269 /************************************************************ 2270 * See if argument to a function is creating temporaries that 2271 * will need destruction after the function is executed. 2272 * Params: 2273 * arg = argument to function 2274 * Returns: 2275 * true if temporaries need destruction 2276 */ 2277 2278 private bool argNeedsDtor(Expression arg) 2279 { 2280 extern (C++) final class NeedsDtor : StoppableVisitor 2281 { 2282 alias visit = typeof(super).visit; 2283 Expression arg; 2284 2285 public: 2286 extern (D) this(Expression arg) 2287 { 2288 this.arg = arg; 2289 } 2290 2291 override void visit(Expression) 2292 { 2293 } 2294 2295 override void visit(DeclarationExp de) 2296 { 2297 if (de != arg) 2298 Dsymbol_needsDtor(de.declaration); 2299 } 2300 2301 void Dsymbol_needsDtor(Dsymbol s) 2302 { 2303 /* This mirrors logic of Dsymbol_toElem() in e2ir.d 2304 * perhaps they can be combined. 2305 */ 2306 2307 void symbolDg(Dsymbol s) 2308 { 2309 Dsymbol_needsDtor(s); 2310 } 2311 2312 if (auto vd = s.isVarDeclaration()) 2313 { 2314 s = s.toAlias(); 2315 if (s != vd) 2316 return Dsymbol_needsDtor(s); 2317 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.tls | STC.gshared | STC.manifest)) 2318 return; 2319 if (vd.needsScopeDtor()) 2320 { 2321 stop = true; 2322 } 2323 } 2324 else if (auto tm = s.isTemplateMixin()) 2325 { 2326 tm.members.foreachDsymbol(&symbolDg); 2327 } 2328 else if (auto ad = s.isAttribDeclaration()) 2329 { 2330 ad.include(null).foreachDsymbol(&symbolDg); 2331 } 2332 else if (auto td = s.isTupleDeclaration()) 2333 { 2334 foreach (o; *td.objects) 2335 { 2336 import dmd.root.rootobject; 2337 2338 if (o.dyncast() == DYNCAST.expression) 2339 { 2340 Expression eo = cast(Expression)o; 2341 if (eo.op == TOK.dSymbol) 2342 { 2343 DsymbolExp se = cast(DsymbolExp)eo; 2344 Dsymbol_needsDtor(se.s); 2345 } 2346 } 2347 } 2348 } 2349 2350 2351 } 2352 } 2353 2354 scope NeedsDtor ct = new NeedsDtor(arg); 2355 return walkPostorder(arg, ct); 2356 }