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-2023 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 https://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.astenums;
21 import dmd.attrib;
22 import dmd.ctorflow;
23 import dmd.dclass;
24 import dmd.declaration;
25 import dmd.dmodule;
26 import dmd.doc;
27 import dmd.dsymbol;
28 import dmd.dsymbolsem;
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.location;
37 import dmd.common.outbuffer;
38 import dmd.root.rmem;
39 import dmd.root.speller;
40 import dmd.statement;
41 import dmd.target;
42 import dmd.tokens;
43 
44 //version=LOGSEARCH;
45 
46 
47 // List of flags that can be applied to this `Scope`
48 enum SCOPE
49 {
50     ctor          = 0x0001,   /// constructor type
51     noaccesscheck = 0x0002,   /// don't do access checks
52     condition     = 0x0004,   /// inside static if/assert condition
53     debug_        = 0x0008,   /// inside debug conditional
54     constraint    = 0x0010,   /// inside template constraint
55     invariant_    = 0x0020,   /// inside invariant code
56     require       = 0x0040,   /// inside in contract code
57     ensure        = 0x0060,   /// inside out contract code
58     contract      = 0x0060,   /// [mask] we're inside contract code
59     ctfe          = 0x0080,   /// inside a ctfe-only expression
60     compile       = 0x0100,   /// inside __traits(compile)
61     ignoresymbolvisibility    = 0x0200,   /// ignore symbol visibility
62                                           /// https://issues.dlang.org/show_bug.cgi?id=15907
63     Cfile         = 0x0800,   /// C semantics apply
64     free          = 0x8000,   /// is on free list
65 
66     fullinst      = 0x10000,  /// fully instantiate templates
67     ctfeBlock     = 0x20000,  /// inside a `if (__ctfe)` block
68 }
69 
70 /// Flags that are carried along with a scope push()
71 private enum PersistentFlags =
72     SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint |
73     SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility |
74     SCOPE.Cfile | SCOPE.ctfeBlock;
75 
76 extern (C++) struct Scope
77 {
78     Scope* enclosing;               /// enclosing Scope
79 
80     Module _module;                 /// Root module
81     ScopeDsymbol scopesym;          /// current symbol
82     FuncDeclaration func;           /// function we are in
83     VarDeclaration varDecl;         /// variable we are in during semantic2
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         if (_module.filetype == FileType.c)
176             sc.flags |= SCOPE.Cfile;
177         // Create the module scope underneath the global scope
178         sc = sc.push(_module);
179         sc.parent = _module;
180         return sc;
181     }
182 
183     extern (D) Scope* copy()
184     {
185         Scope* sc = Scope.alloc();
186         *sc = this;
187         /* https://issues.dlang.org/show_bug.cgi?id=11777
188          * The copied scope should not inherit fieldinit.
189          */
190         sc.ctorflow.fieldinit = null;
191         return sc;
192     }
193 
194     extern (D) Scope* push()
195     {
196         Scope* s = copy();
197         //printf("Scope::push(this = %p) new = %p\n", this, s);
198         assert(!(flags & SCOPE.free));
199         s.scopesym = null;
200         s.enclosing = &this;
201         debug
202         {
203             if (enclosing)
204                 assert(!(enclosing.flags & SCOPE.free));
205             if (s == enclosing)
206             {
207                 printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing);
208             }
209             assert(s != enclosing);
210         }
211         s.slabel = null;
212         s.nofree = false;
213         s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup;
214         s.flags = (flags & PersistentFlags);
215         s.lastdc = null;
216         assert(&this != s);
217         return s;
218     }
219 
220     extern (D) Scope* push(ScopeDsymbol ss)
221     {
222         //printf("Scope::push(%s)\n", ss.toChars());
223         Scope* s = push();
224         s.scopesym = ss;
225         return s;
226     }
227 
228     extern (D) Scope* pop()
229     {
230         //printf("Scope::pop() %p nofree = %d\n", this, nofree);
231         if (enclosing)
232             enclosing.ctorflow.OR(ctorflow);
233         ctorflow.freeFieldinit();
234 
235         Scope* enc = enclosing;
236         if (!nofree)
237         {
238             if (mem.isGCEnabled)
239                 this = this.init;
240             enclosing = freelist;
241             freelist = &this;
242             flags |= SCOPE.free;
243         }
244         return enc;
245     }
246 
247     /*************************
248      * Similar to pop(), but the results in `this` are not folded
249      * into `enclosing`.
250      */
251     extern (D) void detach()
252     {
253         ctorflow.freeFieldinit();
254         enclosing = null;
255         pop();
256     }
257 
258     extern (D) Scope* startCTFE()
259     {
260         Scope* sc = this.push();
261         sc.flags = this.flags | SCOPE.ctfe;
262         version (none)
263         {
264             /* TODO: Currently this is not possible, because we need to
265              * unspeculative some types and symbols if they are necessary for the
266              * final executable. Consider:
267              *
268              * struct S(T) {
269              *   string toString() const { return "instantiated"; }
270              * }
271              * enum x = S!int();
272              * void main() {
273              *   // To call x.toString in runtime, compiler should unspeculative S!int.
274              *   assert(x.toString() == "instantiated");
275              * }
276              *
277              * This results in an undefined reference to `RTInfoImpl`:
278              *  class C {  int a,b,c;   int* p,q; }
279              *  void test() {    C c = new C(); }
280              */
281             // If a template is instantiated from CT evaluated expression,
282             // compiler can elide its code generation.
283             sc.tinst = null;
284             sc.minst = null;
285         }
286         return sc;
287     }
288 
289     extern (D) Scope* endCTFE()
290     {
291         assert(flags & SCOPE.ctfe);
292         return pop();
293     }
294 
295 
296     /*******************************
297      * Merge results of `ctorflow` into `this`.
298      * Params:
299      *   loc = for error messages
300      *   ctorflow = flow results to merge in
301      */
302     extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow)
303     {
304         if (!mergeCallSuper(this.ctorflow.callSuper, ctorflow.callSuper))
305             error(loc, "one path skips constructor");
306 
307         const fies = ctorflow.fieldinit;
308         if (this.ctorflow.fieldinit.length && fies.length)
309         {
310             FuncDeclaration f = func;
311             if (fes)
312                 f = fes.func;
313             auto ad = f.isMemberDecl();
314             assert(ad);
315             foreach (i, v; ad.fields)
316             {
317                 bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
318                 auto fieldInit = &this.ctorflow.fieldinit[i];
319                 const fiesCurrent = fies[i];
320                 if (fieldInit.loc is Loc.init)
321                     fieldInit.loc = fiesCurrent.loc;
322                 if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit)
323                 {
324                     error(loc, "one path skips field `%s`", v.toChars());
325                 }
326             }
327         }
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                 else if (sc.flags & SCOPE.Cfile && sc.scopesym.isStructDeclaration())
463                     continue;                                // C doesn't have struct scope
464 
465                 if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
466                 {
467                     if (flags & TagNameSpace)
468                     {
469                         // ImportC: if symbol is not a tag, look for it in tag table
470                         if (!s.isScopeDsymbol())
471                         {
472                             auto ps = cast(void*)s in sc._module.tagSymTab;
473                             if (!ps)
474                                 goto NotFound;
475                             s = *ps;
476                         }
477                     }
478                     if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
479                         ident == Id.length && sc.scopesym.isArrayScopeSymbol() &&
480                         sc.enclosing && sc.enclosing.search(loc, ident, null, flags))
481                     {
482                         warning(s.loc, "array `length` hides other `length` name in outer scope");
483                     }
484                     //printMsg("\tfound local", s);
485                     if (pscopesym)
486                         *pscopesym = sc.scopesym;
487                     return s;
488                 }
489 
490             NotFound:
491                 if (global.params.fixAliasThis)
492                 {
493                     Expression exp = new ThisExp(loc);
494                     Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp);
495                     if (aliasSym)
496                     {
497                         //printf("found aliassym: %s\n", aliasSym.toChars());
498                         if (pscopesym)
499                             *pscopesym = new ExpressionDsymbol(exp);
500                         return aliasSym;
501                     }
502                 }
503 
504                 // Stop when we hit a module, but keep going if that is not just under the global scope
505                 if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing))
506                     break;
507             }
508             return null;
509         }
510 
511         if (this.flags & SCOPE.ignoresymbolvisibility)
512             flags |= IgnoreSymbolVisibility;
513 
514         // First look in local scopes
515         Dsymbol s = searchScopes(flags | SearchLocalsOnly);
516         version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
517         if (!s)
518         {
519             // Second look in imported modules
520             s = searchScopes(flags | SearchImportsOnly);
521             version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s);
522         }
523         return s;
524     }
525 
526     extern (D) Dsymbol search_correct(Identifier ident)
527     {
528         if (global.gag)
529             return null; // don't do it for speculative compiles; too time consuming
530 
531         /************************************************
532          * Given the failed search attempt, try to find
533          * one with a close spelling.
534          * Params:
535          *      seed = identifier to search for
536          *      cost = set to the cost, which rises with each outer scope
537          * Returns:
538          *      Dsymbol if found, null if not
539          */
540         extern (D) Dsymbol scope_search_fp(const(char)[] seed, out int cost)
541         {
542             //printf("scope_search_fp('%s')\n", seed);
543             /* If not in the lexer's string table, it certainly isn't in the symbol table.
544              * Doing this first is a lot faster.
545              */
546             if (!seed.length)
547                 return null;
548             Identifier id = Identifier.lookup(seed);
549             if (!id)
550                 return null;
551             Scope* sc = &this;
552             Module.clearCache();
553             Dsymbol scopesym = null;
554             Dsymbol s = sc.search(Loc.initial, id, &scopesym, IgnoreErrors);
555             if (!s)
556                 return null;
557 
558             // Do not show `@disable`d declarations
559             if (auto decl = s.isDeclaration())
560                 if (decl.storage_class & STC.disable)
561                     return null;
562             // Or `deprecated` ones if we're not in a deprecated scope
563             if (s.isDeprecated() && !sc.isDeprecated())
564                 return null;
565 
566             for (cost = 0; sc; sc = sc.enclosing, ++cost)
567                 if (sc.scopesym == scopesym)
568                     break;
569             if (scopesym != s.parent)
570             {
571                 ++cost; // got to the symbol through an import
572                 if (s.visible().kind == Visibility.Kind.private_)
573                     return null;
574             }
575             return s;
576         }
577 
578         Dsymbol scopesym = null;
579         // search for exact name first
580         if (auto s = search(Loc.initial, ident, &scopesym, IgnoreErrors))
581             return s;
582         return speller!scope_search_fp(ident.toString());
583     }
584 
585     /************************************
586      * Maybe `ident` was a C or C++ name. Check for that,
587      * and suggest the D equivalent.
588      * Params:
589      *  ident = unknown identifier
590      * Returns:
591      *  D identifier string if found, null if not
592      */
593     extern (D) static const(char)* search_correct_C(Identifier ident)
594     {
595         import dmd.astenums : Twchar;
596         TOK tok;
597         if (ident == Id.NULL)
598             tok = TOK.null_;
599         else if (ident == Id.TRUE)
600             tok = TOK.true_;
601         else if (ident == Id.FALSE)
602             tok = TOK.false_;
603         else if (ident == Id.unsigned)
604             tok = TOK.uns32;
605         else if (ident == Id.wchar_t)
606             tok = target.c.wchar_tsize == 2 ? TOK.wchar_ : TOK.dchar_;
607         else
608             return null;
609         return Token.toChars(tok);
610     }
611 
612     /***************************
613      * Find the innermost scope with a symbol table.
614      * Returns:
615      *  innermost scope, null if none
616      */
617     extern (D) Scope* inner() return
618     {
619         for (Scope* sc = &this; sc; sc = sc.enclosing)
620         {
621             if (sc.scopesym)
622                 return sc;
623         }
624         return null;
625     }
626 
627     /******************************
628      * Add symbol s to innermost symbol table.
629      * Params:
630      *  s = symbol to insert
631      * Returns:
632      *  null if already in table, `s` if not
633      */
634     extern (D) Dsymbol insert(Dsymbol s)
635     {
636         //printf("insert() %s\n", s.toChars());
637         if (VarDeclaration vd = s.isVarDeclaration())
638         {
639             if (lastVar)
640                 vd.lastVar = lastVar;
641             lastVar = vd;
642         }
643         else if (WithScopeSymbol ss = s.isWithScopeSymbol())
644         {
645             if (VarDeclaration vd = ss.withstate.wthis)
646             {
647                 if (lastVar)
648                     vd.lastVar = lastVar;
649                 lastVar = vd;
650             }
651             return null;
652         }
653 
654         auto scopesym = inner().scopesym;
655         //printf("\t\tscopesym = %p\n", scopesym);
656         if (!scopesym.symtab)
657             scopesym.symtab = new DsymbolTable();
658         if (!(flags & SCOPE.Cfile))
659             return scopesym.symtabInsert(s);
660 
661         // ImportC insert
662         if (!scopesym.symtabInsert(s)) // if already in table
663         {
664             Dsymbol s2 = scopesym.symtabLookup(s, s.ident); // s2 is existing entry
665             return handleTagSymbols(this, s, s2, scopesym);
666         }
667         return s; // inserted
668     }
669 
670     /********************************************
671      * Search enclosing scopes for ScopeDsymbol.
672      */
673     extern (D) ScopeDsymbol getScopesym()
674     {
675         for (Scope* sc = &this; sc; sc = sc.enclosing)
676         {
677             if (sc.scopesym)
678                 return sc.scopesym;
679         }
680         return null; // not found
681     }
682 
683     /********************************************
684      * Search enclosing scopes for ClassDeclaration.
685      */
686     extern (D) ClassDeclaration getClassScope()
687     {
688         for (Scope* sc = &this; sc; sc = sc.enclosing)
689         {
690             if (!sc.scopesym)
691                 continue;
692             if (ClassDeclaration cd = sc.scopesym.isClassDeclaration())
693                 return cd;
694         }
695         return null;
696     }
697 
698     /********************************************
699      * Search enclosing scopes for ClassDeclaration or StructDeclaration.
700      */
701     extern (D) AggregateDeclaration getStructClassScope()
702     {
703         for (Scope* sc = &this; sc; sc = sc.enclosing)
704         {
705             if (!sc.scopesym)
706                 continue;
707             if (AggregateDeclaration ad = sc.scopesym.isClassDeclaration())
708                 return ad;
709             if (AggregateDeclaration ad = sc.scopesym.isStructDeclaration())
710                 return ad;
711         }
712         return null;
713     }
714 
715     /********************************************
716      * Find the lexically enclosing function (if any).
717      *
718      * This function skips through generated FuncDeclarations,
719      * e.g. rewritten foreach bodies.
720      *
721      * Returns: the function or null
722      */
723     extern (D) inout(FuncDeclaration) getEnclosingFunction() inout
724     {
725         if (!this.func)
726             return null;
727 
728         auto fd = cast(FuncDeclaration) this.func;
729 
730         // Look through foreach bodies rewritten as delegates
731         while (fd.fes)
732         {
733             assert(fd.fes.func);
734             fd = fd.fes.func;
735         }
736 
737         return cast(inout(FuncDeclaration)) fd;
738     }
739 
740     /*******************************************
741      * For TemplateDeclarations, we need to remember the Scope
742      * where it was declared. So mark the Scope as not
743      * to be free'd.
744      */
745     extern (D) void setNoFree()
746     {
747         //int i = 0;
748         //printf("Scope::setNoFree(this = %p)\n", this);
749         for (Scope* sc = &this; sc; sc = sc.enclosing)
750         {
751             //printf("\tsc = %p\n", sc);
752             sc.nofree = true;
753             assert(!(flags & SCOPE.free));
754             //assert(sc != sc.enclosing);
755             //assert(!sc.enclosing || sc != sc.enclosing.enclosing);
756             //if (++i == 10)
757             //    assert(0);
758         }
759     }
760     /******************************
761      */
762     extern (D) structalign_t alignment()
763     {
764         if (aligndecl)
765         {
766             auto ad = aligndecl.getAlignment(&this);
767             return ad.salign;
768         }
769         else
770         {
771             structalign_t sa;
772             sa.setDefault();
773             return sa;
774         }
775     }
776     @safe @nogc pure nothrow const:
777     /**********************************
778     * Checks whether the current scope (or any of its parents) is deprecated.
779     *
780     * Returns: `true` if this or any parent scope is deprecated, `false` otherwise`
781     */
782     extern (D) bool isDeprecated()
783     {
784         for (const(Dsymbol)* sp = &(this.parent); *sp; sp = &(sp.parent))
785         {
786             if (sp.isDeprecated())
787                 return true;
788         }
789         for (const(Scope)* sc2 = &this; sc2; sc2 = sc2.enclosing)
790         {
791             if (sc2.scopesym && sc2.scopesym.isDeprecated())
792                 return true;
793 
794             // If inside a StorageClassDeclaration that is deprecated
795             if (sc2.stc & STC.deprecated_)
796                 return true;
797         }
798         if (_module.md && _module.md.isdeprecated)
799         {
800             return true;
801         }
802         return false;
803     }
804     /**
805      * dmd relies on mutation of state during semantic analysis, however
806      * sometimes semantic is being performed in a speculative context that should
807      * not have any visible effect on the rest of the compilation: for example when compiling
808      * a typeof() or __traits(compiles).
809      *
810      * Returns: `true` if this `Scope` is known to be from one of these speculative contexts
811      */
812     extern (D) bool isFromSpeculativeSemanticContext() scope
813     {
814         return this.intypeof || this.flags & SCOPE.compile;
815     }
816 
817 
818     /**
819      * Returns: true if the code needs to go all the way through to code generation.
820      * This implies things like needing lowering to simpler forms.
821      */
822     extern (D) bool needsCodegen()
823     {
824         return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0;
825     }
826 }