1 /** 2 * A scope as defined by curly braces `{}`. 3 * 4 * Not to be confused with the `scope` storage class. 5 * 6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dscope.d, _dscope.d) 10 * Documentation: https://dlang.org/phobos/dmd_dscope.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dscope.d 12 */ 13 14 module dmd.dscope; 15 16 import core.stdc.stdio; 17 import core.stdc.string; 18 import dmd.aggregate; 19 import dmd.arraytypes; 20 import dmd.attrib; 21 import dmd.ctorflow; 22 import dmd.dclass; 23 import dmd.declaration; 24 import dmd.dmodule; 25 import dmd.doc; 26 import dmd.dsymbol; 27 import dmd.dsymbolsem; 28 import dmd.dtemplate; 29 import dmd.expression; 30 import dmd.errors; 31 import dmd.func; 32 import dmd.globals; 33 import dmd.id; 34 import dmd.identifier; 35 import dmd.root.outbuffer; 36 import dmd.root.rmem; 37 import dmd.root.speller; 38 import dmd.statement; 39 import dmd.tokens; 40 41 //version=LOGSEARCH; 42 43 44 // List of flags that can be applied to this `Scope` 45 enum SCOPE 46 { 47 ctor = 0x0001, /// constructor type 48 noaccesscheck = 0x0002, /// don't do access checks 49 condition = 0x0004, /// inside static if/assert condition 50 debug_ = 0x0008, /// inside debug conditional 51 constraint = 0x0010, /// inside template constraint 52 invariant_ = 0x0020, /// inside invariant code 53 require = 0x0040, /// inside in contract code 54 ensure = 0x0060, /// inside out contract code 55 contract = 0x0060, /// [mask] we're inside contract code 56 ctfe = 0x0080, /// inside a ctfe-only expression 57 compile = 0x0100, /// inside __traits(compile) 58 ignoresymbolvisibility = 0x0200, /// ignore symbol visibility 59 /// https://issues.dlang.org/show_bug.cgi?id=15907 60 onlysafeaccess = 0x0400, /// unsafe access is not allowed for @safe code 61 free = 0x8000, /// is on free list 62 63 fullinst = 0x10000, /// fully instantiate templates 64 alias_ = 0x20000, /// inside alias declaration. 65 66 // The following are mutually exclusive 67 printf = 0x4_0000, /// printf-style function 68 scanf = 0x8_0000, /// scanf-style function 69 } 70 71 /// Flags that are carried along with a scope push() 72 private enum PersistentFlags = 73 SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint | 74 SCOPE.noaccesscheck | SCOPE.onlysafeaccess | SCOPE.ignoresymbolvisibility | 75 SCOPE.printf | SCOPE.scanf; 76 77 struct Scope 78 { 79 Scope* enclosing; /// enclosing Scope 80 81 Module _module; /// Root module 82 ScopeDsymbol scopesym; /// current symbol 83 FuncDeclaration func; /// function we are in 84 Dsymbol parent; /// parent to use 85 LabelStatement slabel; /// enclosing labelled statement 86 SwitchStatement sw; /// enclosing switch statement 87 Statement tryBody; /// enclosing _body of TryCatchStatement or TryFinallyStatement 88 TryFinallyStatement tf; /// enclosing try finally statement 89 ScopeGuardStatement os; /// enclosing scope(xxx) statement 90 Statement sbreak; /// enclosing statement that supports "break" 91 Statement scontinue; /// enclosing statement that supports "continue" 92 ForeachStatement fes; /// if nested function for ForeachStatement, this is it 93 Scope* callsc; /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__ 94 Dsymbol inunion; /// != null if processing members of a union 95 bool nofree; /// true if shouldn't free it 96 bool inLoop; /// true if inside a loop (where constructor calls aren't allowed) 97 int intypeof; /// in typeof(exp) 98 VarDeclaration lastVar; /// Previous symbol used to prevent goto-skips-init 99 100 /* If minst && !tinst, it's in definitely non-speculative scope (eg. module member scope). 101 * If !minst && !tinst, it's in definitely speculative scope (eg. template constraint). 102 * If minst && tinst, it's in instantiated code scope without speculation. 103 * If !minst && tinst, it's in instantiated code scope with speculation. 104 */ 105 Module minst; /// root module where the instantiated templates should belong to 106 TemplateInstance tinst; /// enclosing template instance 107 108 CtorFlow ctorflow; /// flow analysis for constructors 109 110 /// alignment for struct members 111 AlignDeclaration aligndecl; 112 113 /// C++ namespace this symbol is in 114 CPPNamespaceDeclaration namespace; 115 116 /// linkage for external functions 117 LINK linkage = LINK.d; 118 119 /// mangle type 120 CPPMANGLE cppmangle = CPPMANGLE.def; 121 122 /// inlining strategy for functions 123 PragmaDeclaration inlining; 124 125 /// visibility for class members 126 Visibility visibility = Visibility(Visibility.Kind.public_); 127 int explicitVisibility; /// set if in an explicit visibility attribute 128 129 StorageClass stc; /// storage class 130 131 DeprecatedDeclaration depdecl; /// customized deprecation message 132 133 uint flags; 134 135 // user defined attributes 136 UserAttributeDeclaration userAttribDecl; 137 138 DocComment* lastdc; /// documentation comment for last symbol at this scope 139 uint[void*] anchorCounts; /// lookup duplicate anchor name count 140 Identifier prevAnchor; /// qualified symbol name of last doc anchor 141 142 AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value, 143 /// do not set wasRead for it 144 145 extern (D) __gshared Scope* freelist; 146 147 extern (D) static Scope* alloc() 148 { 149 if (freelist) 150 { 151 Scope* s = freelist; 152 freelist = s.enclosing; 153 //printf("freelist %p\n", s); 154 assert(s.flags & SCOPE.free); 155 s.flags &= ~SCOPE.free; 156 return s; 157 } 158 return new Scope(); 159 } 160 161 extern (D) static Scope* createGlobal(Module _module) 162 { 163 Scope* sc = Scope.alloc(); 164 *sc = Scope.init; 165 sc._module = _module; 166 sc.minst = _module; 167 sc.scopesym = new ScopeDsymbol(); 168 sc.scopesym.symtab = new DsymbolTable(); 169 // Add top level package as member of this global scope 170 Dsymbol m = _module; 171 while (m.parent) 172 m = m.parent; 173 m.addMember(null, sc.scopesym); 174 m.parent = null; // got changed by addMember() 175 // Create the module scope underneath the global scope 176 sc = sc.push(_module); 177 sc.parent = _module; 178 return sc; 179 } 180 181 extern (C++) Scope* copy() 182 { 183 Scope* sc = Scope.alloc(); 184 *sc = this; 185 /* https://issues.dlang.org/show_bug.cgi?id=11777 186 * The copied scope should not inherit fieldinit. 187 */ 188 sc.ctorflow.fieldinit = null; 189 return sc; 190 } 191 192 extern (C++) Scope* push() 193 { 194 Scope* s = copy(); 195 //printf("Scope::push(this = %p) new = %p\n", this, s); 196 assert(!(flags & SCOPE.free)); 197 s.scopesym = null; 198 s.enclosing = &this; 199 debug 200 { 201 if (enclosing) 202 assert(!(enclosing.flags & SCOPE.free)); 203 if (s == enclosing) 204 { 205 printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing); 206 } 207 assert(s != enclosing); 208 } 209 s.slabel = null; 210 s.nofree = false; 211 s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup; 212 s.flags = (flags & PersistentFlags); 213 s.lastdc = null; 214 assert(&this != s); 215 return s; 216 } 217 218 extern (C++) Scope* push(ScopeDsymbol ss) 219 { 220 //printf("Scope::push(%s)\n", ss.toChars()); 221 Scope* s = push(); 222 s.scopesym = ss; 223 return s; 224 } 225 226 extern (C++) Scope* pop() 227 { 228 //printf("Scope::pop() %p nofree = %d\n", this, nofree); 229 if (enclosing) 230 enclosing.ctorflow.OR(ctorflow); 231 ctorflow.freeFieldinit(); 232 233 Scope* enc = enclosing; 234 if (!nofree) 235 { 236 if (mem.isGCEnabled) 237 this = this.init; 238 enclosing = freelist; 239 freelist = &this; 240 flags |= SCOPE.free; 241 } 242 return enc; 243 } 244 245 /************************* 246 * Similar to pop(), but the results in `this` are not folded 247 * into `enclosing`. 248 */ 249 extern (D) void detach() 250 { 251 ctorflow.freeFieldinit(); 252 enclosing = null; 253 pop(); 254 } 255 256 extern (C++) Scope* startCTFE() 257 { 258 Scope* sc = this.push(); 259 sc.flags = this.flags | SCOPE.ctfe; 260 version (none) 261 { 262 /* TODO: Currently this is not possible, because we need to 263 * unspeculative some types and symbols if they are necessary for the 264 * final executable. Consider: 265 * 266 * struct S(T) { 267 * string toString() const { return "instantiated"; } 268 * } 269 * enum x = S!int(); 270 * void main() { 271 * // To call x.toString in runtime, compiler should unspeculative S!int. 272 * assert(x.toString() == "instantiated"); 273 * } 274 */ 275 // If a template is instantiated from CT evaluated expression, 276 // compiler can elide its code generation. 277 sc.tinst = null; 278 sc.minst = null; 279 } 280 return sc; 281 } 282 283 extern (C++) Scope* endCTFE() 284 { 285 assert(flags & SCOPE.ctfe); 286 return pop(); 287 } 288 289 290 /******************************* 291 * Merge results of `ctorflow` into `this`. 292 * Params: 293 * loc = for error messages 294 * ctorflow = flow results to merge in 295 */ 296 extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow) 297 { 298 if (!mergeCallSuper(this.ctorflow.callSuper, ctorflow.callSuper)) 299 error(loc, "one path skips constructor"); 300 301 const fies = ctorflow.fieldinit; 302 if (this.ctorflow.fieldinit.length && fies.length) 303 { 304 FuncDeclaration f = func; 305 if (fes) 306 f = fes.func; 307 auto ad = f.isMemberDecl(); 308 assert(ad); 309 foreach (i, v; ad.fields) 310 { 311 bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); 312 auto fieldInit = &this.ctorflow.fieldinit[i]; 313 const fiesCurrent = fies[i]; 314 if (fieldInit.loc is Loc.init) 315 fieldInit.loc = fiesCurrent.loc; 316 if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit) 317 { 318 error(loc, "one path skips field `%s`", v.toChars()); 319 } 320 } 321 } 322 } 323 324 extern (C++) Module instantiatingModule() 325 { 326 // TODO: in speculative context, returning 'module' is correct? 327 return minst ? minst : _module; 328 } 329 330 /************************************ 331 * Perform unqualified name lookup by following the chain of scopes up 332 * until found. 333 * 334 * Params: 335 * loc = location to use for error messages 336 * ident = name to look up 337 * pscopesym = if supplied and name is found, set to scope that ident was found in 338 * flags = modify search based on flags 339 * 340 * Returns: 341 * symbol if found, null if not 342 */ 343 extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, Dsymbol* pscopesym, int flags = IgnoreNone) 344 { 345 version (LOGSEARCH) 346 { 347 printf("Scope.search(%p, '%s' flags=x%x)\n", &this, ident.toChars(), flags); 348 // Print scope chain 349 for (Scope* sc = &this; sc; sc = sc.enclosing) 350 { 351 if (!sc.scopesym) 352 continue; 353 printf("\tscope %s\n", sc.scopesym.toChars()); 354 } 355 356 static void printMsg(string txt, Dsymbol s) 357 { 358 printf("%.*s %s.%s, kind = '%s'\n", cast(int)txt.length, txt.ptr, 359 s.parent ? s.parent.toChars() : "", s.toChars(), s.kind()); 360 } 361 } 362 363 // This function is called only for unqualified lookup 364 assert(!(flags & (SearchLocalsOnly | SearchImportsOnly))); 365 366 /* If ident is "start at module scope", only look at module scope 367 */ 368 if (ident == Id.empty) 369 { 370 // Look for module scope 371 for (Scope* sc = &this; sc; sc = sc.enclosing) 372 { 373 assert(sc != sc.enclosing); 374 if (!sc.scopesym) 375 continue; 376 if (Dsymbol s = sc.scopesym.isModule()) 377 { 378 //printMsg("\tfound", s); 379 if (pscopesym) 380 *pscopesym = sc.scopesym; 381 return s; 382 } 383 } 384 return null; 385 } 386 387 Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, int flags, Expression* exp) 388 { 389 import dmd.mtype; 390 if (!ad || !ad.aliasthis) 391 return null; 392 393 Declaration decl = ad.aliasthis.sym.isDeclaration(); 394 if (!decl) 395 return null; 396 397 Type t = decl.type; 398 ScopeDsymbol sds; 399 TypeClass tc; 400 TypeStruct ts; 401 switch(t.ty) 402 { 403 case Tstruct: 404 ts = cast(TypeStruct)t; 405 sds = ts.sym; 406 break; 407 case Tclass: 408 tc = cast(TypeClass)t; 409 sds = tc.sym; 410 break; 411 case Tinstance: 412 sds = (cast(TypeInstance)t).tempinst; 413 break; 414 case Tenum: 415 sds = (cast(TypeEnum)t).sym; 416 break; 417 default: break; 418 } 419 420 if (!sds) 421 return null; 422 423 Dsymbol ret = sds.search(loc, ident, flags); 424 if (ret) 425 { 426 *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident); 427 *exp = new DotIdExp(loc, *exp, ident); 428 return ret; 429 } 430 431 if (!ts && !tc) 432 return null; 433 434 Dsymbol s; 435 *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident); 436 if (ts && !(ts.att & AliasThisRec.tracing)) 437 { 438 ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracing); 439 s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp); 440 ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracing); 441 } 442 else if(tc && !(tc.att & AliasThisRec.tracing)) 443 { 444 tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracing); 445 s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp); 446 tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracing); 447 } 448 return s; 449 } 450 451 Dsymbol searchScopes(int flags) 452 { 453 for (Scope* sc = &this; sc; sc = sc.enclosing) 454 { 455 assert(sc != sc.enclosing); 456 if (!sc.scopesym) 457 continue; 458 //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags); 459 460 if (sc.scopesym.isModule()) 461 flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed 462 463 if (Dsymbol s = sc.scopesym.search(loc, ident, flags)) 464 { 465 if (!(flags & (SearchImportsOnly | IgnoreErrors)) && 466 ident == Id.length && sc.scopesym.isArrayScopeSymbol() && 467 sc.enclosing && sc.enclosing.search(loc, ident, null, flags)) 468 { 469 warning(s.loc, "array `length` hides other `length` name in outer scope"); 470 } 471 //printMsg("\tfound local", s); 472 if (pscopesym) 473 *pscopesym = sc.scopesym; 474 return s; 475 } 476 477 if (global.params.fixAliasThis) 478 { 479 Expression exp = new ThisExp(loc); 480 Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp); 481 if (aliasSym) 482 { 483 //printf("found aliassym: %s\n", aliasSym.toChars()); 484 if (pscopesym) 485 *pscopesym = new ExpressionDsymbol(exp); 486 return aliasSym; 487 } 488 } 489 490 // Stop when we hit a module, but keep going if that is not just under the global scope 491 if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing)) 492 break; 493 } 494 return null; 495 } 496 497 if (this.flags & SCOPE.ignoresymbolvisibility) 498 flags |= IgnoreSymbolVisibility; 499 500 // First look in local scopes 501 Dsymbol s = searchScopes(flags | SearchLocalsOnly); 502 version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s); 503 if (!s) 504 { 505 // Second look in imported modules 506 s = searchScopes(flags | SearchImportsOnly); 507 version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s); 508 } 509 return s; 510 } 511 512 extern (D) Dsymbol search_correct(Identifier ident) 513 { 514 if (global.gag) 515 return null; // don't do it for speculative compiles; too time consuming 516 517 /************************************************ 518 * Given the failed search attempt, try to find 519 * one with a close spelling. 520 * Params: 521 * seed = identifier to search for 522 * cost = set to the cost, which rises with each outer scope 523 * Returns: 524 * Dsymbol if found, null if not 525 */ 526 extern (D) Dsymbol scope_search_fp(const(char)[] seed, out int cost) 527 { 528 //printf("scope_search_fp('%s')\n", seed); 529 /* If not in the lexer's string table, it certainly isn't in the symbol table. 530 * Doing this first is a lot faster. 531 */ 532 if (!seed.length) 533 return null; 534 Identifier id = Identifier.lookup(seed); 535 if (!id) 536 return null; 537 Scope* sc = &this; 538 Module.clearCache(); 539 Dsymbol scopesym = null; 540 Dsymbol s = sc.search(Loc.initial, id, &scopesym, IgnoreErrors); 541 if (!s) 542 return null; 543 544 // Do not show `@disable`d declarations 545 if (auto decl = s.isDeclaration()) 546 if (decl.storage_class & STC.disable) 547 return null; 548 // Or `deprecated` ones if we're not in a deprecated scope 549 if (s.isDeprecated() && !sc.isDeprecated()) 550 return null; 551 552 for (cost = 0; sc; sc = sc.enclosing, ++cost) 553 if (sc.scopesym == scopesym) 554 break; 555 if (scopesym != s.parent) 556 { 557 ++cost; // got to the symbol through an import 558 if (s.visible().kind == Visibility.Kind.private_) 559 return null; 560 } 561 return s; 562 } 563 564 Dsymbol scopesym = null; 565 // search for exact name first 566 if (auto s = search(Loc.initial, ident, &scopesym, IgnoreErrors)) 567 return s; 568 return speller!scope_search_fp(ident.toString()); 569 } 570 571 /************************************ 572 * Maybe `ident` was a C or C++ name. Check for that, 573 * and suggest the D equivalent. 574 * Params: 575 * ident = unknown identifier 576 * Returns: 577 * D identifier string if found, null if not 578 */ 579 extern (D) static const(char)* search_correct_C(Identifier ident) 580 { 581 TOK tok; 582 if (ident == Id.NULL) 583 tok = TOK.null_; 584 else if (ident == Id.TRUE) 585 tok = TOK.true_; 586 else if (ident == Id.FALSE) 587 tok = TOK.false_; 588 else if (ident == Id.unsigned) 589 tok = TOK.uns32; 590 else if (ident == Id.wchar_t) 591 tok = global.params.targetOS == TargetOS.Windows ? TOK.wchar_ : TOK.dchar_; 592 else 593 return null; 594 return Token.toChars(tok); 595 } 596 597 extern (D) Dsymbol insert(Dsymbol s) 598 { 599 if (VarDeclaration vd = s.isVarDeclaration()) 600 { 601 if (lastVar) 602 vd.lastVar = lastVar; 603 lastVar = vd; 604 } 605 else if (WithScopeSymbol ss = s.isWithScopeSymbol()) 606 { 607 if (VarDeclaration vd = ss.withstate.wthis) 608 { 609 if (lastVar) 610 vd.lastVar = lastVar; 611 lastVar = vd; 612 } 613 return null; 614 } 615 for (Scope* sc = &this; sc; sc = sc.enclosing) 616 { 617 //printf("\tsc = %p\n", sc); 618 if (sc.scopesym) 619 { 620 //printf("\t\tsc.scopesym = %p\n", sc.scopesym); 621 if (!sc.scopesym.symtab) 622 sc.scopesym.symtab = new DsymbolTable(); 623 return sc.scopesym.symtabInsert(s); 624 } 625 } 626 assert(0); 627 } 628 629 /******************************************** 630 * Search enclosing scopes for ClassDeclaration. 631 */ 632 extern (C++) ClassDeclaration getClassScope() 633 { 634 for (Scope* sc = &this; sc; sc = sc.enclosing) 635 { 636 if (!sc.scopesym) 637 continue; 638 ClassDeclaration cd = sc.scopesym.isClassDeclaration(); 639 if (cd) 640 return cd; 641 } 642 return null; 643 } 644 645 /******************************************** 646 * Search enclosing scopes for ClassDeclaration. 647 */ 648 extern (C++) AggregateDeclaration getStructClassScope() 649 { 650 for (Scope* sc = &this; sc; sc = sc.enclosing) 651 { 652 if (!sc.scopesym) 653 continue; 654 AggregateDeclaration ad = sc.scopesym.isClassDeclaration(); 655 if (ad) 656 return ad; 657 ad = sc.scopesym.isStructDeclaration(); 658 if (ad) 659 return ad; 660 } 661 return null; 662 } 663 664 /******************************************* 665 * For TemplateDeclarations, we need to remember the Scope 666 * where it was declared. So mark the Scope as not 667 * to be free'd. 668 */ 669 extern (D) void setNoFree() 670 { 671 //int i = 0; 672 //printf("Scope::setNoFree(this = %p)\n", this); 673 for (Scope* sc = &this; sc; sc = sc.enclosing) 674 { 675 //printf("\tsc = %p\n", sc); 676 sc.nofree = true; 677 assert(!(flags & SCOPE.free)); 678 //assert(sc != sc.enclosing); 679 //assert(!sc.enclosing || sc != sc.enclosing.enclosing); 680 //if (++i == 10) 681 // assert(0); 682 } 683 } 684 685 structalign_t alignment() 686 { 687 if (aligndecl) 688 return aligndecl.getAlignment(&this); 689 else 690 return STRUCTALIGN_DEFAULT; 691 } 692 693 /********************************** 694 * Checks whether the current scope (or any of its parents) is deprecated. 695 * 696 * Returns: `true` if this or any parent scope is deprecated, `false` otherwise` 697 */ 698 extern(C++) bool isDeprecated() const 699 { 700 for (const(Dsymbol)* sp = &(this.parent); *sp; sp = &(sp.parent)) 701 { 702 if (sp.isDeprecated()) 703 return true; 704 } 705 for (const(Scope)* sc2 = &this; sc2; sc2 = sc2.enclosing) 706 { 707 if (sc2.scopesym && sc2.scopesym.isDeprecated()) 708 return true; 709 710 // If inside a StorageClassDeclaration that is deprecated 711 if (sc2.stc & STC.deprecated_) 712 return true; 713 } 714 if (_module.md && _module.md.isdeprecated) 715 { 716 return true; 717 } 718 return false; 719 } 720 }