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