1 /**
2  * The base class for a D symbol, which can be a module, variable, function, enum, etc.
3  *
4  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dsymbol.d, _dsymbol.d)
8  * Documentation:  https://dlang.org/phobos/dmd_dsymbol.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbol.d
10  */
11 
12 module dmd.dsymbol;
13 
14 import core.stdc.stdarg;
15 import core.stdc.stdio;
16 import core.stdc.string;
17 import core.stdc.stdlib;
18 
19 import dmd.aggregate;
20 import dmd.aliasthis;
21 import dmd.arraytypes;
22 import dmd.attrib;
23 import dmd.ast_node;
24 import dmd.gluelayer;
25 import dmd.dclass;
26 import dmd.declaration;
27 import dmd.denum;
28 import dmd.dimport;
29 import dmd.dmodule;
30 import dmd.dscope;
31 import dmd.dstruct;
32 import dmd.dsymbolsem;
33 import dmd.dtemplate;
34 import dmd.errors;
35 import dmd.expression;
36 import dmd.expressionsem;
37 import dmd.func;
38 import dmd.globals;
39 import dmd.id;
40 import dmd.identifier;
41 import dmd.init;
42 import dmd.lexer;
43 import dmd.mtype;
44 import dmd.nspace;
45 import dmd.opover;
46 import dmd.root.aav;
47 import dmd.root.rmem;
48 import dmd.root.rootobject;
49 import dmd.root.speller;
50 import dmd.root.string;
51 import dmd.statement;
52 import dmd.tokens;
53 import dmd.visitor;
54 
55 /***************************************
56  * Calls dg(Dsymbol *sym) for each Dsymbol.
57  * If dg returns !=0, stops and returns that value else returns 0.
58  * Params:
59  *    symbols = Dsymbols
60  *    dg = delegate to call for each Dsymbol
61  * Returns:
62  *    last value returned by dg()
63  */
64 int foreachDsymbol(Dsymbols* symbols, scope int delegate(Dsymbol) dg)
65 {
66     assert(dg);
67     if (symbols)
68     {
69         /* Do not use foreach, as the size of the array may expand during iteration
70          */
71         for (size_t i = 0; i < symbols.dim; ++i)
72         {
73             Dsymbol s = (*symbols)[i];
74             const result = dg(s);
75             if (result)
76                 return result;
77         }
78     }
79     return 0;
80 }
81 
82 /***************************************
83  * Calls dg(Dsymbol *sym) for each Dsymbol.
84  * Params:
85  *    symbols = Dsymbols
86  *    dg = delegate to call for each Dsymbol
87  */
88 void foreachDsymbol(Dsymbols* symbols, scope void delegate(Dsymbol) dg)
89 {
90     assert(dg);
91     if (symbols)
92     {
93         /* Do not use foreach, as the size of the array may expand during iteration
94          */
95         for (size_t i = 0; i < symbols.dim; ++i)
96         {
97             Dsymbol s = (*symbols)[i];
98             dg(s);
99         }
100     }
101 }
102 
103 
104 struct Ungag
105 {
106     uint oldgag;
107 
108     extern (D) this(uint old)
109     {
110         this.oldgag = old;
111     }
112 
113     extern (C++) ~this()
114     {
115         global.gag = oldgag;
116     }
117 }
118 
119 struct Prot
120 {
121     ///
122     enum Kind : int
123     {
124         undefined,
125         none,           // no access
126         private_,
127         package_,
128         protected_,
129         public_,
130         export_,
131     }
132 
133     Kind kind;
134     Package pkg;
135 
136     extern (D) this(Prot.Kind kind) pure nothrow @nogc @safe
137     {
138         this.kind = kind;
139     }
140 
141     extern (C++):
142 
143     /**
144      * Checks if `this` is superset of `other` restrictions.
145      * For example, "protected" is more restrictive than "public".
146      */
147     bool isMoreRestrictiveThan(const Prot other) const
148     {
149         return this.kind < other.kind;
150     }
151 
152     /**
153      * Checks if `this` is absolutely identical protection attribute to `other`
154      */
155     bool opEquals(ref const Prot other) const
156     {
157         if (this.kind == other.kind)
158         {
159             if (this.kind == Prot.Kind.package_)
160                 return this.pkg == other.pkg;
161             return true;
162         }
163         return false;
164     }
165 
166     /**
167      * Checks if parent defines different access restrictions than this one.
168      *
169      * Params:
170      *  parent = protection attribute for scope that hosts this one
171      *
172      * Returns:
173      *  'true' if parent is already more restrictive than this one and thus
174      *  no differentiation is needed.
175      */
176     bool isSubsetOf(ref const Prot parent) const
177     {
178         if (this.kind != parent.kind)
179             return false;
180         if (this.kind == Prot.Kind.package_)
181         {
182             if (!this.pkg)
183                 return true;
184             if (!parent.pkg)
185                 return false;
186             if (parent.pkg.isAncestorPackageOf(this.pkg))
187                 return true;
188         }
189         return true;
190     }
191 }
192 
193 enum PASS : int
194 {
195     init,           // initial state
196     semantic,       // semantic() started
197     semanticdone,   // semantic() done
198     semantic2,      // semantic2() started
199     semantic2done,  // semantic2() done
200     semantic3,      // semantic3() started
201     semantic3done,  // semantic3() done
202     inline,         // inline started
203     inlinedone,     // inline done
204     obj,            // toObjFile() run
205 }
206 
207 // Search options
208 enum : int
209 {
210     IgnoreNone              = 0x00, // default
211     IgnorePrivateImports    = 0x01, // don't search private imports
212     IgnoreErrors            = 0x02, // don't give error messages
213     IgnoreAmbiguous         = 0x04, // return NULL if ambiguous
214     SearchLocalsOnly        = 0x08, // only look at locals (don't search imports)
215     SearchImportsOnly       = 0x10, // only look in imports
216     SearchUnqualifiedModule = 0x20, // the module scope search is unqualified,
217                                     // meaning don't search imports in that scope,
218                                     // because qualified module searches search
219                                     // their imports
220     IgnoreSymbolVisibility  = 0x80, // also find private and package protected symbols
221 }
222 
223 /***********************************************************
224  */
225 extern (C++) class Dsymbol : ASTNode
226 {
227     Identifier ident;
228     Dsymbol parent;
229     /// C++ namespace this symbol belongs to
230     CPPNamespaceDeclaration cppnamespace;
231     Symbol* csym;           // symbol for code generator
232     Symbol* isym;           // import version of csym
233     const(char)* comment;   // documentation comment for this Dsymbol
234     const Loc loc;          // where defined
235     Scope* _scope;          // !=null means context to use for semantic()
236     const(char)* prettystring;  // cached value of toPrettyChars()
237     bool errors;            // this symbol failed to pass semantic()
238     PASS semanticRun = PASS.init;
239 
240     DeprecatedDeclaration depdecl;           // customized deprecation message
241     UserAttributeDeclaration userAttribDecl;    // user defined attributes
242 
243     // !=null means there's a ddoc unittest associated with this symbol
244     // (only use this with ddoc)
245     UnitTestDeclaration ddocUnittest;
246 
247     final extern (D) this()
248     {
249         //printf("Dsymbol::Dsymbol(%p)\n", this);
250         loc = Loc(null, 0, 0);
251     }
252 
253     final extern (D) this(Identifier ident)
254     {
255         //printf("Dsymbol::Dsymbol(%p, ident)\n", this);
256         this.loc = Loc(null, 0, 0);
257         this.ident = ident;
258     }
259 
260     final extern (D) this(const ref Loc loc, Identifier ident)
261     {
262         //printf("Dsymbol::Dsymbol(%p, ident)\n", this);
263         this.loc = loc;
264         this.ident = ident;
265     }
266 
267     static Dsymbol create(Identifier ident)
268     {
269         return new Dsymbol(ident);
270     }
271 
272     override const(char)* toChars() const
273     {
274         return ident ? ident.toChars() : "__anonymous";
275     }
276 
277     // helper to print fully qualified (template) arguments
278     const(char)* toPrettyCharsHelper()
279     {
280         return toChars();
281     }
282 
283     final const(Loc) getLoc()
284     {
285         if (!loc.isValid()) // avoid bug 5861.
286             if (const m = getModule())
287                 return Loc(m.srcfile.toChars(), 0, 0);
288         return loc;
289     }
290 
291     final const(char)* locToChars()
292     {
293         return getLoc().toChars();
294     }
295 
296     override bool equals(const RootObject o) const
297     {
298         if (this == o)
299             return true;
300         if (o.dyncast() != DYNCAST.dsymbol)
301             return false;
302         auto s = cast(Dsymbol)o;
303         // Overload sets don't have an ident
304         if (s && ident && s.ident && ident.equals(s.ident))
305             return true;
306         return false;
307     }
308 
309     bool isAnonymous()
310     {
311         return ident is null;
312     }
313 
314     extern(D) private const(char)[] prettyFormatHelper()
315     {
316         const cstr = toPrettyChars();
317         return '`' ~ cstr.toDString() ~ "`\0";
318     }
319 
320     final void error(const ref Loc loc, const(char)* format, ...)
321     {
322         va_list ap;
323         va_start(ap, format);
324         .verror(loc, format, ap, kind(), prettyFormatHelper().ptr);
325         va_end(ap);
326     }
327 
328     final void error(const(char)* format, ...)
329     {
330         va_list ap;
331         va_start(ap, format);
332         const loc = getLoc();
333         .verror(loc, format, ap, kind(), prettyFormatHelper().ptr);
334         va_end(ap);
335     }
336 
337     final void deprecation(const ref Loc loc, const(char)* format, ...)
338     {
339         va_list ap;
340         va_start(ap, format);
341         .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr);
342         va_end(ap);
343     }
344 
345     final void deprecation(const(char)* format, ...)
346     {
347         va_list ap;
348         va_start(ap, format);
349         const loc = getLoc();
350         .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr);
351         va_end(ap);
352     }
353 
354     final bool checkDeprecated(const ref Loc loc, Scope* sc)
355     {
356         if (global.params.useDeprecated == DiagnosticReporting.off)
357             return false;
358         if (!this.isDeprecated())
359             return false;
360         // Don't complain if we're inside a deprecated symbol's scope
361         if (sc.isDeprecated())
362             return false;
363 
364         const(char)* message = null;
365         for (Dsymbol p = this; p; p = p.parent)
366         {
367             message = p.depdecl ? p.depdecl.getMessage() : null;
368             if (message)
369                 break;
370         }
371         if (message)
372             deprecation(loc, "is deprecated - %s", message);
373         else
374             deprecation(loc, "is deprecated");
375 
376         return true;
377     }
378 
379     /**********************************
380      * Determine which Module a Dsymbol is in.
381      */
382     final Module getModule()
383     {
384         //printf("Dsymbol::getModule()\n");
385         if (TemplateInstance ti = isInstantiated())
386             return ti.tempdecl.getModule();
387         Dsymbol s = this;
388         while (s)
389         {
390             //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars());
391             Module m = s.isModule();
392             if (m)
393                 return m;
394             s = s.parent;
395         }
396         return null;
397     }
398 
399     /**********************************
400      * Determine which Module a Dsymbol is in, as far as access rights go.
401      */
402     final Module getAccessModule()
403     {
404         //printf("Dsymbol::getAccessModule()\n");
405         if (TemplateInstance ti = isInstantiated())
406             return ti.tempdecl.getAccessModule();
407         Dsymbol s = this;
408         while (s)
409         {
410             //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars());
411             Module m = s.isModule();
412             if (m)
413                 return m;
414             TemplateInstance ti = s.isTemplateInstance();
415             if (ti && ti.enclosing)
416             {
417                 /* Because of local template instantiation, the parent isn't where the access
418                  * rights come from - it's the template declaration
419                  */
420                 s = ti.tempdecl;
421             }
422             else
423                 s = s.parent;
424         }
425         return null;
426     }
427 
428     /**
429      * `pastMixin` returns the enclosing symbol if this is a template mixin.
430      *
431      * `pastMixinAndNspace` does likewise, additionally skipping over Nspaces that
432      * are mangleOnly.
433      *
434      * See also `parent`, `toParent` and `toParent2`.
435      */
436     final inout(Dsymbol) pastMixin() inout
437     {
438         //printf("Dsymbol::pastMixin() %s\n", toChars());
439         if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol())
440             return this;
441         if (!parent)
442             return null;
443         return parent.pastMixin();
444     }
445 
446     /**********************************
447      * `parent` field returns a lexically enclosing scope symbol this is a member of.
448      *
449      * `toParent()` returns a logically enclosing scope symbol this is a member of.
450      * It skips over TemplateMixin's.
451      *
452      * `toParent2()` returns an enclosing scope symbol this is living at runtime.
453      * It skips over both TemplateInstance's and TemplateMixin's.
454      * It's used when looking for the 'this' pointer of the enclosing function/class.
455      *
456      * `toParentDecl()` similar to `toParent2()` but always follows the template declaration scope
457      * instead of the instantiation scope.
458      *
459      * `toParentLocal()` similar to `toParentDecl()` but follows the instantiation scope
460      * if a template declaration is non-local i.e. global or static.
461      *
462      * Examples:
463      *  module mod;
464      *  template Foo(alias a) { mixin Bar!(); }
465      *  mixin template Bar() {
466      *    public {  // ProtDeclaration
467      *      void baz() { a = 2; }
468      *    }
469      *  }
470      *  void test() {
471      *    int v = 1;
472      *    alias foo = Foo!(v);
473      *    foo.baz();
474      *    assert(v == 2);
475      *  }
476      *
477      *  // s == FuncDeclaration('mod.test.Foo!().Bar!().baz()')
478      *  // s.parent == TemplateMixin('mod.test.Foo!().Bar!()')
479      *  // s.toParent() == TemplateInstance('mod.test.Foo!()')
480      *  // s.toParent2() == FuncDeclaration('mod.test')
481      *  // s.toParentDecl() == Module('mod')
482      *  // s.toParentLocal() == FuncDeclaration('mod.test')
483      */
484     final inout(Dsymbol) toParent() inout
485     {
486         return parent ? parent.pastMixin() : null;
487     }
488 
489     /// ditto
490     final inout(Dsymbol) toParent2() inout
491     {
492         if (!parent || !parent.isTemplateInstance && !parent.isForwardingAttribDeclaration() && !parent.isForwardingScopeDsymbol())
493             return parent;
494         return parent.toParent2;
495     }
496 
497     /// ditto
498     final inout(Dsymbol) toParentDecl() inout
499     {
500         return toParentDeclImpl(false);
501     }
502 
503     /// ditto
504     final inout(Dsymbol) toParentLocal() inout
505     {
506         return toParentDeclImpl(true);
507     }
508 
509     private inout(Dsymbol) toParentDeclImpl(bool localOnly) inout
510     {
511         auto p = toParent();
512         if (!p || !p.isTemplateInstance())
513             return p;
514         auto ti = p.isTemplateInstance();
515         if (ti.tempdecl && (!localOnly || !(cast(TemplateDeclaration)ti.tempdecl).isstatic))
516             return ti.tempdecl.toParentDeclImpl(localOnly);
517         return parent.toParentDeclImpl(localOnly);
518     }
519 
520     /**
521      * Returns the declaration scope scope of `this` unless any of the symbols
522      * `p1` or `p2` resides in its enclosing instantiation scope then the
523      * latter is returned.
524      */
525     final Dsymbol toParentP(Dsymbol p1, Dsymbol p2 = null)
526     {
527         return followInstantiationContext(p1, p2) ? toParent2() : toParentLocal();
528     }
529 
530     final inout(TemplateInstance) isInstantiated() inout
531     {
532         if (!parent)
533             return null;
534         auto ti = parent.isTemplateInstance();
535         if (ti && !ti.isTemplateMixin())
536             return ti;
537         return parent.isInstantiated();
538     }
539 
540     /***
541      * Returns true if any of the symbols `p1` or `p2` resides in the enclosing
542      * instantiation scope of `this`.
543      */
544     final bool followInstantiationContext(Dsymbol p1, Dsymbol p2 = null)
545     {
546         static bool has2This(Dsymbol s)
547         {
548             if (auto f = s.isFuncDeclaration())
549                 return f.isThis2;
550             if (auto ad = s.isAggregateDeclaration())
551                 return ad.vthis2 !is null;
552             return false;
553         }
554 
555         if (has2This(this))
556         {
557             assert(p1);
558             auto outer = toParent();
559             while (outer)
560             {
561                 auto ti = outer.isTemplateInstance();
562                 if (!ti)
563                     break;
564                 foreach (oarg; *ti.tiargs)
565                 {
566                     auto sa = getDsymbol(oarg);
567                     if (!sa)
568                         continue;
569                     sa = sa.toAlias().toParent2();
570                     if (!sa)
571                         continue;
572                     if (sa == p1)
573                         return true;
574                     else if (p2 && sa == p2)
575                         return true;
576                 }
577                 outer = ti.tempdecl.toParent();
578             }
579             return false;
580         }
581         return false;
582     }
583 
584     // Check if this function is a member of a template which has only been
585     // instantiated speculatively, eg from inside is(typeof()).
586     // Return the speculative template instance it is part of,
587     // or NULL if not speculative.
588     final inout(TemplateInstance) isSpeculative() inout
589     {
590         if (!parent)
591             return null;
592         auto ti = parent.isTemplateInstance();
593         if (ti && ti.gagged)
594             return ti;
595         if (!parent.toParent())
596             return null;
597         return parent.isSpeculative();
598     }
599 
600     final Ungag ungagSpeculative() const
601     {
602         uint oldgag = global.gag;
603         if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration())
604             global.gag = 0;
605         return Ungag(oldgag);
606     }
607 
608     // kludge for template.isSymbol()
609     override final DYNCAST dyncast() const
610     {
611         return DYNCAST.dsymbol;
612     }
613 
614     /*************************************
615      * Do syntax copy of an array of Dsymbol's.
616      */
617     extern (D) static Dsymbols* arraySyntaxCopy(Dsymbols* a)
618     {
619         Dsymbols* b = null;
620         if (a)
621         {
622             b = a.copy();
623             for (size_t i = 0; i < b.dim; i++)
624             {
625                 (*b)[i] = (*b)[i].syntaxCopy(null);
626             }
627         }
628         return b;
629     }
630 
631     Identifier getIdent()
632     {
633         return ident;
634     }
635 
636     const(char)* toPrettyChars(bool QualifyTypes = false)
637     {
638         if (prettystring && !QualifyTypes)
639             return prettystring;
640 
641         //printf("Dsymbol::toPrettyChars() '%s'\n", toChars());
642         if (!parent)
643         {
644             auto s = toChars();
645             if (!QualifyTypes)
646                 prettystring = s;
647             return s;
648         }
649 
650         // Computer number of components
651         size_t complength = 0;
652         for (Dsymbol p = this; p; p = p.parent)
653             ++complength;
654 
655         // Allocate temporary array comp[]
656         alias T = const(char)[];
657         auto compptr = cast(T*)Mem.check(malloc(complength * T.sizeof));
658         auto comp = compptr[0 .. complength];
659 
660         // Fill in comp[] and compute length of final result
661         size_t length = 0;
662         int i;
663         for (Dsymbol p = this; p; p = p.parent)
664         {
665             const s = QualifyTypes ? p.toPrettyCharsHelper() : p.toChars();
666             const len = strlen(s);
667             comp[i] = s[0 .. len];
668             ++i;
669             length += len + 1;
670         }
671 
672         auto s = cast(char*)mem.xmalloc_noscan(length);
673         auto q = s + length - 1;
674         *q = 0;
675         foreach (j; 0 .. complength)
676         {
677             const t = comp[j].ptr;
678             const len = comp[j].length;
679             q -= len;
680             memcpy(q, t, len);
681             if (q == s)
682                 break;
683             *--q = '.';
684         }
685         free(comp.ptr);
686         if (!QualifyTypes)
687             prettystring = s;
688         return s;
689     }
690 
691     const(char)* kind() const pure nothrow @nogc @safe
692     {
693         return "symbol";
694     }
695 
696     /*********************************
697      * If this symbol is really an alias for another,
698      * return that other.
699      * If needed, semantic() is invoked due to resolve forward reference.
700      */
701     Dsymbol toAlias()
702     {
703         return this;
704     }
705 
706     /*********************************
707      * Resolve recursive tuple expansion in eponymous template.
708      */
709     Dsymbol toAlias2()
710     {
711         return toAlias();
712     }
713 
714     void addMember(Scope* sc, ScopeDsymbol sds)
715     {
716         //printf("Dsymbol::addMember('%s')\n", toChars());
717         //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds.toChars());
718         //printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds.symtab = %p)\n", this, toChars(), sds, sds.symtab);
719         parent = sds;
720         if (isAnonymous()) // no name, so can't add it to symbol table
721             return;
722 
723         if (!sds.symtabInsert(this)) // if name is already defined
724         {
725             if (isAliasDeclaration() && !_scope)
726                 setScope(sc);
727             Dsymbol s2 = sds.symtabLookup(this,ident);
728             if (!s2.overloadInsert(this))
729             {
730                 sds.multiplyDefined(Loc.initial, this, s2);
731                 errors = true;
732             }
733         }
734         if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
735         {
736             if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof)
737             {
738                 error("`.%s` property cannot be redefined", ident.toChars());
739                 errors = true;
740             }
741         }
742     }
743 
744     /*************************************
745      * Set scope for future semantic analysis so we can
746      * deal better with forward references.
747      */
748     void setScope(Scope* sc)
749     {
750         //printf("Dsymbol::setScope() %p %s, %p stc = %llx\n", this, toChars(), sc, sc.stc);
751         if (!sc.nofree)
752             sc.setNoFree(); // may need it even after semantic() finishes
753         _scope = sc;
754         if (sc.depdecl)
755             depdecl = sc.depdecl;
756         if (!userAttribDecl)
757             userAttribDecl = sc.userAttribDecl;
758     }
759 
760     void importAll(Scope* sc)
761     {
762     }
763 
764     /*********************************************
765      * Search for ident as member of s.
766      * Params:
767      *  loc = location to print for error messages
768      *  ident = identifier to search for
769      *  flags = IgnoreXXXX
770      * Returns:
771      *  null if not found
772      */
773     Dsymbol search(const ref Loc loc, Identifier ident, int flags = IgnoreNone)
774     {
775         //printf("Dsymbol::search(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars());
776         return null;
777     }
778 
779     extern (D) final Dsymbol search_correct(Identifier ident)
780     {
781         /***************************************************
782          * Search for symbol with correct spelling.
783          */
784         extern (D) Dsymbol symbol_search_fp(const(char)[] seed, ref int cost)
785         {
786             /* If not in the lexer's string table, it certainly isn't in the symbol table.
787              * Doing this first is a lot faster.
788              */
789             if (!seed.length)
790                 return null;
791             Identifier id = Identifier.lookup(seed);
792             if (!id)
793                 return null;
794             cost = 0;
795             Dsymbol s = this;
796             Module.clearCache();
797             return s.search(Loc.initial, id, IgnoreErrors);
798         }
799 
800         if (global.gag)
801             return null; // don't do it for speculative compiles; too time consuming
802         // search for exact name first
803         if (auto s = search(Loc.initial, ident, IgnoreErrors))
804             return s;
805         return speller!symbol_search_fp(ident.toString());
806     }
807 
808     /***************************************
809      * Search for identifier id as a member of `this`.
810      * `id` may be a template instance.
811      *
812      * Params:
813      *  loc = location to print the error messages
814      *  sc = the scope where the symbol is located
815      *  id = the id of the symbol
816      *  flags = the search flags which can be `SearchLocalsOnly` or `IgnorePrivateImports`
817      *
818      * Returns:
819      *      symbol found, NULL if not
820      */
821     extern (D) final Dsymbol searchX(const ref Loc loc, Scope* sc, RootObject id, int flags)
822     {
823         //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars());
824         Dsymbol s = toAlias();
825         Dsymbol sm;
826         if (Declaration d = s.isDeclaration())
827         {
828             if (d.inuse)
829             {
830                 .error(loc, "circular reference to `%s`", d.toPrettyChars());
831                 return null;
832             }
833         }
834         switch (id.dyncast())
835         {
836         case DYNCAST.identifier:
837             sm = s.search(loc, cast(Identifier)id, flags);
838             break;
839         case DYNCAST.dsymbol:
840             {
841                 // It's a template instance
842                 //printf("\ttemplate instance id\n");
843                 Dsymbol st = cast(Dsymbol)id;
844                 TemplateInstance ti = st.isTemplateInstance();
845                 sm = s.search(loc, ti.name);
846                 if (!sm)
847                 {
848                     sm = s.search_correct(ti.name);
849                     if (sm)
850                         .error(loc, "template identifier `%s` is not a member of %s `%s`, did you mean %s `%s`?", ti.name.toChars(), s.kind(), s.toPrettyChars(), sm.kind(), sm.toChars());
851                     else
852                         .error(loc, "template identifier `%s` is not a member of %s `%s`", ti.name.toChars(), s.kind(), s.toPrettyChars());
853                     return null;
854                 }
855                 sm = sm.toAlias();
856                 TemplateDeclaration td = sm.isTemplateDeclaration();
857                 if (!td)
858                 {
859                     .error(loc, "`%s.%s` is not a template, it is a %s", s.toPrettyChars(), ti.name.toChars(), sm.kind());
860                     return null;
861                 }
862                 ti.tempdecl = td;
863                 if (!ti.semanticRun)
864                     ti.dsymbolSemantic(sc);
865                 sm = ti.toAlias();
866                 break;
867             }
868         case DYNCAST.type:
869         case DYNCAST.expression:
870         default:
871             assert(0);
872         }
873         return sm;
874     }
875 
876     bool overloadInsert(Dsymbol s)
877     {
878         //printf("Dsymbol::overloadInsert('%s')\n", s.toChars());
879         return false;
880     }
881 
882     /*********************************
883      * Returns:
884      *  SIZE_INVALID when the size cannot be determined
885      */
886     d_uns64 size(const ref Loc loc)
887     {
888         error("Dsymbol `%s` has no size", toChars());
889         return SIZE_INVALID;
890     }
891 
892     bool isforwardRef()
893     {
894         return false;
895     }
896 
897     // is a 'this' required to access the member
898     inout(AggregateDeclaration) isThis() inout
899     {
900         return null;
901     }
902 
903     // is Dsymbol exported?
904     bool isExport() const
905     {
906         return false;
907     }
908 
909     // is Dsymbol imported?
910     bool isImportedSymbol() const
911     {
912         return false;
913     }
914 
915     // is Dsymbol deprecated?
916     bool isDeprecated() const
917     {
918         return false;
919     }
920 
921     bool isOverloadable() const
922     {
923         return false;
924     }
925 
926     // is this a LabelDsymbol()?
927     LabelDsymbol isLabel()
928     {
929         return null;
930     }
931 
932     /// Returns an AggregateDeclaration when toParent() is that.
933     final inout(AggregateDeclaration) isMember() inout
934     {
935         //printf("Dsymbol::isMember() %s\n", toChars());
936         auto p = toParent();
937         //printf("parent is %s %s\n", p.kind(), p.toChars());
938         return p ? p.isAggregateDeclaration() : null;
939     }
940 
941     /// Returns an AggregateDeclaration when toParent2() is that.
942     final inout(AggregateDeclaration) isMember2() inout
943     {
944         //printf("Dsymbol::isMember2() '%s'\n", toChars());
945         auto p = toParent2();
946         //printf("parent is %s %s\n", p.kind(), p.toChars());
947         return p ? p.isAggregateDeclaration() : null;
948     }
949 
950     /// Returns an AggregateDeclaration when toParentDecl() is that.
951     final inout(AggregateDeclaration) isMemberDecl() inout
952     {
953         //printf("Dsymbol::isMemberDecl() '%s'\n", toChars());
954         auto p = toParentDecl();
955         //printf("parent is %s %s\n", p.kind(), p.toChars());
956         return p ? p.isAggregateDeclaration() : null;
957     }
958 
959     /// Returns an AggregateDeclaration when toParentLocal() is that.
960     final inout(AggregateDeclaration) isMemberLocal() inout
961     {
962         //printf("Dsymbol::isMemberLocal() '%s'\n", toChars());
963         auto p = toParentLocal();
964         //printf("parent is %s %s\n", p.kind(), p.toChars());
965         return p ? p.isAggregateDeclaration() : null;
966     }
967 
968     // is this a member of a ClassDeclaration?
969     final ClassDeclaration isClassMember()
970     {
971         auto ad = isMember();
972         return ad ? ad.isClassDeclaration() : null;
973     }
974 
975     // is this a type?
976     Type getType()
977     {
978         return null;
979     }
980 
981     // need a 'this' pointer?
982     bool needThis()
983     {
984         return false;
985     }
986 
987     /*************************************
988      */
989     Prot prot() pure nothrow @nogc @safe
990     {
991         return Prot(Prot.Kind.public_);
992     }
993 
994     /**************************************
995      * Copy the syntax.
996      * Used for template instantiations.
997      * If s is NULL, allocate the new object, otherwise fill it in.
998      */
999     Dsymbol syntaxCopy(Dsymbol s)
1000     {
1001         printf("%s %s\n", kind(), toChars());
1002         assert(0);
1003     }
1004 
1005     /**************************************
1006      * Determine if this symbol is only one.
1007      * Returns:
1008      *      false, *ps = NULL: There are 2 or more symbols
1009      *      true,  *ps = NULL: There are zero symbols
1010      *      true,  *ps = symbol: The one and only one symbol
1011      */
1012     bool oneMember(Dsymbol* ps, Identifier ident)
1013     {
1014         //printf("Dsymbol::oneMember()\n");
1015         *ps = this;
1016         return true;
1017     }
1018 
1019     /*****************************************
1020      * Same as Dsymbol::oneMember(), but look at an array of Dsymbols.
1021      */
1022     extern (D) static bool oneMembers(Dsymbols* members, Dsymbol* ps, Identifier ident)
1023     {
1024         //printf("Dsymbol::oneMembers() %d\n", members ? members.dim : 0);
1025         Dsymbol s = null;
1026         if (!members)
1027         {
1028             *ps = null;
1029             return true;
1030         }
1031 
1032         for (size_t i = 0; i < members.dim; i++)
1033         {
1034             Dsymbol sx = (*members)[i];
1035             bool x = sx.oneMember(ps, ident);
1036             //printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps);
1037             if (!x)
1038             {
1039                 //printf("\tfalse 1\n");
1040                 assert(*ps is null);
1041                 return false;
1042             }
1043             if (*ps)
1044             {
1045                 assert(ident);
1046                 if (!(*ps).ident || !(*ps).ident.equals(ident))
1047                     continue;
1048                 if (!s)
1049                     s = *ps;
1050                 else if (s.isOverloadable() && (*ps).isOverloadable())
1051                 {
1052                     // keep head of overload set
1053                     FuncDeclaration f1 = s.isFuncDeclaration();
1054                     FuncDeclaration f2 = (*ps).isFuncDeclaration();
1055                     if (f1 && f2)
1056                     {
1057                         assert(!f1.isFuncAliasDeclaration());
1058                         assert(!f2.isFuncAliasDeclaration());
1059                         for (; f1 != f2; f1 = f1.overnext0)
1060                         {
1061                             if (f1.overnext0 is null)
1062                             {
1063                                 f1.overnext0 = f2;
1064                                 break;
1065                             }
1066                         }
1067                     }
1068                 }
1069                 else // more than one symbol
1070                 {
1071                     *ps = null;
1072                     //printf("\tfalse 2\n");
1073                     return false;
1074                 }
1075             }
1076         }
1077         *ps = s; // s is the one symbol, null if none
1078         //printf("\ttrue\n");
1079         return true;
1080     }
1081 
1082     void setFieldOffset(AggregateDeclaration ad, uint* poffset, bool isunion)
1083     {
1084     }
1085 
1086     /*****************************************
1087      * Is Dsymbol a variable that contains pointers?
1088      */
1089     bool hasPointers()
1090     {
1091         //printf("Dsymbol::hasPointers() %s\n", toChars());
1092         return false;
1093     }
1094 
1095     bool hasStaticCtorOrDtor()
1096     {
1097         //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars());
1098         return false;
1099     }
1100 
1101     void addLocalClass(ClassDeclarations*)
1102     {
1103     }
1104 
1105     void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
1106     {
1107     }
1108 
1109     void checkCtorConstInit()
1110     {
1111     }
1112 
1113     /****************************************
1114      * Add documentation comment to Dsymbol.
1115      * Ignore NULL comments.
1116      */
1117     void addComment(const(char)* comment)
1118     {
1119         //if (comment)
1120         //    printf("adding comment '%s' to symbol %p '%s'\n", comment, this, toChars());
1121         if (!this.comment)
1122             this.comment = comment;
1123         else if (comment && strcmp(cast(char*)comment, cast(char*)this.comment) != 0)
1124         {
1125             // Concatenate the two
1126             this.comment = Lexer.combineComments(this.comment.toDString(), comment.toDString(), true);
1127         }
1128     }
1129 
1130     /****************************************
1131      * Returns true if this symbol is defined in a non-root module without instantiation.
1132      */
1133     final bool inNonRoot()
1134     {
1135         Dsymbol s = parent;
1136         for (; s; s = s.toParent())
1137         {
1138             if (auto ti = s.isTemplateInstance())
1139             {
1140                 return false;
1141             }
1142             if (auto m = s.isModule())
1143             {
1144                 if (!m.isRoot())
1145                     return true;
1146                 break;
1147             }
1148         }
1149         return false;
1150     }
1151 
1152     /************
1153      */
1154     override void accept(Visitor v)
1155     {
1156         v.visit(this);
1157     }
1158 
1159   pure nothrow @safe @nogc:
1160 
1161     // Eliminate need for dynamic_cast
1162     inout(Package)                     isPackage()                     inout { return null; }
1163     inout(Module)                      isModule()                      inout { return null; }
1164     inout(EnumMember)                  isEnumMember()                  inout { return null; }
1165     inout(TemplateDeclaration)         isTemplateDeclaration()         inout { return null; }
1166     inout(TemplateInstance)            isTemplateInstance()            inout { return null; }
1167     inout(TemplateMixin)               isTemplateMixin()               inout { return null; }
1168     inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { return null; }
1169     inout(Nspace)                      isNspace()                      inout { return null; }
1170     inout(Declaration)                 isDeclaration()                 inout { return null; }
1171     inout(StorageClassDeclaration)     isStorageClassDeclaration()     inout { return null; }
1172     inout(ExpressionDsymbol)           isExpressionDsymbol()           inout { return null; }
1173     inout(ThisDeclaration)             isThisDeclaration()             inout { return null; }
1174     inout(TypeInfoDeclaration)         isTypeInfoDeclaration()         inout { return null; }
1175     inout(TupleDeclaration)            isTupleDeclaration()            inout { return null; }
1176     inout(AliasDeclaration)            isAliasDeclaration()            inout { return null; }
1177     inout(AggregateDeclaration)        isAggregateDeclaration()        inout { return null; }
1178     inout(FuncDeclaration)             isFuncDeclaration()             inout { return null; }
1179     inout(FuncAliasDeclaration)        isFuncAliasDeclaration()        inout { return null; }
1180     inout(OverDeclaration)             isOverDeclaration()             inout { return null; }
1181     inout(FuncLiteralDeclaration)      isFuncLiteralDeclaration()      inout { return null; }
1182     inout(CtorDeclaration)             isCtorDeclaration()             inout { return null; }
1183     inout(PostBlitDeclaration)         isPostBlitDeclaration()         inout { return null; }
1184     inout(DtorDeclaration)             isDtorDeclaration()             inout { return null; }
1185     inout(StaticCtorDeclaration)       isStaticCtorDeclaration()       inout { return null; }
1186     inout(StaticDtorDeclaration)       isStaticDtorDeclaration()       inout { return null; }
1187     inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout { return null; }
1188     inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout { return null; }
1189     inout(InvariantDeclaration)        isInvariantDeclaration()        inout { return null; }
1190     inout(UnitTestDeclaration)         isUnitTestDeclaration()         inout { return null; }
1191     inout(NewDeclaration)              isNewDeclaration()              inout { return null; }
1192     inout(VarDeclaration)              isVarDeclaration()              inout { return null; }
1193     inout(ClassDeclaration)            isClassDeclaration()            inout { return null; }
1194     inout(StructDeclaration)           isStructDeclaration()           inout { return null; }
1195     inout(UnionDeclaration)            isUnionDeclaration()            inout { return null; }
1196     inout(InterfaceDeclaration)        isInterfaceDeclaration()        inout { return null; }
1197     inout(ScopeDsymbol)                isScopeDsymbol()                inout { return null; }
1198     inout(ForwardingScopeDsymbol)      isForwardingScopeDsymbol()      inout { return null; }
1199     inout(WithScopeSymbol)             isWithScopeSymbol()             inout { return null; }
1200     inout(ArrayScopeSymbol)            isArrayScopeSymbol()            inout { return null; }
1201     inout(Import)                      isImport()                      inout { return null; }
1202     inout(EnumDeclaration)             isEnumDeclaration()             inout { return null; }
1203     inout(SymbolDeclaration)           isSymbolDeclaration()           inout { return null; }
1204     inout(AttribDeclaration)           isAttribDeclaration()           inout { return null; }
1205     inout(AnonDeclaration)             isAnonDeclaration()             inout { return null; }
1206     inout(CPPNamespaceDeclaration)     isCPPNamespaceDeclaration()     inout { return null; }
1207     inout(ProtDeclaration)             isProtDeclaration()             inout { return null; }
1208     inout(OverloadSet)                 isOverloadSet()                 inout { return null; }
1209     inout(CompileDeclaration)          isCompileDeclaration()          inout { return null; }
1210 }
1211 
1212 /***********************************************************
1213  * Dsymbol that generates a scope
1214  */
1215 extern (C++) class ScopeDsymbol : Dsymbol
1216 {
1217     Dsymbols* members;          // all Dsymbol's in this scope
1218     DsymbolTable symtab;        // members[] sorted into table
1219     uint endlinnum;             // the linnumber of the statement after the scope (0 if unknown)
1220 
1221 private:
1222     /// symbols whose members have been imported, i.e. imported modules and template mixins
1223     Dsymbols* importedScopes;
1224     Prot.Kind* prots;            // array of Prot.Kind, one for each import
1225 
1226     import dmd.root.bitarray;
1227     BitArray accessiblePackages, privateAccessiblePackages;// whitelists of accessible (imported) packages
1228 
1229 public:
1230     final extern (D) this()
1231     {
1232     }
1233 
1234     final extern (D) this(Identifier ident)
1235     {
1236         super(ident);
1237     }
1238 
1239     final extern (D) this(const ref Loc loc, Identifier ident)
1240     {
1241         super(loc, ident);
1242     }
1243 
1244     override Dsymbol syntaxCopy(Dsymbol s)
1245     {
1246         //printf("ScopeDsymbol::syntaxCopy('%s')\n", toChars());
1247         ScopeDsymbol sds = s ? cast(ScopeDsymbol)s : new ScopeDsymbol(ident);
1248         sds.comment = comment;
1249         sds.members = arraySyntaxCopy(members);
1250         sds.endlinnum = endlinnum;
1251         return sds;
1252     }
1253 
1254     /*****************************************
1255      * This function is #1 on the list of functions that eat cpu time.
1256      * Be very, very careful about slowing it down.
1257      */
1258     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
1259     {
1260         //printf("%s.ScopeDsymbol::search(ident='%s', flags=x%x)\n", toChars(), ident.toChars(), flags);
1261         //if (strcmp(ident.toChars(),"c") == 0) *(char*)0=0;
1262 
1263         // Look in symbols declared in this module
1264         if (symtab && !(flags & SearchImportsOnly))
1265         {
1266             //printf(" look in locals\n");
1267             auto s1 = symtab.lookup(ident);
1268             if (s1)
1269             {
1270                 //printf("\tfound in locals = '%s.%s'\n",toChars(),s1.toChars());
1271                 return s1;
1272             }
1273         }
1274         //printf(" not found in locals\n");
1275 
1276         // Look in imported scopes
1277         if (!importedScopes)
1278             return null;
1279 
1280         //printf(" look in imports\n");
1281         Dsymbol s = null;
1282         OverloadSet a = null;
1283         // Look in imported modules
1284         for (size_t i = 0; i < importedScopes.dim; i++)
1285         {
1286             // If private import, don't search it
1287             if ((flags & IgnorePrivateImports) && prots[i] == Prot.Kind.private_)
1288                 continue;
1289             int sflags = flags & (IgnoreErrors | IgnoreAmbiguous); // remember these in recursive searches
1290             Dsymbol ss = (*importedScopes)[i];
1291             //printf("\tscanning import '%s', prots = %d, isModule = %p, isImport = %p\n", ss.toChars(), prots[i], ss.isModule(), ss.isImport());
1292 
1293             if (ss.isModule())
1294             {
1295                 if (flags & SearchLocalsOnly)
1296                     continue;
1297             }
1298             else // mixin template
1299             {
1300                 if (flags & SearchImportsOnly)
1301                     continue;
1302 
1303                 sflags |= SearchLocalsOnly;
1304             }
1305 
1306             /* Don't find private members if ss is a module
1307              */
1308             Dsymbol s2 = ss.search(loc, ident, sflags | (ss.isModule() ? IgnorePrivateImports : IgnoreNone));
1309             import dmd.access : symbolIsVisible;
1310             if (!s2 || !(flags & IgnoreSymbolVisibility) && !symbolIsVisible(this, s2))
1311                 continue;
1312             if (!s)
1313             {
1314                 s = s2;
1315                 if (s && s.isOverloadSet())
1316                     a = mergeOverloadSet(ident, a, s);
1317             }
1318             else if (s2 && s != s2)
1319             {
1320                 if (s.toAlias() == s2.toAlias() || s.getType() == s2.getType() && s.getType())
1321                 {
1322                     /* After following aliases, we found the same
1323                      * symbol, so it's not an ambiguity.  But if one
1324                      * alias is deprecated or less accessible, prefer
1325                      * the other.
1326                      */
1327                     if (s.isDeprecated() || s.prot().isMoreRestrictiveThan(s2.prot()) && s2.prot().kind != Prot.Kind.none)
1328                         s = s2;
1329                 }
1330                 else
1331                 {
1332                     /* Two imports of the same module should be regarded as
1333                      * the same.
1334                      */
1335                     Import i1 = s.isImport();
1336                     Import i2 = s2.isImport();
1337                     if (!(i1 && i2 && (i1.mod == i2.mod || (!i1.parent.isImport() && !i2.parent.isImport() && i1.ident.equals(i2.ident)))))
1338                     {
1339                         /* https://issues.dlang.org/show_bug.cgi?id=8668
1340                          * Public selective import adds AliasDeclaration in module.
1341                          * To make an overload set, resolve aliases in here and
1342                          * get actual overload roots which accessible via s and s2.
1343                          */
1344                         s = s.toAlias();
1345                         s2 = s2.toAlias();
1346                         /* If both s2 and s are overloadable (though we only
1347                          * need to check s once)
1348                          */
1349 
1350                         auto so2 = s2.isOverloadSet();
1351                         if ((so2 || s2.isOverloadable()) && (a || s.isOverloadable()))
1352                         {
1353                             if (symbolIsVisible(this, s2))
1354                             {
1355                                 a = mergeOverloadSet(ident, a, s2);
1356                             }
1357                             if (!symbolIsVisible(this, s))
1358                                 s = s2;
1359                             continue;
1360                         }
1361 
1362                         /* Two different overflow sets can have the same members
1363                          * https://issues.dlang.org/show_bug.cgi?id=16709
1364                          */
1365                         auto so = s.isOverloadSet();
1366                         if (so && so2)
1367                         {
1368                             if (so.a.length == so2.a.length)
1369                             {
1370                                 foreach (j; 0 .. so.a.length)
1371                                 {
1372                                     if (so.a[j] !is so2.a[j])
1373                                         goto L1;
1374                                 }
1375                                 continue;  // the same
1376                               L1:
1377                                 {   } // different
1378                             }
1379                         }
1380 
1381                         if (flags & IgnoreAmbiguous) // if return NULL on ambiguity
1382                             return null;
1383                         if (!(flags & IgnoreErrors))
1384                             ScopeDsymbol.multiplyDefined(loc, s, s2);
1385                         break;
1386                     }
1387                 }
1388             }
1389         }
1390         if (s)
1391         {
1392             /* Build special symbol if we had multiple finds
1393              */
1394             if (a)
1395             {
1396                 if (!s.isOverloadSet())
1397                     a = mergeOverloadSet(ident, a, s);
1398                 s = a;
1399             }
1400             //printf("\tfound in imports %s.%s\n", toChars(), s.toChars());
1401             return s;
1402         }
1403         //printf(" not found in imports\n");
1404         return null;
1405     }
1406 
1407     extern (D) private OverloadSet mergeOverloadSet(Identifier ident, OverloadSet os, Dsymbol s)
1408     {
1409         if (!os)
1410         {
1411             os = new OverloadSet(ident);
1412             os.parent = this;
1413         }
1414         if (OverloadSet os2 = s.isOverloadSet())
1415         {
1416             // Merge the cross-module overload set 'os2' into 'os'
1417             if (os.a.dim == 0)
1418             {
1419                 os.a.setDim(os2.a.dim);
1420                 memcpy(os.a.tdata(), os2.a.tdata(), (os.a[0]).sizeof * os2.a.dim);
1421             }
1422             else
1423             {
1424                 for (size_t i = 0; i < os2.a.dim; i++)
1425                 {
1426                     os = mergeOverloadSet(ident, os, os2.a[i]);
1427                 }
1428             }
1429         }
1430         else
1431         {
1432             assert(s.isOverloadable());
1433             /* Don't add to os[] if s is alias of previous sym
1434              */
1435             for (size_t j = 0; j < os.a.dim; j++)
1436             {
1437                 Dsymbol s2 = os.a[j];
1438                 if (s.toAlias() == s2.toAlias())
1439                 {
1440                     if (s2.isDeprecated() || (s2.prot().isMoreRestrictiveThan(s.prot()) && s.prot().kind != Prot.Kind.none))
1441                     {
1442                         os.a[j] = s;
1443                     }
1444                     goto Lcontinue;
1445                 }
1446             }
1447             os.push(s);
1448         Lcontinue:
1449         }
1450         return os;
1451     }
1452 
1453     void importScope(Dsymbol s, Prot protection)
1454     {
1455         //printf("%s.ScopeDsymbol::importScope(%s, %d)\n", toChars(), s.toChars(), protection);
1456         // No circular or redundant import's
1457         if (s != this)
1458         {
1459             if (!importedScopes)
1460                 importedScopes = new Dsymbols();
1461             else
1462             {
1463                 for (size_t i = 0; i < importedScopes.dim; i++)
1464                 {
1465                     Dsymbol ss = (*importedScopes)[i];
1466                     if (ss == s) // if already imported
1467                     {
1468                         if (protection.kind > prots[i])
1469                             prots[i] = protection.kind; // upgrade access
1470                         return;
1471                     }
1472                 }
1473             }
1474             importedScopes.push(s);
1475             prots = cast(Prot.Kind*)mem.xrealloc(prots, importedScopes.dim * (prots[0]).sizeof);
1476             prots[importedScopes.dim - 1] = protection.kind;
1477         }
1478     }
1479 
1480     extern (D) final void addAccessiblePackage(Package p, Prot protection)
1481     {
1482         auto pary = protection.kind == Prot.Kind.private_ ? &privateAccessiblePackages : &accessiblePackages;
1483         if (pary.length <= p.tag)
1484             pary.length = p.tag + 1;
1485         (*pary)[p.tag] = true;
1486     }
1487 
1488     bool isPackageAccessible(Package p, Prot protection, int flags = 0)
1489     {
1490         if (p.tag < accessiblePackages.length && accessiblePackages[p.tag] ||
1491             protection.kind == Prot.Kind.private_ && p.tag < privateAccessiblePackages.length && privateAccessiblePackages[p.tag])
1492             return true;
1493         foreach (i, ss; importedScopes ? (*importedScopes)[] : null)
1494         {
1495             // only search visible scopes && imported modules should ignore private imports
1496             if (protection.kind <= prots[i] &&
1497                 ss.isScopeDsymbol.isPackageAccessible(p, protection, IgnorePrivateImports))
1498                 return true;
1499         }
1500         return false;
1501     }
1502 
1503     override final bool isforwardRef()
1504     {
1505         return (members is null);
1506     }
1507 
1508     static void multiplyDefined(const ref Loc loc, Dsymbol s1, Dsymbol s2)
1509     {
1510         version (none)
1511         {
1512             printf("ScopeDsymbol::multiplyDefined()\n");
1513             printf("s1 = %p, '%s' kind = '%s', parent = %s\n", s1, s1.toChars(), s1.kind(), s1.parent ? s1.parent.toChars() : "");
1514             printf("s2 = %p, '%s' kind = '%s', parent = %s\n", s2, s2.toChars(), s2.kind(), s2.parent ? s2.parent.toChars() : "");
1515         }
1516         if (loc.isValid())
1517         {
1518             .error(loc, "%s `%s` at %s conflicts with %s `%s` at %s",
1519                 s1.kind(), s1.toPrettyChars(), s1.locToChars(),
1520                 s2.kind(), s2.toPrettyChars(), s2.locToChars());
1521 
1522             static if (0)
1523             {
1524                 if (auto so = s1.isOverloadSet())
1525                 {
1526                     printf("first %p:\n", so);
1527                     foreach (s; so.a[])
1528                     {
1529                         printf("  %p %s `%s` at %s\n", s, s.kind(), s.toPrettyChars(), s.locToChars());
1530                     }
1531                 }
1532                 if (auto so = s2.isOverloadSet())
1533                 {
1534                     printf("second %p:\n", so);
1535                     foreach (s; so.a[])
1536                     {
1537                         printf("  %p %s `%s` at %s\n", s, s.kind(), s.toPrettyChars(), s.locToChars());
1538                     }
1539                 }
1540             }
1541         }
1542         else
1543         {
1544             s1.error(s1.loc, "conflicts with %s `%s` at %s", s2.kind(), s2.toPrettyChars(), s2.locToChars());
1545         }
1546     }
1547 
1548     override const(char)* kind() const
1549     {
1550         return "ScopeDsymbol";
1551     }
1552 
1553     /*******************************************
1554      * Look for member of the form:
1555      *      const(MemberInfo)[] getMembers(string);
1556      * Returns NULL if not found
1557      */
1558     final FuncDeclaration findGetMembers()
1559     {
1560         Dsymbol s = search_function(this, Id.getmembers);
1561         FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
1562         version (none)
1563         {
1564             // Finish
1565             __gshared TypeFunction tfgetmembers;
1566             if (!tfgetmembers)
1567             {
1568                 Scope sc;
1569                 auto parameters = new Parameters();
1570                 Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
1571                 parameters.push(p);
1572                 Type tret = null;
1573                 tfgetmembers = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
1574                 tfgetmembers = cast(TypeFunction)tfgetmembers.dsymbolSemantic(Loc.initial, &sc);
1575             }
1576             if (fdx)
1577                 fdx = fdx.overloadExactMatch(tfgetmembers);
1578         }
1579         if (fdx && fdx.isVirtual())
1580             fdx = null;
1581         return fdx;
1582     }
1583 
1584     Dsymbol symtabInsert(Dsymbol s)
1585     {
1586         return symtab.insert(s);
1587     }
1588 
1589     /****************************************
1590      * Look up identifier in symbol table.
1591      */
1592 
1593     Dsymbol symtabLookup(Dsymbol s, Identifier id)
1594     {
1595         return symtab.lookup(id);
1596     }
1597 
1598     /****************************************
1599      * Return true if any of the members are static ctors or static dtors, or if
1600      * any members have members that are.
1601      */
1602     override bool hasStaticCtorOrDtor()
1603     {
1604         if (members)
1605         {
1606             for (size_t i = 0; i < members.dim; i++)
1607             {
1608                 Dsymbol member = (*members)[i];
1609                 if (member.hasStaticCtorOrDtor())
1610                     return true;
1611             }
1612         }
1613         return false;
1614     }
1615 
1616     extern (D) alias ForeachDg = int delegate(size_t idx, Dsymbol s);
1617 
1618     /***************************************
1619      * Expands attribute declarations in members in depth first
1620      * order. Calls dg(size_t symidx, Dsymbol *sym) for each
1621      * member.
1622      * If dg returns !=0, stops and returns that value else returns 0.
1623      * Use this function to avoid the O(N + N^2/2) complexity of
1624      * calculating dim and calling N times getNth.
1625      * Returns:
1626      *  last value returned by dg()
1627      */
1628     extern (D) static int _foreach(Scope* sc, Dsymbols* members, scope ForeachDg dg, size_t* pn = null)
1629     {
1630         assert(dg);
1631         if (!members)
1632             return 0;
1633         size_t n = pn ? *pn : 0; // take over index
1634         int result = 0;
1635         foreach (size_t i; 0 .. members.dim)
1636         {
1637             Dsymbol s = (*members)[i];
1638             if (AttribDeclaration a = s.isAttribDeclaration())
1639                 result = _foreach(sc, a.include(sc), dg, &n);
1640             else if (TemplateMixin tm = s.isTemplateMixin())
1641                 result = _foreach(sc, tm.members, dg, &n);
1642             else if (s.isTemplateInstance())
1643             {
1644             }
1645             else if (s.isUnitTestDeclaration())
1646             {
1647             }
1648             else
1649                 result = dg(n++, s);
1650             if (result)
1651                 break;
1652         }
1653         if (pn)
1654             *pn = n; // update index
1655         return result;
1656     }
1657 
1658     override final inout(ScopeDsymbol) isScopeDsymbol() inout
1659     {
1660         return this;
1661     }
1662 
1663     override void accept(Visitor v)
1664     {
1665         v.visit(this);
1666     }
1667 }
1668 
1669 /***********************************************************
1670  * With statement scope
1671  */
1672 extern (C++) final class WithScopeSymbol : ScopeDsymbol
1673 {
1674     WithStatement withstate;
1675 
1676     extern (D) this(WithStatement withstate)
1677     {
1678         this.withstate = withstate;
1679     }
1680 
1681     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
1682     {
1683         //printf("WithScopeSymbol.search(%s)\n", ident.toChars());
1684         if (flags & SearchImportsOnly)
1685             return null;
1686         // Acts as proxy to the with class declaration
1687         Dsymbol s = null;
1688         Expression eold = null;
1689         for (Expression e = withstate.exp; e != eold; e = resolveAliasThis(_scope, e))
1690         {
1691             if (e.op == TOK.scope_)
1692             {
1693                 s = (cast(ScopeExp)e).sds;
1694             }
1695             else if (e.op == TOK.type)
1696             {
1697                 s = e.type.toDsymbol(null);
1698             }
1699             else
1700             {
1701                 Type t = e.type.toBasetype();
1702                 s = t.toDsymbol(null);
1703             }
1704             if (s)
1705             {
1706                 s = s.search(loc, ident, flags);
1707                 if (s)
1708                     return s;
1709             }
1710             eold = e;
1711         }
1712         return null;
1713     }
1714 
1715     override inout(WithScopeSymbol) isWithScopeSymbol() inout
1716     {
1717         return this;
1718     }
1719 
1720     override void accept(Visitor v)
1721     {
1722         v.visit(this);
1723     }
1724 }
1725 
1726 /***********************************************************
1727  * Array Index/Slice scope
1728  */
1729 extern (C++) final class ArrayScopeSymbol : ScopeDsymbol
1730 {
1731     Expression exp;         // IndexExp or SliceExp
1732     TypeTuple type;         // for tuple[length]
1733     TupleDeclaration td;    // for tuples of objects
1734     Scope* sc;
1735 
1736     extern (D) this(Scope* sc, Expression exp)
1737     {
1738         super(exp.loc, null);
1739         assert(exp.op == TOK.index || exp.op == TOK.slice || exp.op == TOK.array);
1740         this.exp = exp;
1741         this.sc = sc;
1742     }
1743 
1744     extern (D) this(Scope* sc, TypeTuple type)
1745     {
1746         this.type = type;
1747         this.sc = sc;
1748     }
1749 
1750     extern (D) this(Scope* sc, TupleDeclaration td)
1751     {
1752         this.td = td;
1753         this.sc = sc;
1754     }
1755 
1756     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = IgnoreNone)
1757     {
1758         //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident.toChars(), flags);
1759         if (ident != Id.dollar)
1760             return null;
1761 
1762         VarDeclaration* pvar;
1763         Expression ce;
1764     L1:
1765         if (td)
1766         {
1767             /* $ gives the number of elements in the tuple
1768              */
1769             auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null);
1770             Expression e = new IntegerExp(Loc.initial, td.objects.dim, Type.tsize_t);
1771             v._init = new ExpInitializer(Loc.initial, e);
1772             v.storage_class |= STC.temp | STC.static_ | STC.const_;
1773             v.dsymbolSemantic(sc);
1774             return v;
1775         }
1776         if (type)
1777         {
1778             /* $ gives the number of type entries in the type tuple
1779              */
1780             auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null);
1781             Expression e = new IntegerExp(Loc.initial, type.arguments.dim, Type.tsize_t);
1782             v._init = new ExpInitializer(Loc.initial, e);
1783             v.storage_class |= STC.temp | STC.static_ | STC.const_;
1784             v.dsymbolSemantic(sc);
1785             return v;
1786         }
1787         if (auto ie = exp.isIndexExp())
1788         {
1789             /* array[index] where index is some function of $
1790              */
1791             pvar = &ie.lengthVar;
1792             ce = ie.e1;
1793         }
1794         else if (auto se = exp.isSliceExp())
1795         {
1796             /* array[lwr .. upr] where lwr or upr is some function of $
1797              */
1798             pvar = &se.lengthVar;
1799             ce = se.e1;
1800         }
1801         else if (auto ae = exp.isArrayExp())
1802         {
1803             /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $
1804              * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...)
1805              */
1806             pvar = &ae.lengthVar;
1807             ce = ae.e1;
1808         }
1809         else
1810         {
1811             /* Didn't find $, look in enclosing scope(s).
1812              */
1813             return null;
1814         }
1815         ce = ce.lastComma();
1816         /* If we are indexing into an array that is really a type
1817          * tuple, rewrite this as an index into a type tuple and
1818          * try again.
1819          */
1820         if (auto te = ce.isTypeExp())
1821         {
1822             if (auto ttp = te.type.isTypeTuple())
1823             {
1824                 type = ttp;
1825                 goto L1;
1826             }
1827         }
1828         /* *pvar is lazily initialized, so if we refer to $
1829          * multiple times, it gets set only once.
1830          */
1831         if (!*pvar) // if not already initialized
1832         {
1833             /* Create variable v and set it to the value of $
1834              */
1835             VarDeclaration v;
1836             Type t;
1837             if (auto tupexp = ce.isTupleExp())
1838             {
1839                 /* It is for an expression tuple, so the
1840                  * length will be a const.
1841                  */
1842                 Expression e = new IntegerExp(Loc.initial, tupexp.exps.dim, Type.tsize_t);
1843                 v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, new ExpInitializer(Loc.initial, e));
1844                 v.storage_class |= STC.temp | STC.static_ | STC.const_;
1845             }
1846             else if (ce.type && (t = ce.type.toBasetype()) !is null && (t.ty == Tstruct || t.ty == Tclass))
1847             {
1848                 // Look for opDollar
1849                 assert(exp.op == TOK.array || exp.op == TOK.slice);
1850                 AggregateDeclaration ad = isAggregate(t);
1851                 assert(ad);
1852                 Dsymbol s = ad.search(loc, Id.opDollar);
1853                 if (!s) // no dollar exists -- search in higher scope
1854                     return null;
1855                 s = s.toAlias();
1856                 Expression e = null;
1857                 // Check for multi-dimensional opDollar(dim) template.
1858                 if (TemplateDeclaration td = s.isTemplateDeclaration())
1859                 {
1860                     dinteger_t dim = 0;
1861                     if (exp.op == TOK.array)
1862                     {
1863                         dim = (cast(ArrayExp)exp).currentDimension;
1864                     }
1865                     else if (exp.op == TOK.slice)
1866                     {
1867                         dim = 0; // slices are currently always one-dimensional
1868                     }
1869                     else
1870                     {
1871                         assert(0);
1872                     }
1873                     auto tiargs = new Objects();
1874                     Expression edim = new IntegerExp(Loc.initial, dim, Type.tsize_t);
1875                     edim = edim.expressionSemantic(sc);
1876                     tiargs.push(edim);
1877                     e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs);
1878                 }
1879                 else
1880                 {
1881                     /* opDollar exists, but it's not a template.
1882                      * This is acceptable ONLY for single-dimension indexing.
1883                      * Note that it's impossible to have both template & function opDollar,
1884                      * because both take no arguments.
1885                      */
1886                     if (exp.op == TOK.array && (cast(ArrayExp)exp).arguments.dim != 1)
1887                     {
1888                         exp.error("`%s` only defines opDollar for one dimension", ad.toChars());
1889                         return null;
1890                     }
1891                     Declaration d = s.isDeclaration();
1892                     assert(d);
1893                     e = new DotVarExp(loc, ce, d);
1894                 }
1895                 e = e.expressionSemantic(sc);
1896                 if (!e.type)
1897                     exp.error("`%s` has no value", e.toChars());
1898                 t = e.type.toBasetype();
1899                 if (t && t.ty == Tfunction)
1900                     e = new CallExp(e.loc, e);
1901                 v = new VarDeclaration(loc, null, Id.dollar, new ExpInitializer(Loc.initial, e));
1902                 v.storage_class |= STC.temp | STC.ctfe | STC.rvalue;
1903             }
1904             else
1905             {
1906                 /* For arrays, $ will either be a compile-time constant
1907                  * (in which case its value in set during constant-folding),
1908                  * or a variable (in which case an expression is created in
1909                  * toir.c).
1910                  */
1911                 auto e = new VoidInitializer(Loc.initial);
1912                 e.type = Type.tsize_t;
1913                 v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, e);
1914                 v.storage_class |= STC.temp | STC.ctfe; // it's never a true static variable
1915             }
1916             *pvar = v;
1917         }
1918         (*pvar).dsymbolSemantic(sc);
1919         return (*pvar);
1920     }
1921 
1922     override inout(ArrayScopeSymbol) isArrayScopeSymbol() inout
1923     {
1924         return this;
1925     }
1926 
1927     override void accept(Visitor v)
1928     {
1929         v.visit(this);
1930     }
1931 }
1932 
1933 /***********************************************************
1934  * Overload Sets
1935  */
1936 extern (C++) final class OverloadSet : Dsymbol
1937 {
1938     Dsymbols a;     // array of Dsymbols
1939 
1940     extern (D) this(Identifier ident, OverloadSet os = null)
1941     {
1942         super(ident);
1943         if (os)
1944         {
1945             a.pushSlice(os.a[]);
1946         }
1947     }
1948 
1949     void push(Dsymbol s)
1950     {
1951         a.push(s);
1952     }
1953 
1954     override inout(OverloadSet) isOverloadSet() inout
1955     {
1956         return this;
1957     }
1958 
1959     override const(char)* kind() const
1960     {
1961         return "overloadset";
1962     }
1963 
1964     override void accept(Visitor v)
1965     {
1966         v.visit(this);
1967     }
1968 }
1969 
1970 /***********************************************************
1971  * Forwarding ScopeDsymbol.  Used by ForwardingAttribDeclaration and
1972  * ForwardingScopeDeclaration to forward symbol insertions to another
1973  * scope.  See `dmd.attrib.ForwardingAttribDeclaration` for more
1974  * details.
1975  */
1976 extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol
1977 {
1978     /*************************
1979      * Symbol to forward insertions to.
1980      * Can be `null` before being lazily initialized.
1981      */
1982     ScopeDsymbol forward;
1983     extern (D) this(ScopeDsymbol forward)
1984     {
1985         super(null);
1986         this.forward = forward;
1987     }
1988     override Dsymbol symtabInsert(Dsymbol s)
1989     {
1990         assert(forward);
1991         if (auto d = s.isDeclaration())
1992         {
1993             if (d.storage_class & STC.local)
1994             {
1995                 // Symbols with storage class STC.local are not
1996                 // forwarded, but stored in the local symbol
1997                 // table. (Those are the `static foreach` variables.)
1998                 if (!symtab)
1999                 {
2000                     symtab = new DsymbolTable();
2001                 }
2002                 return super.symtabInsert(s); // insert locally
2003             }
2004         }
2005         if (!forward.symtab)
2006         {
2007             forward.symtab = new DsymbolTable();
2008         }
2009         // Non-STC.local symbols are forwarded to `forward`.
2010         return forward.symtabInsert(s);
2011     }
2012 
2013     /************************
2014      * This override handles the following two cases:
2015      *     static foreach (i, i; [0]) { ... }
2016      * and
2017      *     static foreach (i; [0]) { enum i = 2; }
2018      */
2019     override Dsymbol symtabLookup(Dsymbol s, Identifier id)
2020     {
2021         assert(forward);
2022         // correctly diagnose clashing foreach loop variables.
2023         if (auto d = s.isDeclaration())
2024         {
2025             if (d.storage_class & STC.local)
2026             {
2027                 if (!symtab)
2028                 {
2029                     symtab = new DsymbolTable();
2030                 }
2031                 return super.symtabLookup(s,id);
2032             }
2033         }
2034         // Declarations within `static foreach` do not clash with
2035         // `static foreach` loop variables.
2036         if (!forward.symtab)
2037         {
2038             forward.symtab = new DsymbolTable();
2039         }
2040         return forward.symtabLookup(s,id);
2041     }
2042 
2043     override void importScope(Dsymbol s, Prot protection)
2044     {
2045         forward.importScope(s, protection);
2046     }
2047 
2048     override const(char)* kind()const{ return "local scope"; }
2049 
2050     override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout
2051     {
2052         return this;
2053     }
2054 
2055 }
2056 
2057 /**
2058  * Class that holds an expression in a Dsymbol wraper.
2059  * This is not an AST node, but a class used to pass
2060  * an expression as a function parameter of type Dsymbol.
2061  */
2062 extern (C++) final class ExpressionDsymbol : Dsymbol
2063 {
2064     Expression exp;
2065     this(Expression exp)
2066     {
2067         super();
2068         this.exp = exp;
2069     }
2070 
2071     override inout(ExpressionDsymbol) isExpressionDsymbol() inout
2072     {
2073         return this;
2074     }
2075 }
2076 
2077 
2078 /***********************************************************
2079  * Table of Dsymbol's
2080  */
2081 extern (C++) final class DsymbolTable : RootObject
2082 {
2083     AssocArray!(Identifier, Dsymbol) tab;
2084 
2085     // Look up Identifier. Return Dsymbol if found, NULL if not.
2086     Dsymbol lookup(const Identifier ident)
2087     {
2088         //printf("DsymbolTable::lookup(%s)\n", ident.toChars());
2089         return tab[ident];
2090     }
2091 
2092     // Look for Dsymbol in table. If there, return it. If not, insert s and return that.
2093     Dsymbol update(Dsymbol s)
2094     {
2095         const ident = s.ident;
2096         Dsymbol* ps = tab.getLvalue(ident);
2097         *ps = s;
2098         return s;
2099     }
2100 
2101     // Insert Dsymbol in table. Return NULL if already there.
2102     Dsymbol insert(Dsymbol s)
2103     {
2104         //printf("DsymbolTable::insert(this = %p, '%s')\n", this, s.ident.toChars());
2105         return insert(s.ident, s);
2106     }
2107 
2108     // when ident and s are not the same
2109     Dsymbol insert(const Identifier ident, Dsymbol s)
2110     {
2111         //printf("DsymbolTable::insert()\n");
2112         Dsymbol* ps = tab.getLvalue(ident);
2113         if (*ps)
2114             return null; // already in table
2115         *ps = s;
2116         return s;
2117     }
2118 
2119     /*****
2120      * Returns:
2121      *  number of symbols in symbol table
2122      */
2123     size_t length() const pure
2124     {
2125         return tab.length;
2126     }
2127 }