| /** |
| * The base class for a D symbol, which can be a module, variable, function, enum, etc. |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dsymbol.d, _dsymbol.d) |
| * Documentation: https://dlang.org/phobos/dmd_dsymbol.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbol.d |
| */ |
| |
| module dmd.dsymbol; |
| |
| import core.stdc.stdarg; |
| import core.stdc.stdio; |
| import core.stdc.string; |
| import core.stdc.stdlib; |
| |
| import dmd.aggregate; |
| import dmd.aliasthis; |
| import dmd.arraytypes; |
| import dmd.attrib; |
| import dmd.astenums; |
| import dmd.ast_node; |
| import dmd.gluelayer; |
| import dmd.dclass; |
| import dmd.declaration; |
| import dmd.denum; |
| import dmd.dimport; |
| import dmd.dmodule; |
| import dmd.dversion; |
| import dmd.dscope; |
| import dmd.dstruct; |
| import dmd.dsymbolsem; |
| import dmd.dtemplate; |
| import dmd.errors; |
| import dmd.expression; |
| import dmd.expressionsem; |
| import dmd.func; |
| import dmd.globals; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.init; |
| import dmd.lexer; |
| import dmd.mtype; |
| import dmd.nspace; |
| import dmd.opover; |
| import dmd.root.aav; |
| import dmd.root.rmem; |
| import dmd.root.rootobject; |
| import dmd.root.speller; |
| import dmd.root.string; |
| import dmd.statement; |
| import dmd.staticassert; |
| import dmd.tokens; |
| import dmd.visitor; |
| |
| /*************************************** |
| * Calls dg(Dsymbol *sym) for each Dsymbol. |
| * If dg returns !=0, stops and returns that value else returns 0. |
| * Params: |
| * symbols = Dsymbols |
| * dg = delegate to call for each Dsymbol |
| * Returns: |
| * last value returned by dg() |
| * |
| * See_Also: $(REF each, dmd, root, array) |
| */ |
| int foreachDsymbol(Dsymbols* symbols, scope int delegate(Dsymbol) dg) |
| { |
| assert(dg); |
| if (symbols) |
| { |
| /* Do not use foreach, as the size of the array may expand during iteration |
| */ |
| for (size_t i = 0; i < symbols.dim; ++i) |
| { |
| Dsymbol s = (*symbols)[i]; |
| const result = dg(s); |
| if (result) |
| return result; |
| } |
| } |
| return 0; |
| } |
| |
| /*************************************** |
| * Calls dg(Dsymbol *sym) for each Dsymbol. |
| * Params: |
| * symbols = Dsymbols |
| * dg = delegate to call for each Dsymbol |
| * |
| * See_Also: $(REF each, dmd, root, array) |
| */ |
| void foreachDsymbol(Dsymbols* symbols, scope void delegate(Dsymbol) dg) |
| { |
| assert(dg); |
| if (symbols) |
| { |
| /* Do not use foreach, as the size of the array may expand during iteration |
| */ |
| for (size_t i = 0; i < symbols.dim; ++i) |
| { |
| Dsymbol s = (*symbols)[i]; |
| dg(s); |
| } |
| } |
| } |
| |
| |
| struct Ungag |
| { |
| uint oldgag; |
| |
| extern (D) this(uint old) nothrow |
| { |
| this.oldgag = old; |
| } |
| |
| extern (C++) ~this() nothrow |
| { |
| global.gag = oldgag; |
| } |
| } |
| |
| struct Visibility |
| { |
| /// |
| enum Kind : ubyte |
| { |
| undefined, |
| none, // no access |
| private_, |
| package_, |
| protected_, |
| public_, |
| export_, |
| } |
| |
| Kind kind; |
| Package pkg; |
| |
| extern (D): |
| |
| this(Visibility.Kind kind) pure nothrow @nogc @safe |
| { |
| this.kind = kind; |
| } |
| |
| /** |
| * Checks if `this` is less or more visible than `other` |
| * |
| * Params: |
| * other = Visibility to compare `this` to. |
| * |
| * Returns: |
| * A value `< 0` if `this` is less visible than `other`, |
| * a value `> 0` if `this` is more visible than `other`, |
| * and `0` if they are at the same level. |
| * Note that `package` visibility with different packages |
| * will also return `0`. |
| */ |
| int opCmp(const Visibility other) const pure nothrow @nogc @safe |
| { |
| return this.kind - other.kind; |
| } |
| |
| /// |
| unittest |
| { |
| assert(Visibility(Visibility.Kind.public_) > Visibility(Visibility.Kind.private_)); |
| assert(Visibility(Visibility.Kind.private_) < Visibility(Visibility.Kind.protected_)); |
| assert(Visibility(Visibility.Kind.package_) >= Visibility(Visibility.Kind.package_)); |
| } |
| |
| /** |
| * Checks if `this` is absolutely identical visibility attribute to `other` |
| */ |
| bool opEquals(ref const Visibility other) const |
| { |
| if (this.kind == other.kind) |
| { |
| if (this.kind == Visibility.Kind.package_) |
| return this.pkg == other.pkg; |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| enum PASS : ubyte |
| { |
| initial, // initial state |
| semantic, // semantic() started |
| semanticdone, // semantic() done |
| semantic2, // semantic2() started |
| semantic2done, // semantic2() done |
| semantic3, // semantic3() started |
| semantic3done, // semantic3() done |
| inline, // inline started |
| inlinedone, // inline done |
| obj, // toObjFile() run |
| } |
| |
| // Search options |
| enum : int |
| { |
| IgnoreNone = 0x00, // default |
| IgnorePrivateImports = 0x01, // don't search private imports |
| IgnoreErrors = 0x02, // don't give error messages |
| IgnoreAmbiguous = 0x04, // return NULL if ambiguous |
| SearchLocalsOnly = 0x08, // only look at locals (don't search imports) |
| SearchImportsOnly = 0x10, // only look in imports |
| SearchUnqualifiedModule = 0x20, // the module scope search is unqualified, |
| // meaning don't search imports in that scope, |
| // because qualified module searches search |
| // their imports |
| IgnoreSymbolVisibility = 0x80, // also find private and package protected symbols |
| TagNameSpace = 0x100, // search ImportC tag symbol table |
| } |
| |
| /*********************************************************** |
| * Struct/Class/Union field state. |
| * Used for transitory information when setting field offsets, such |
| * as bit fields. |
| */ |
| struct FieldState |
| { |
| uint offset; /// byte offset for next field |
| |
| uint fieldOffset; /// byte offset for the start of the bit field |
| uint fieldSize; /// byte size of field |
| uint fieldAlign; /// byte alignment of field |
| uint bitOffset; /// bit offset for field |
| |
| bool inFlight; /// bit field is in flight |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) class Dsymbol : ASTNode |
| { |
| Identifier ident; |
| Dsymbol parent; |
| /// C++ namespace this symbol belongs to |
| CPPNamespaceDeclaration cppnamespace; |
| Symbol* csym; // symbol for code generator |
| const Loc loc; // where defined |
| Scope* _scope; // !=null means context to use for semantic() |
| const(char)* prettystring; // cached value of toPrettyChars() |
| bool errors; // this symbol failed to pass semantic() |
| PASS semanticRun = PASS.initial; |
| ushort localNum; /// perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab |
| |
| DeprecatedDeclaration depdecl; // customized deprecation message |
| UserAttributeDeclaration userAttribDecl; // user defined attributes |
| |
| final extern (D) this() nothrow |
| { |
| //printf("Dsymbol::Dsymbol(%p)\n", this); |
| loc = Loc(null, 0, 0); |
| } |
| |
| final extern (D) this(Identifier ident) nothrow |
| { |
| //printf("Dsymbol::Dsymbol(%p, ident)\n", this); |
| this.loc = Loc(null, 0, 0); |
| this.ident = ident; |
| } |
| |
| final extern (D) this(const ref Loc loc, Identifier ident) nothrow |
| { |
| //printf("Dsymbol::Dsymbol(%p, ident)\n", this); |
| this.loc = loc; |
| this.ident = ident; |
| } |
| |
| static Dsymbol create(Identifier ident) nothrow |
| { |
| return new Dsymbol(ident); |
| } |
| |
| override const(char)* toChars() const |
| { |
| return ident ? ident.toChars() : "__anonymous"; |
| } |
| |
| // helper to print fully qualified (template) arguments |
| const(char)* toPrettyCharsHelper() |
| { |
| return toChars(); |
| } |
| |
| final const(Loc) getLoc() |
| { |
| if (!loc.isValid()) // avoid bug 5861. |
| if (const m = getModule()) |
| return Loc(m.srcfile.toChars(), 0, 0); |
| return loc; |
| } |
| |
| final const(char)* locToChars() |
| { |
| return getLoc().toChars(); |
| } |
| |
| override bool equals(const RootObject o) const |
| { |
| if (this == o) |
| return true; |
| if (o.dyncast() != DYNCAST.dsymbol) |
| return false; |
| auto s = cast(Dsymbol)o; |
| // Overload sets don't have an ident |
| // Function-local declarations may have identical names |
| // if they are declared in different scopes |
| if (s && ident && s.ident && ident.equals(s.ident) && localNum == s.localNum) |
| return true; |
| return false; |
| } |
| |
| final bool isAnonymous() const |
| { |
| return ident is null || ident.isAnonymous; |
| } |
| |
| extern(D) private const(char)[] prettyFormatHelper() |
| { |
| const cstr = toPrettyChars(); |
| return '`' ~ cstr.toDString() ~ "`\0"; |
| } |
| |
| static if (__VERSION__ < 2092) |
| { |
| final void error(const ref Loc loc, const(char)* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| .verror(loc, format, ap, kind(), prettyFormatHelper().ptr); |
| va_end(ap); |
| } |
| |
| final void error(const(char)* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| const loc = getLoc(); |
| .verror(loc, format, ap, kind(), prettyFormatHelper().ptr); |
| va_end(ap); |
| } |
| |
| final void deprecation(const ref Loc loc, const(char)* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr); |
| va_end(ap); |
| } |
| |
| final void deprecation(const(char)* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| const loc = getLoc(); |
| .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr); |
| va_end(ap); |
| } |
| } |
| else |
| { |
| pragma(printf) final void error(const ref Loc loc, const(char)* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| .verror(loc, format, ap, kind(), prettyFormatHelper().ptr); |
| va_end(ap); |
| } |
| |
| pragma(printf) final void error(const(char)* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| const loc = getLoc(); |
| .verror(loc, format, ap, kind(), prettyFormatHelper().ptr); |
| va_end(ap); |
| } |
| |
| pragma(printf) final void deprecation(const ref Loc loc, const(char)* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr); |
| va_end(ap); |
| } |
| |
| pragma(printf) final void deprecation(const(char)* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| const loc = getLoc(); |
| .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr); |
| va_end(ap); |
| } |
| } |
| |
| final bool checkDeprecated(const ref Loc loc, Scope* sc) |
| { |
| if (global.params.useDeprecated == DiagnosticReporting.off) |
| return false; |
| if (!this.isDeprecated()) |
| return false; |
| // Don't complain if we're inside a deprecated symbol's scope |
| if (sc.isDeprecated()) |
| return false; |
| // Don't complain if we're inside a template constraint |
| // https://issues.dlang.org/show_bug.cgi?id=21831 |
| if (sc.flags & SCOPE.constraint) |
| return false; |
| |
| const(char)* message = null; |
| for (Dsymbol p = this; p; p = p.parent) |
| { |
| message = p.depdecl ? p.depdecl.getMessage() : null; |
| if (message) |
| break; |
| } |
| if (message) |
| deprecation(loc, "is deprecated - %s", message); |
| else |
| deprecation(loc, "is deprecated"); |
| |
| if (auto ti = sc.parent ? sc.parent.isInstantiated() : null) |
| ti.printInstantiationTrace(Classification.deprecation); |
| else if (auto ti = sc.parent ? sc.parent.isTemplateInstance() : null) |
| ti.printInstantiationTrace(Classification.deprecation); |
| |
| return true; |
| } |
| |
| /********************************** |
| * Determine which Module a Dsymbol is in. |
| */ |
| final Module getModule() |
| { |
| //printf("Dsymbol::getModule()\n"); |
| if (TemplateInstance ti = isInstantiated()) |
| return ti.tempdecl.getModule(); |
| Dsymbol s = this; |
| while (s) |
| { |
| //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); |
| Module m = s.isModule(); |
| if (m) |
| return m; |
| s = s.parent; |
| } |
| return null; |
| } |
| |
| /************************************** |
| * Does this Dsymbol come from a C file? |
| * Returns: |
| * true if it does |
| */ |
| final bool isCsymbol() |
| { |
| if (Module m = getModule()) |
| return m.filetype == FileType.c; |
| return false; |
| } |
| |
| /********************************** |
| * Determine which Module a Dsymbol is in, as far as access rights go. |
| */ |
| final Module getAccessModule() |
| { |
| //printf("Dsymbol::getAccessModule()\n"); |
| if (TemplateInstance ti = isInstantiated()) |
| return ti.tempdecl.getAccessModule(); |
| Dsymbol s = this; |
| while (s) |
| { |
| //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); |
| Module m = s.isModule(); |
| if (m) |
| return m; |
| TemplateInstance ti = s.isTemplateInstance(); |
| if (ti && ti.enclosing) |
| { |
| /* Because of local template instantiation, the parent isn't where the access |
| * rights come from - it's the template declaration |
| */ |
| s = ti.tempdecl; |
| } |
| else |
| s = s.parent; |
| } |
| return null; |
| } |
| |
| /** |
| * `pastMixin` returns the enclosing symbol if this is a template mixin. |
| * |
| * `pastMixinAndNspace` does likewise, additionally skipping over Nspaces that |
| * are mangleOnly. |
| * |
| * See also `parent`, `toParent` and `toParent2`. |
| */ |
| final inout(Dsymbol) pastMixin() inout |
| { |
| //printf("Dsymbol::pastMixin() %s\n", toChars()); |
| if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol()) |
| return this; |
| if (!parent) |
| return null; |
| return parent.pastMixin(); |
| } |
| |
| /********************************** |
| * `parent` field returns a lexically enclosing scope symbol this is a member of. |
| * |
| * `toParent()` returns a logically enclosing scope symbol this is a member of. |
| * It skips over TemplateMixin's. |
| * |
| * `toParent2()` returns an enclosing scope symbol this is living at runtime. |
| * It skips over both TemplateInstance's and TemplateMixin's. |
| * It's used when looking for the 'this' pointer of the enclosing function/class. |
| * |
| * `toParentDecl()` similar to `toParent2()` but always follows the template declaration scope |
| * instead of the instantiation scope. |
| * |
| * `toParentLocal()` similar to `toParentDecl()` but follows the instantiation scope |
| * if a template declaration is non-local i.e. global or static. |
| * |
| * Examples: |
| * --- |
| * module mod; |
| * template Foo(alias a) { mixin Bar!(); } |
| * mixin template Bar() { |
| * public { // VisibilityDeclaration |
| * void baz() { a = 2; } |
| * } |
| * } |
| * void test() { |
| * int v = 1; |
| * alias foo = Foo!(v); |
| * foo.baz(); |
| * assert(v == 2); |
| * } |
| * |
| * // s == FuncDeclaration('mod.test.Foo!().Bar!().baz()') |
| * // s.parent == TemplateMixin('mod.test.Foo!().Bar!()') |
| * // s.toParent() == TemplateInstance('mod.test.Foo!()') |
| * // s.toParent2() == FuncDeclaration('mod.test') |
| * // s.toParentDecl() == Module('mod') |
| * // s.toParentLocal() == FuncDeclaration('mod.test') |
| * --- |
| */ |
| final inout(Dsymbol) toParent() inout |
| { |
| return parent ? parent.pastMixin() : null; |
| } |
| |
| /// ditto |
| final inout(Dsymbol) toParent2() inout |
| { |
| if (!parent || !parent.isTemplateInstance && !parent.isForwardingAttribDeclaration() && !parent.isForwardingScopeDsymbol()) |
| return parent; |
| return parent.toParent2; |
| } |
| |
| /// ditto |
| final inout(Dsymbol) toParentDecl() inout |
| { |
| return toParentDeclImpl(false); |
| } |
| |
| /// ditto |
| final inout(Dsymbol) toParentLocal() inout |
| { |
| return toParentDeclImpl(true); |
| } |
| |
| private inout(Dsymbol) toParentDeclImpl(bool localOnly) inout |
| { |
| auto p = toParent(); |
| if (!p || !p.isTemplateInstance()) |
| return p; |
| auto ti = p.isTemplateInstance(); |
| if (ti.tempdecl && (!localOnly || !(cast(TemplateDeclaration)ti.tempdecl).isstatic)) |
| return ti.tempdecl.toParentDeclImpl(localOnly); |
| return parent.toParentDeclImpl(localOnly); |
| } |
| |
| /** |
| * Returns the declaration scope scope of `this` unless any of the symbols |
| * `p1` or `p2` resides in its enclosing instantiation scope then the |
| * latter is returned. |
| */ |
| final Dsymbol toParentP(Dsymbol p1, Dsymbol p2 = null) |
| { |
| return followInstantiationContext(p1, p2) ? toParent2() : toParentLocal(); |
| } |
| |
| final inout(TemplateInstance) isInstantiated() inout |
| { |
| if (!parent) |
| return null; |
| auto ti = parent.isTemplateInstance(); |
| if (ti && !ti.isTemplateMixin()) |
| return ti; |
| return parent.isInstantiated(); |
| } |
| |
| /*** |
| * Returns true if any of the symbols `p1` or `p2` resides in the enclosing |
| * instantiation scope of `this`. |
| */ |
| final bool followInstantiationContext(Dsymbol p1, Dsymbol p2 = null) |
| { |
| static bool has2This(Dsymbol s) |
| { |
| if (auto f = s.isFuncDeclaration()) |
| return f.hasDualContext(); |
| if (auto ad = s.isAggregateDeclaration()) |
| return ad.vthis2 !is null; |
| return false; |
| } |
| |
| if (has2This(this)) |
| { |
| assert(p1); |
| auto outer = toParent(); |
| while (outer) |
| { |
| auto ti = outer.isTemplateInstance(); |
| if (!ti) |
| break; |
| foreach (oarg; *ti.tiargs) |
| { |
| auto sa = getDsymbol(oarg); |
| if (!sa) |
| continue; |
| sa = sa.toAlias().toParent2(); |
| if (!sa) |
| continue; |
| if (sa == p1) |
| return true; |
| else if (p2 && sa == p2) |
| return true; |
| } |
| outer = ti.tempdecl.toParent(); |
| } |
| return false; |
| } |
| return false; |
| } |
| |
| // Check if this function is a member of a template which has only been |
| // instantiated speculatively, eg from inside is(typeof()). |
| // Return the speculative template instance it is part of, |
| // or NULL if not speculative. |
| final inout(TemplateInstance) isSpeculative() inout |
| { |
| if (!parent) |
| return null; |
| auto ti = parent.isTemplateInstance(); |
| if (ti && ti.gagged) |
| return ti; |
| if (!parent.toParent()) |
| return null; |
| return parent.isSpeculative(); |
| } |
| |
| final Ungag ungagSpeculative() const |
| { |
| uint oldgag = global.gag; |
| if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration()) |
| global.gag = 0; |
| return Ungag(oldgag); |
| } |
| |
| // kludge for template.isSymbol() |
| override final DYNCAST dyncast() const |
| { |
| return DYNCAST.dsymbol; |
| } |
| |
| /************************************* |
| * Do syntax copy of an array of Dsymbol's. |
| */ |
| extern (D) static Dsymbols* arraySyntaxCopy(Dsymbols* a) |
| { |
| Dsymbols* b = null; |
| if (a) |
| { |
| b = a.copy(); |
| for (size_t i = 0; i < b.dim; i++) |
| { |
| (*b)[i] = (*b)[i].syntaxCopy(null); |
| } |
| } |
| return b; |
| } |
| |
| Identifier getIdent() |
| { |
| return ident; |
| } |
| |
| const(char)* toPrettyChars(bool QualifyTypes = false) |
| { |
| if (prettystring && !QualifyTypes) |
| return prettystring; |
| |
| //printf("Dsymbol::toPrettyChars() '%s'\n", toChars()); |
| if (!parent) |
| { |
| auto s = toChars(); |
| if (!QualifyTypes) |
| prettystring = s; |
| return s; |
| } |
| |
| // Computer number of components |
| size_t complength = 0; |
| for (Dsymbol p = this; p; p = p.parent) |
| ++complength; |
| |
| // Allocate temporary array comp[] |
| alias T = const(char)[]; |
| auto compptr = cast(T*)Mem.check(malloc(complength * T.sizeof)); |
| auto comp = compptr[0 .. complength]; |
| |
| // Fill in comp[] and compute length of final result |
| size_t length = 0; |
| int i; |
| for (Dsymbol p = this; p; p = p.parent) |
| { |
| const s = QualifyTypes ? p.toPrettyCharsHelper() : p.toChars(); |
| const len = strlen(s); |
| comp[i] = s[0 .. len]; |
| ++i; |
| length += len + 1; |
| } |
| |
| auto s = cast(char*)mem.xmalloc_noscan(length); |
| auto q = s + length - 1; |
| *q = 0; |
| foreach (j; 0 .. complength) |
| { |
| const t = comp[j].ptr; |
| const len = comp[j].length; |
| q -= len; |
| memcpy(q, t, len); |
| if (q == s) |
| break; |
| *--q = '.'; |
| } |
| free(comp.ptr); |
| if (!QualifyTypes) |
| prettystring = s; |
| return s; |
| } |
| |
| const(char)* kind() const pure nothrow @nogc @safe |
| { |
| return "symbol"; |
| } |
| |
| /********************************* |
| * If this symbol is really an alias for another, |
| * return that other. |
| * If needed, semantic() is invoked due to resolve forward reference. |
| */ |
| Dsymbol toAlias() |
| { |
| return this; |
| } |
| |
| /********************************* |
| * Resolve recursive tuple expansion in eponymous template. |
| */ |
| Dsymbol toAlias2() |
| { |
| return toAlias(); |
| } |
| |
| void addMember(Scope* sc, ScopeDsymbol sds) |
| { |
| //printf("Dsymbol::addMember('%s')\n", toChars()); |
| //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds.toChars()); |
| //printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds.symtab = %p)\n", this, toChars(), sds, sds.symtab); |
| parent = sds; |
| if (isAnonymous()) // no name, so can't add it to symbol table |
| return; |
| |
| if (!sds.symtabInsert(this)) // if name is already defined |
| { |
| if (isAliasDeclaration() && !_scope) |
| setScope(sc); |
| Dsymbol s2 = sds.symtabLookup(this,ident); |
| /* https://issues.dlang.org/show_bug.cgi?id=17434 |
| * |
| * If we are trying to add an import to the symbol table |
| * that has already been introduced, then keep the one with |
| * larger visibility. This is fine for imports because if |
| * we have multiple imports of the same file, if a single one |
| * is public then the symbol is reachable. |
| */ |
| if (auto i1 = isImport()) |
| { |
| if (auto i2 = s2.isImport()) |
| { |
| if (sc.explicitVisibility && sc.visibility > i2.visibility) |
| sds.symtab.update(this); |
| } |
| } |
| |
| // If using C tag/prototype/forward declaration rules |
| if (sc.flags & SCOPE.Cfile && !this.isImport()) |
| { |
| if (handleTagSymbols(*sc, this, s2, sds)) |
| return; |
| if (handleSymbolRedeclarations(*sc, this, s2, sds)) |
| return; |
| |
| sds.multiplyDefined(Loc.initial, this, s2); // ImportC doesn't allow overloading |
| errors = true; |
| return; |
| } |
| |
| if (!s2.overloadInsert(this)) |
| { |
| sds.multiplyDefined(Loc.initial, this, s2); |
| errors = true; |
| } |
| } |
| if (sds.isAggregateDeclaration() || sds.isEnumDeclaration()) |
| { |
| if (ident == Id.__sizeof || |
| !(sc && sc.flags & SCOPE.Cfile) && (ident == Id.__xalignof || ident == Id._mangleof)) |
| { |
| error("`.%s` property cannot be redefined", ident.toChars()); |
| errors = true; |
| } |
| } |
| } |
| |
| /************************************* |
| * Set scope for future semantic analysis so we can |
| * deal better with forward references. |
| */ |
| void setScope(Scope* sc) |
| { |
| //printf("Dsymbol::setScope() %p %s, %p stc = %llx\n", this, toChars(), sc, sc.stc); |
| if (!sc.nofree) |
| sc.setNoFree(); // may need it even after semantic() finishes |
| _scope = sc; |
| if (sc.depdecl) |
| depdecl = sc.depdecl; |
| if (!userAttribDecl) |
| userAttribDecl = sc.userAttribDecl; |
| } |
| |
| void importAll(Scope* sc) |
| { |
| } |
| |
| /********************************************* |
| * Search for ident as member of s. |
| * Params: |
| * loc = location to print for error messages |
| * ident = identifier to search for |
| * flags = IgnoreXXXX |
| * Returns: |
| * null if not found |
| */ |
| Dsymbol search(const ref Loc loc, Identifier ident, int flags = IgnoreNone) |
| { |
| //printf("Dsymbol::search(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars()); |
| return null; |
| } |
| |
| extern (D) final Dsymbol search_correct(Identifier ident) |
| { |
| /*************************************************** |
| * Search for symbol with correct spelling. |
| */ |
| extern (D) Dsymbol symbol_search_fp(const(char)[] seed, out int cost) |
| { |
| /* If not in the lexer's string table, it certainly isn't in the symbol table. |
| * Doing this first is a lot faster. |
| */ |
| if (!seed.length) |
| return null; |
| Identifier id = Identifier.lookup(seed); |
| if (!id) |
| return null; |
| cost = 0; // all the same cost |
| Dsymbol s = this; |
| Module.clearCache(); |
| return s.search(Loc.initial, id, IgnoreErrors); |
| } |
| |
| if (global.gag) |
| return null; // don't do it for speculative compiles; too time consuming |
| // search for exact name first |
| if (auto s = search(Loc.initial, ident, IgnoreErrors)) |
| return s; |
| return speller!symbol_search_fp(ident.toString()); |
| } |
| |
| /*************************************** |
| * Search for identifier id as a member of `this`. |
| * `id` may be a template instance. |
| * |
| * Params: |
| * loc = location to print the error messages |
| * sc = the scope where the symbol is located |
| * id = the id of the symbol |
| * flags = the search flags which can be `SearchLocalsOnly` or `IgnorePrivateImports` |
| * |
| * Returns: |
| * symbol found, NULL if not |
| */ |
| extern (D) final Dsymbol searchX(const ref Loc loc, Scope* sc, RootObject id, int flags) |
| { |
| //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars()); |
| Dsymbol s = toAlias(); |
| Dsymbol sm; |
| if (Declaration d = s.isDeclaration()) |
| { |
| if (d.inuse) |
| { |
| .error(loc, "circular reference to `%s`", d.toPrettyChars()); |
| return null; |
| } |
| } |
| switch (id.dyncast()) |
| { |
| case DYNCAST.identifier: |
| sm = s.search(loc, cast(Identifier)id, flags); |
| break; |
| case DYNCAST.dsymbol: |
| { |
| // It's a template instance |
| //printf("\ttemplate instance id\n"); |
| Dsymbol st = cast(Dsymbol)id; |
| TemplateInstance ti = st.isTemplateInstance(); |
| sm = s.search(loc, ti.name); |
| if (!sm) |
| return null; |
| sm = sm.toAlias(); |
| TemplateDeclaration td = sm.isTemplateDeclaration(); |
| if (!td) |
| { |
| .error(loc, "`%s.%s` is not a template, it is a %s", s.toPrettyChars(), ti.name.toChars(), sm.kind()); |
| return null; |
| } |
| ti.tempdecl = td; |
| if (!ti.semanticRun) |
| ti.dsymbolSemantic(sc); |
| sm = ti.toAlias(); |
| break; |
| } |
| case DYNCAST.type: |
| case DYNCAST.expression: |
| default: |
| assert(0); |
| } |
| return sm; |
| } |
| |
| bool overloadInsert(Dsymbol s) |
| { |
| //printf("Dsymbol::overloadInsert('%s')\n", s.toChars()); |
| return false; |
| } |
| |
| /********************************* |
| * Returns: |
| * SIZE_INVALID when the size cannot be determined |
| */ |
| uinteger_t size(const ref Loc loc) |
| { |
| error("Dsymbol `%s` has no size", toChars()); |
| return SIZE_INVALID; |
| } |
| |
| bool isforwardRef() |
| { |
| return false; |
| } |
| |
| // is a 'this' required to access the member |
| inout(AggregateDeclaration) isThis() inout |
| { |
| return null; |
| } |
| |
| // is Dsymbol exported? |
| bool isExport() const |
| { |
| return false; |
| } |
| |
| // is Dsymbol imported? |
| bool isImportedSymbol() const |
| { |
| return false; |
| } |
| |
| // is Dsymbol deprecated? |
| bool isDeprecated() @safe @nogc pure nothrow const |
| { |
| return false; |
| } |
| |
| bool isOverloadable() const |
| { |
| return false; |
| } |
| |
| // is this a LabelDsymbol()? |
| LabelDsymbol isLabel() |
| { |
| return null; |
| } |
| |
| /// Returns an AggregateDeclaration when toParent() is that. |
| final inout(AggregateDeclaration) isMember() inout |
| { |
| //printf("Dsymbol::isMember() %s\n", toChars()); |
| auto p = toParent(); |
| //printf("parent is %s %s\n", p.kind(), p.toChars()); |
| return p ? p.isAggregateDeclaration() : null; |
| } |
| |
| /// Returns an AggregateDeclaration when toParent2() is that. |
| final inout(AggregateDeclaration) isMember2() inout |
| { |
| //printf("Dsymbol::isMember2() '%s'\n", toChars()); |
| auto p = toParent2(); |
| //printf("parent is %s %s\n", p.kind(), p.toChars()); |
| return p ? p.isAggregateDeclaration() : null; |
| } |
| |
| /// Returns an AggregateDeclaration when toParentDecl() is that. |
| final inout(AggregateDeclaration) isMemberDecl() inout |
| { |
| //printf("Dsymbol::isMemberDecl() '%s'\n", toChars()); |
| auto p = toParentDecl(); |
| //printf("parent is %s %s\n", p.kind(), p.toChars()); |
| return p ? p.isAggregateDeclaration() : null; |
| } |
| |
| /// Returns an AggregateDeclaration when toParentLocal() is that. |
| final inout(AggregateDeclaration) isMemberLocal() inout |
| { |
| //printf("Dsymbol::isMemberLocal() '%s'\n", toChars()); |
| auto p = toParentLocal(); |
| //printf("parent is %s %s\n", p.kind(), p.toChars()); |
| return p ? p.isAggregateDeclaration() : null; |
| } |
| |
| // is this a member of a ClassDeclaration? |
| final ClassDeclaration isClassMember() |
| { |
| auto ad = isMember(); |
| return ad ? ad.isClassDeclaration() : null; |
| } |
| |
| // is this a type? |
| Type getType() |
| { |
| return null; |
| } |
| |
| // need a 'this' pointer? |
| bool needThis() |
| { |
| return false; |
| } |
| |
| /************************************* |
| */ |
| Visibility visible() pure nothrow @nogc @safe |
| { |
| return Visibility(Visibility.Kind.public_); |
| } |
| |
| /************************************** |
| * Copy the syntax. |
| * Used for template instantiations. |
| * If s is NULL, allocate the new object, otherwise fill it in. |
| */ |
| Dsymbol syntaxCopy(Dsymbol s) |
| { |
| printf("%s %s\n", kind(), toChars()); |
| assert(0); |
| } |
| |
| /************************************** |
| * Determine if this symbol is only one. |
| * Returns: |
| * false, *ps = NULL: There are 2 or more symbols |
| * true, *ps = NULL: There are zero symbols |
| * true, *ps = symbol: The one and only one symbol |
| */ |
| bool oneMember(Dsymbol* ps, Identifier ident) |
| { |
| //printf("Dsymbol::oneMember()\n"); |
| *ps = this; |
| return true; |
| } |
| |
| /***************************************** |
| * Same as Dsymbol::oneMember(), but look at an array of Dsymbols. |
| */ |
| extern (D) static bool oneMembers(Dsymbols* members, Dsymbol* ps, Identifier ident) |
| { |
| //printf("Dsymbol::oneMembers() %d\n", members ? members.dim : 0); |
| Dsymbol s = null; |
| if (!members) |
| { |
| *ps = null; |
| return true; |
| } |
| |
| for (size_t i = 0; i < members.dim; i++) |
| { |
| Dsymbol sx = (*members)[i]; |
| bool x = sx.oneMember(ps, ident); |
| //printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps); |
| if (!x) |
| { |
| //printf("\tfalse 1\n"); |
| assert(*ps is null); |
| return false; |
| } |
| if (*ps) |
| { |
| assert(ident); |
| if (!(*ps).ident || !(*ps).ident.equals(ident)) |
| continue; |
| if (!s) |
| s = *ps; |
| else if (s.isOverloadable() && (*ps).isOverloadable()) |
| { |
| // keep head of overload set |
| FuncDeclaration f1 = s.isFuncDeclaration(); |
| FuncDeclaration f2 = (*ps).isFuncDeclaration(); |
| if (f1 && f2) |
| { |
| assert(!f1.isFuncAliasDeclaration()); |
| assert(!f2.isFuncAliasDeclaration()); |
| for (; f1 != f2; f1 = f1.overnext0) |
| { |
| if (f1.overnext0 is null) |
| { |
| f1.overnext0 = f2; |
| break; |
| } |
| } |
| } |
| } |
| else // more than one symbol |
| { |
| *ps = null; |
| //printf("\tfalse 2\n"); |
| return false; |
| } |
| } |
| } |
| *ps = s; // s is the one symbol, null if none |
| //printf("\ttrue\n"); |
| return true; |
| } |
| |
| void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) |
| { |
| } |
| |
| /***************************************** |
| * Is Dsymbol a variable that contains pointers? |
| */ |
| bool hasPointers() |
| { |
| //printf("Dsymbol::hasPointers() %s\n", toChars()); |
| return false; |
| } |
| |
| bool hasStaticCtorOrDtor() |
| { |
| //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); |
| return false; |
| } |
| |
| void addLocalClass(ClassDeclarations*) |
| { |
| } |
| |
| void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories) |
| { |
| } |
| |
| void checkCtorConstInit() |
| { |
| } |
| |
| /**************************************** |
| * Add documentation comment to Dsymbol. |
| * Ignore NULL comments. |
| */ |
| void addComment(const(char)* comment) |
| { |
| if (!comment || !*comment) |
| return; |
| |
| //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars()); |
| void* h = cast(void*)this; // just the pointer is the key |
| auto p = h in commentHashTable; |
| if (!p) |
| { |
| commentHashTable[h] = comment; |
| return; |
| } |
| if (strcmp(*p, comment) != 0) |
| { |
| // Concatenate the two |
| *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true); |
| } |
| } |
| |
| /// get documentation comment for this Dsymbol |
| final const(char)* comment() |
| { |
| //printf("getcomment: %p '%s'\n", this, this.toChars()); |
| if (auto p = cast(void*)this in commentHashTable) |
| { |
| //printf("comment: '%s'\n", *p); |
| return *p; |
| } |
| return null; |
| } |
| |
| /* Shell around addComment() to avoid disruption for the moment */ |
| final void comment(const(char)* comment) { addComment(comment); } |
| |
| private extern (D) __gshared const(char)*[void*] commentHashTable; |
| |
| |
| /********************************** |
| * Get ddoc unittest associated with this symbol. |
| * (only use this with ddoc) |
| * Returns: ddoc unittest, null if none |
| */ |
| final UnitTestDeclaration ddocUnittest() |
| { |
| if (auto p = cast(void*)this in ddocUnittestHashTable) |
| return *p; |
| return null; |
| } |
| |
| /********************************** |
| * Set ddoc unittest associated with this symbol. |
| */ |
| final void ddocUnittest(UnitTestDeclaration utd) |
| { |
| ddocUnittestHashTable[cast(void*)this] = utd; |
| } |
| |
| private extern (D) __gshared UnitTestDeclaration[void*] ddocUnittestHashTable; |
| |
| |
| /**************************************** |
| * Returns true if this symbol is defined in a non-root module without instantiation. |
| */ |
| final bool inNonRoot() |
| { |
| Dsymbol s = parent; |
| for (; s; s = s.toParent()) |
| { |
| if (auto ti = s.isTemplateInstance()) |
| { |
| return false; |
| } |
| if (auto m = s.isModule()) |
| { |
| if (!m.isRoot()) |
| return true; |
| break; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Deinitializes the global state of the compiler. |
| * |
| * This can be used to restore the state set by `_init` to its original |
| * state. |
| */ |
| static void deinitialize() |
| { |
| commentHashTable = commentHashTable.init; |
| ddocUnittestHashTable = ddocUnittestHashTable.init; |
| } |
| |
| /************ |
| */ |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| |
| pure nothrow @safe @nogc: |
| |
| // Eliminate need for dynamic_cast |
| inout(Package) isPackage() inout { return null; } |
| inout(Module) isModule() inout { return null; } |
| inout(EnumMember) isEnumMember() inout { return null; } |
| inout(TemplateDeclaration) isTemplateDeclaration() inout { return null; } |
| inout(TemplateInstance) isTemplateInstance() inout { return null; } |
| inout(TemplateMixin) isTemplateMixin() inout { return null; } |
| inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { return null; } |
| inout(Nspace) isNspace() inout { return null; } |
| inout(Declaration) isDeclaration() inout { return null; } |
| inout(StorageClassDeclaration) isStorageClassDeclaration() inout { return null; } |
| inout(ExpressionDsymbol) isExpressionDsymbol() inout { return null; } |
| inout(AliasAssign) isAliasAssign() inout { return null; } |
| inout(ThisDeclaration) isThisDeclaration() inout { return null; } |
| inout(BitFieldDeclaration) isBitFieldDeclaration() inout { return null; } |
| inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout { return null; } |
| inout(TupleDeclaration) isTupleDeclaration() inout { return null; } |
| inout(AliasDeclaration) isAliasDeclaration() inout { return null; } |
| inout(AggregateDeclaration) isAggregateDeclaration() inout { return null; } |
| inout(FuncDeclaration) isFuncDeclaration() inout { return null; } |
| inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout { return null; } |
| inout(OverDeclaration) isOverDeclaration() inout { return null; } |
| inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout { return null; } |
| inout(CtorDeclaration) isCtorDeclaration() inout { return null; } |
| inout(PostBlitDeclaration) isPostBlitDeclaration() inout { return null; } |
| inout(DtorDeclaration) isDtorDeclaration() inout { return null; } |
| inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout { return null; } |
| inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout { return null; } |
| inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout { return null; } |
| inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout { return null; } |
| inout(InvariantDeclaration) isInvariantDeclaration() inout { return null; } |
| inout(UnitTestDeclaration) isUnitTestDeclaration() inout { return null; } |
| inout(NewDeclaration) isNewDeclaration() inout { return null; } |
| inout(VarDeclaration) isVarDeclaration() inout { return null; } |
| inout(VersionSymbol) isVersionSymbol() inout { return null; } |
| inout(DebugSymbol) isDebugSymbol() inout { return null; } |
| inout(ClassDeclaration) isClassDeclaration() inout { return null; } |
| inout(StructDeclaration) isStructDeclaration() inout { return null; } |
| inout(UnionDeclaration) isUnionDeclaration() inout { return null; } |
| inout(InterfaceDeclaration) isInterfaceDeclaration() inout { return null; } |
| inout(ScopeDsymbol) isScopeDsymbol() inout { return null; } |
| inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout { return null; } |
| inout(WithScopeSymbol) isWithScopeSymbol() inout { return null; } |
| inout(ArrayScopeSymbol) isArrayScopeSymbol() inout { return null; } |
| inout(Import) isImport() inout { return null; } |
| inout(EnumDeclaration) isEnumDeclaration() inout { return null; } |
| inout(SymbolDeclaration) isSymbolDeclaration() inout { return null; } |
| inout(AttribDeclaration) isAttribDeclaration() inout { return null; } |
| inout(AnonDeclaration) isAnonDeclaration() inout { return null; } |
| inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; } |
| inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return null; } |
| inout(OverloadSet) isOverloadSet() inout { return null; } |
| inout(CompileDeclaration) isCompileDeclaration() inout { return null; } |
| inout(StaticAssert) isStaticAssert() inout { return null; } |
| } |
| |
| /*********************************************************** |
| * Dsymbol that generates a scope |
| */ |
| extern (C++) class ScopeDsymbol : Dsymbol |
| { |
| Dsymbols* members; // all Dsymbol's in this scope |
| DsymbolTable symtab; // members[] sorted into table |
| uint endlinnum; // the linnumber of the statement after the scope (0 if unknown) |
| |
| private: |
| /// symbols whose members have been imported, i.e. imported modules and template mixins |
| Dsymbols* importedScopes; |
| Visibility.Kind* visibilities; // array of Visibility.Kind, one for each import |
| |
| import dmd.root.bitarray; |
| BitArray accessiblePackages, privateAccessiblePackages;// whitelists of accessible (imported) packages |
| |
| public: |
| final extern (D) this() nothrow |
| { |
| } |
| |
| final extern (D) this(Identifier ident) nothrow |
| { |
| super(ident); |
| } |
| |
| final extern (D) this(const ref Loc loc, Identifier ident) nothrow |
| { |
| super(loc, ident); |
| } |
| |
| override ScopeDsymbol syntaxCopy(Dsymbol s) |
| { |
| //printf("ScopeDsymbol::syntaxCopy('%s')\n", toChars()); |
| ScopeDsymbol sds = s ? cast(ScopeDsymbol)s : new ScopeDsymbol(ident); |
| sds.comment = comment; |
| sds.members = arraySyntaxCopy(members); |
| sds.endlinnum = endlinnum; |
| return sds; |
| } |
| |
| /***************************************** |
| * This function is #1 on the list of functions that eat cpu time. |
| * Be very, very careful about slowing it down. |
| */ |
| override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) |
| { |
| //printf("%s.ScopeDsymbol::search(ident='%s', flags=x%x)\n", toChars(), ident.toChars(), flags); |
| //if (strcmp(ident.toChars(),"c") == 0) *(char*)0=0; |
| |
| // Look in symbols declared in this module |
| if (symtab && !(flags & SearchImportsOnly)) |
| { |
| //printf(" look in locals\n"); |
| auto s1 = symtab.lookup(ident); |
| if (s1) |
| { |
| //printf("\tfound in locals = '%s.%s'\n",toChars(),s1.toChars()); |
| return s1; |
| } |
| } |
| //printf(" not found in locals\n"); |
| |
| // Look in imported scopes |
| if (!importedScopes) |
| return null; |
| |
| //printf(" look in imports\n"); |
| Dsymbol s = null; |
| OverloadSet a = null; |
| // Look in imported modules |
| for (size_t i = 0; i < importedScopes.dim; i++) |
| { |
| // If private import, don't search it |
| if ((flags & IgnorePrivateImports) && visibilities[i] == Visibility.Kind.private_) |
| continue; |
| int sflags = flags & (IgnoreErrors | IgnoreAmbiguous); // remember these in recursive searches |
| Dsymbol ss = (*importedScopes)[i]; |
| //printf("\tscanning import '%s', visibilities = %d, isModule = %p, isImport = %p\n", ss.toChars(), visibilities[i], ss.isModule(), ss.isImport()); |
| |
| if (ss.isModule()) |
| { |
| if (flags & SearchLocalsOnly) |
| continue; |
| } |
| else // mixin template |
| { |
| if (flags & SearchImportsOnly) |
| continue; |
| |
| sflags |= SearchLocalsOnly; |
| } |
| |
| /* Don't find private members if ss is a module |
| */ |
| Dsymbol s2 = ss.search(loc, ident, sflags | (ss.isModule() ? IgnorePrivateImports : IgnoreNone)); |
| import dmd.access : symbolIsVisible; |
| if (!s2 || !(flags & IgnoreSymbolVisibility) && !symbolIsVisible(this, s2)) |
| continue; |
| if (!s) |
| { |
| s = s2; |
| if (s && s.isOverloadSet()) |
| a = mergeOverloadSet(ident, a, s); |
| } |
| else if (s2 && s != s2) |
| { |
| if (s.toAlias() == s2.toAlias() || s.getType() == s2.getType() && s.getType()) |
| { |
| /* After following aliases, we found the same |
| * symbol, so it's not an ambiguity. But if one |
| * alias is deprecated or less accessible, prefer |
| * the other. |
| */ |
| if (s.isDeprecated() || s.visible() < s2.visible() && s2.visible().kind != Visibility.Kind.none) |
| s = s2; |
| } |
| else |
| { |
| /* Two imports of the same module should be regarded as |
| * the same. |
| */ |
| Import i1 = s.isImport(); |
| Import i2 = s2.isImport(); |
| if (!(i1 && i2 && (i1.mod == i2.mod || (!i1.parent.isImport() && !i2.parent.isImport() && i1.ident.equals(i2.ident))))) |
| { |
| /* https://issues.dlang.org/show_bug.cgi?id=8668 |
| * Public selective import adds AliasDeclaration in module. |
| * To make an overload set, resolve aliases in here and |
| * get actual overload roots which accessible via s and s2. |
| */ |
| s = s.toAlias(); |
| s2 = s2.toAlias(); |
| /* If both s2 and s are overloadable (though we only |
| * need to check s once) |
| */ |
| |
| auto so2 = s2.isOverloadSet(); |
| if ((so2 || s2.isOverloadable()) && (a || s.isOverloadable())) |
| { |
| if (symbolIsVisible(this, s2)) |
| { |
| a = mergeOverloadSet(ident, a, s2); |
| } |
| if (!symbolIsVisible(this, s)) |
| s = s2; |
| continue; |
| } |
| |
| /* Two different overflow sets can have the same members |
| * https://issues.dlang.org/show_bug.cgi?id=16709 |
| */ |
| auto so = s.isOverloadSet(); |
| if (so && so2) |
| { |
| if (so.a.length == so2.a.length) |
| { |
| foreach (j; 0 .. so.a.length) |
| { |
| if (so.a[j] !is so2.a[j]) |
| goto L1; |
| } |
| continue; // the same |
| L1: |
| { } // different |
| } |
| } |
| |
| if (flags & IgnoreAmbiguous) // if return NULL on ambiguity |
| return null; |
| if (!(flags & IgnoreErrors)) |
| ScopeDsymbol.multiplyDefined(loc, s, s2); |
| break; |
| } |
| } |
| } |
| } |
| if (s) |
| { |
| /* Build special symbol if we had multiple finds |
| */ |
| if (a) |
| { |
| if (!s.isOverloadSet()) |
| a = mergeOverloadSet(ident, a, s); |
| s = a; |
| } |
| //printf("\tfound in imports %s.%s\n", toChars(), s.toChars()); |
| return s; |
| } |
| //printf(" not found in imports\n"); |
| return null; |
| } |
| |
| extern (D) private OverloadSet mergeOverloadSet(Identifier ident, OverloadSet os, Dsymbol s) |
| { |
| if (!os) |
| { |
| os = new OverloadSet(ident); |
| os.parent = this; |
| } |
| if (OverloadSet os2 = s.isOverloadSet()) |
| { |
| // Merge the cross-module overload set 'os2' into 'os' |
| if (os.a.dim == 0) |
| { |
| os.a.setDim(os2.a.dim); |
| memcpy(os.a.tdata(), os2.a.tdata(), (os.a[0]).sizeof * os2.a.dim); |
| } |
| else |
| { |
| for (size_t i = 0; i < os2.a.dim; i++) |
| { |
| os = mergeOverloadSet(ident, os, os2.a[i]); |
| } |
| } |
| } |
| else |
| { |
| assert(s.isOverloadable()); |
| /* Don't add to os[] if s is alias of previous sym |
| */ |
| for (size_t j = 0; j < os.a.dim; j++) |
| { |
| Dsymbol s2 = os.a[j]; |
| if (s.toAlias() == s2.toAlias()) |
| { |
| if (s2.isDeprecated() || (s2.visible() < s.visible() && s.visible().kind != Visibility.Kind.none)) |
| { |
| os.a[j] = s; |
| } |
| goto Lcontinue; |
| } |
| } |
| os.push(s); |
| Lcontinue: |
| } |
| return os; |
| } |
| |
| void importScope(Dsymbol s, Visibility visibility) nothrow |
| { |
| //printf("%s.ScopeDsymbol::importScope(%s, %d)\n", toChars(), s.toChars(), visibility); |
| // No circular or redundant import's |
| if (s != this) |
| { |
| if (!importedScopes) |
| importedScopes = new Dsymbols(); |
| else |
| { |
| for (size_t i = 0; i < importedScopes.dim; i++) |
| { |
| Dsymbol ss = (*importedScopes)[i]; |
| if (ss == s) // if already imported |
| { |
| if (visibility.kind > visibilities[i]) |
| visibilities[i] = visibility.kind; // upgrade access |
| return; |
| } |
| } |
| } |
| importedScopes.push(s); |
| visibilities = cast(Visibility.Kind*)mem.xrealloc(visibilities, importedScopes.dim * (visibilities[0]).sizeof); |
| visibilities[importedScopes.dim - 1] = visibility.kind; |
| } |
| } |
| |
| extern (D) final void addAccessiblePackage(Package p, Visibility visibility) nothrow |
| { |
| auto pary = visibility.kind == Visibility.Kind.private_ ? &privateAccessiblePackages : &accessiblePackages; |
| if (pary.length <= p.tag) |
| pary.length = p.tag + 1; |
| (*pary)[p.tag] = true; |
| } |
| |
| bool isPackageAccessible(Package p, Visibility visibility, int flags = 0) nothrow |
| { |
| if (p.tag < accessiblePackages.length && accessiblePackages[p.tag] || |
| visibility.kind == Visibility.Kind.private_ && p.tag < privateAccessiblePackages.length && privateAccessiblePackages[p.tag]) |
| return true; |
| foreach (i, ss; importedScopes ? (*importedScopes)[] : null) |
| { |
| // only search visible scopes && imported modules should ignore private imports |
| if (visibility.kind <= visibilities[i] && |
| ss.isScopeDsymbol.isPackageAccessible(p, visibility, IgnorePrivateImports)) |
| return true; |
| } |
| return false; |
| } |
| |
| override final bool isforwardRef() nothrow |
| { |
| return (members is null); |
| } |
| |
| static void multiplyDefined(const ref Loc loc, Dsymbol s1, Dsymbol s2) |
| { |
| version (none) |
| { |
| printf("ScopeDsymbol::multiplyDefined()\n"); |
| printf("s1 = %p, '%s' kind = '%s', parent = %s\n", s1, s1.toChars(), s1.kind(), s1.parent ? s1.parent.toChars() : ""); |
| printf("s2 = %p, '%s' kind = '%s', parent = %s\n", s2, s2.toChars(), s2.kind(), s2.parent ? s2.parent.toChars() : ""); |
| } |
| if (loc.isValid()) |
| { |
| .error(loc, "%s `%s` at %s conflicts with %s `%s` at %s", |
| s1.kind(), s1.toPrettyChars(), s1.locToChars(), |
| s2.kind(), s2.toPrettyChars(), s2.locToChars()); |
| |
| static if (0) |
| { |
| if (auto so = s1.isOverloadSet()) |
| { |
| printf("first %p:\n", so); |
| foreach (s; so.a[]) |
| { |
| printf(" %p %s `%s` at %s\n", s, s.kind(), s.toPrettyChars(), s.locToChars()); |
| } |
| } |
| if (auto so = s2.isOverloadSet()) |
| { |
| printf("second %p:\n", so); |
| foreach (s; so.a[]) |
| { |
| printf(" %p %s `%s` at %s\n", s, s.kind(), s.toPrettyChars(), s.locToChars()); |
| } |
| } |
| } |
| } |
| else |
| { |
| s1.error(s1.loc, "conflicts with %s `%s` at %s", s2.kind(), s2.toPrettyChars(), s2.locToChars()); |
| } |
| } |
| |
| override const(char)* kind() const |
| { |
| return "ScopeDsymbol"; |
| } |
| |
| /******************************************* |
| * Look for member of the form: |
| * const(MemberInfo)[] getMembers(string); |
| * Returns NULL if not found |
| */ |
| final FuncDeclaration findGetMembers() |
| { |
| Dsymbol s = search_function(this, Id.getmembers); |
| FuncDeclaration fdx = s ? s.isFuncDeclaration() : null; |
| version (none) |
| { |
| // Finish |
| __gshared TypeFunction tfgetmembers; |
| if (!tfgetmembers) |
| { |
| Scope sc; |
| auto parameters = new Parameters(); |
| Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null); |
| parameters.push(p); |
| Type tret = null; |
| tfgetmembers = new TypeFunction(parameters, tret, VarArg.none, LINK.d); |
| tfgetmembers = cast(TypeFunction)tfgetmembers.dsymbolSemantic(Loc.initial, &sc); |
| } |
| if (fdx) |
| fdx = fdx.overloadExactMatch(tfgetmembers); |
| } |
| if (fdx && fdx.isVirtual()) |
| fdx = null; |
| return fdx; |
| } |
| |
| /******************************** |
| * Insert Dsymbol in table. |
| * Params: |
| * s = symbol to add |
| * Returns: |
| * null if already in table, `s` if inserted |
| */ |
| Dsymbol symtabInsert(Dsymbol s) nothrow |
| { |
| return symtab.insert(s); |
| } |
| |
| /**************************************** |
| * Look up identifier in symbol table. |
| * Params: |
| * s = symbol |
| * id = identifier to look up |
| * Returns: |
| * Dsymbol if found, null if not |
| */ |
| Dsymbol symtabLookup(Dsymbol s, Identifier id) nothrow |
| { |
| return symtab.lookup(id); |
| } |
| |
| /**************************************** |
| * Return true if any of the members are static ctors or static dtors, or if |
| * any members have members that are. |
| */ |
| override bool hasStaticCtorOrDtor() |
| { |
| if (members) |
| { |
| for (size_t i = 0; i < members.dim; i++) |
| { |
| Dsymbol member = (*members)[i]; |
| if (member.hasStaticCtorOrDtor()) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| extern (D) alias ForeachDg = int delegate(size_t idx, Dsymbol s); |
| |
| /*************************************** |
| * Expands attribute declarations in members in depth first |
| * order. Calls dg(size_t symidx, Dsymbol *sym) for each |
| * member. |
| * If dg returns !=0, stops and returns that value else returns 0. |
| * Use this function to avoid the O(N + N^2/2) complexity of |
| * calculating dim and calling N times getNth. |
| * Returns: |
| * last value returned by dg() |
| */ |
| extern (D) static int _foreach(Scope* sc, Dsymbols* members, scope ForeachDg dg, size_t* pn = null) |
| { |
| assert(dg); |
| if (!members) |
| return 0; |
| size_t n = pn ? *pn : 0; // take over index |
| int result = 0; |
| foreach (size_t i; 0 .. members.dim) |
| { |
| Dsymbol s = (*members)[i]; |
| if (AttribDeclaration a = s.isAttribDeclaration()) |
| result = _foreach(sc, a.include(sc), dg, &n); |
| else if (TemplateMixin tm = s.isTemplateMixin()) |
| result = _foreach(sc, tm.members, dg, &n); |
| else if (s.isTemplateInstance()) |
| { |
| } |
| else if (s.isUnitTestDeclaration()) |
| { |
| } |
| else |
| result = dg(n++, s); |
| if (result) |
| break; |
| } |
| if (pn) |
| *pn = n; // update index |
| return result; |
| } |
| |
| override final inout(ScopeDsymbol) isScopeDsymbol() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * With statement scope |
| */ |
| extern (C++) final class WithScopeSymbol : ScopeDsymbol |
| { |
| WithStatement withstate; |
| |
| extern (D) this(WithStatement withstate) nothrow |
| { |
| this.withstate = withstate; |
| } |
| |
| override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) |
| { |
| //printf("WithScopeSymbol.search(%s)\n", ident.toChars()); |
| if (flags & SearchImportsOnly) |
| return null; |
| // Acts as proxy to the with class declaration |
| Dsymbol s = null; |
| Expression eold = null; |
| for (Expression e = withstate.exp; e && e != eold; e = resolveAliasThis(_scope, e, true)) |
| { |
| if (e.op == EXP.scope_) |
| { |
| s = (cast(ScopeExp)e).sds; |
| } |
| else if (e.op == EXP.type) |
| { |
| s = e.type.toDsymbol(null); |
| } |
| else |
| { |
| Type t = e.type.toBasetype(); |
| s = t.toDsymbol(null); |
| } |
| if (s) |
| { |
| s = s.search(loc, ident, flags); |
| if (s) |
| return s; |
| } |
| eold = e; |
| } |
| return null; |
| } |
| |
| override inout(WithScopeSymbol) isWithScopeSymbol() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Array Index/Slice scope |
| */ |
| extern (C++) final class ArrayScopeSymbol : ScopeDsymbol |
| { |
| // either a SliceExp, an IndexExp, an ArrayExp, a TypeTuple or a TupleDeclaration. |
| // Discriminated using DYNCAST and, for expressions, also EXP |
| private RootObject arrayContent; |
| Scope* sc; |
| |
| extern (D) this(Scope* sc, Expression exp) nothrow |
| { |
| super(exp.loc, null); |
| assert(exp.op == EXP.index || exp.op == EXP.slice || exp.op == EXP.array); |
| this.sc = sc; |
| this.arrayContent = exp; |
| } |
| |
| extern (D) this(Scope* sc, TypeTuple type) nothrow |
| { |
| this.sc = sc; |
| this.arrayContent = type; |
| } |
| |
| extern (D) this(Scope* sc, TupleDeclaration td) nothrow |
| { |
| this.sc = sc; |
| this.arrayContent = td; |
| } |
| |
| /// This override is used to solve `$` |
| override Dsymbol search(const ref Loc loc, Identifier ident, int flags = IgnoreNone) |
| { |
| //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident.toChars(), flags); |
| if (ident != Id.dollar) |
| return null; |
| |
| VarDeclaration* pvar; |
| Expression ce; |
| |
| static Dsymbol dollarFromTypeTuple(const ref Loc loc, TypeTuple tt, Scope* sc) |
| { |
| |
| /* $ gives the number of type entries in the type tuple |
| */ |
| auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null); |
| Expression e = new IntegerExp(Loc.initial, tt.arguments.dim, Type.tsize_t); |
| v._init = new ExpInitializer(Loc.initial, e); |
| v.storage_class |= STC.temp | STC.static_ | STC.const_; |
| v.dsymbolSemantic(sc); |
| return v; |
| } |
| |
| const DYNCAST kind = arrayContent.dyncast(); |
| if (kind == DYNCAST.dsymbol) |
| { |
| TupleDeclaration td = cast(TupleDeclaration) arrayContent; |
| /* $ gives the number of elements in the tuple |
| */ |
| auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null); |
| Expression e = new IntegerExp(Loc.initial, td.objects.dim, Type.tsize_t); |
| v._init = new ExpInitializer(Loc.initial, e); |
| v.storage_class |= STC.temp | STC.static_ | STC.const_; |
| v.dsymbolSemantic(sc); |
| return v; |
| } |
| if (kind == DYNCAST.type) |
| { |
| return dollarFromTypeTuple(loc, cast(TypeTuple) arrayContent, sc); |
| } |
| Expression exp = cast(Expression) arrayContent; |
| if (auto ie = exp.isIndexExp()) |
| { |
| /* array[index] where index is some function of $ |
| */ |
| pvar = &ie.lengthVar; |
| ce = ie.e1; |
| } |
| else if (auto se = exp.isSliceExp()) |
| { |
| /* array[lwr .. upr] where lwr or upr is some function of $ |
| */ |
| pvar = &se.lengthVar; |
| ce = se.e1; |
| } |
| else if (auto ae = exp.isArrayExp()) |
| { |
| /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ |
| * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...) |
| */ |
| pvar = &ae.lengthVar; |
| ce = ae.e1; |
| } |
| else |
| { |
| /* Didn't find $, look in enclosing scope(s). |
| */ |
| return null; |
| } |
| ce = ce.lastComma(); |
| /* If we are indexing into an array that is really a type |
| * tuple, rewrite this as an index into a type tuple and |
| * try again. |
| */ |
| if (auto te = ce.isTypeExp()) |
| { |
| if (auto ttp = te.type.isTypeTuple()) |
| return dollarFromTypeTuple(loc, ttp, sc); |
| } |
| /* *pvar is lazily initialized, so if we refer to $ |
| * multiple times, it gets set only once. |
| */ |
| if (!*pvar) // if not already initialized |
| { |
| /* Create variable v and set it to the value of $ |
| */ |
| VarDeclaration v; |
| Type t; |
| if (auto tupexp = ce.isTupleExp()) |
| { |
| /* It is for an expression tuple, so the |
| * length will be a const. |
| */ |
| Expression e = new IntegerExp(Loc.initial, tupexp.exps.dim, Type.tsize_t); |
| v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, new ExpInitializer(Loc.initial, e)); |
| v.storage_class |= STC.temp | STC.static_ | STC.const_; |
| } |
| else if (ce.type && (t = ce.type.toBasetype()) !is null && (t.ty == Tstruct || t.ty == Tclass)) |
| { |
| // Look for opDollar |
| assert(exp.op == EXP.array || exp.op == EXP.slice); |
| AggregateDeclaration ad = isAggregate(t); |
| assert(ad); |
| Dsymbol s = ad.search(loc, Id.opDollar); |
| if (!s) // no dollar exists -- search in higher scope |
| return null; |
| s = s.toAlias(); |
| Expression e = null; |
| // Check for multi-dimensional opDollar(dim) template. |
| if (TemplateDeclaration td = s.isTemplateDeclaration()) |
| { |
| dinteger_t dim = 0; |
| if (exp.op == EXP.array) |
| { |
| dim = (cast(ArrayExp)exp).currentDimension; |
| } |
| else if (exp.op == EXP.slice) |
| { |
| dim = 0; // slices are currently always one-dimensional |
| } |
| else |
| { |
| assert(0); |
| } |
| auto tiargs = new Objects(); |
| Expression edim = new IntegerExp(Loc.initial, dim, Type.tsize_t); |
| edim = edim.expressionSemantic(sc); |
| tiargs.push(edim); |
| e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs); |
| } |
| else |
| { |
| /* opDollar exists, but it's not a template. |
| * This is acceptable ONLY for single-dimension indexing. |
| * Note that it's impossible to have both template & function opDollar, |
| * because both take no arguments. |
| */ |
| if (exp.op == EXP.array && (cast(ArrayExp)exp).arguments.dim != 1) |
| { |
| exp.error("`%s` only defines opDollar for one dimension", ad.toChars()); |
| return null; |
| } |
| Declaration d = s.isDeclaration(); |
| assert(d); |
| e = new DotVarExp(loc, ce, d); |
| } |
| e = e.expressionSemantic(sc); |
| if (!e.type) |
| exp.error("`%s` has no value", e.toChars()); |
| t = e.type.toBasetype(); |
| if (t && t.ty == Tfunction) |
| e = new CallExp(e.loc, e); |
| v = new VarDeclaration(loc, null, Id.dollar, new ExpInitializer(Loc.initial, e)); |
| v.storage_class |= STC.temp | STC.ctfe | STC.rvalue; |
| } |
| else |
| { |
| /* For arrays, $ will either be a compile-time constant |
| * (in which case its value in set during constant-folding), |
| * or a variable (in which case an expression is created in |
| * toir.c). |
| */ |
| auto e = new VoidInitializer(Loc.initial); |
| e.type = Type.tsize_t; |
| v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, e); |
| v.storage_class |= STC.temp | STC.ctfe; // it's never a true static variable |
| } |
| *pvar = v; |
| } |
| (*pvar).dsymbolSemantic(sc); |
| return (*pvar); |
| } |
| |
| override inout(ArrayScopeSymbol) isArrayScopeSymbol() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Overload Sets |
| */ |
| extern (C++) final class OverloadSet : Dsymbol |
| { |
| Dsymbols a; // array of Dsymbols |
| |
| extern (D) this(Identifier ident, OverloadSet os = null) nothrow |
| { |
| super(ident); |
| if (os) |
| { |
| a.pushSlice(os.a[]); |
| } |
| } |
| |
| void push(Dsymbol s) nothrow |
| { |
| a.push(s); |
| } |
| |
| override inout(OverloadSet) isOverloadSet() inout |
| { |
| return this; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "overloadset"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Forwarding ScopeDsymbol. Used by ForwardingAttribDeclaration and |
| * ForwardingScopeDeclaration to forward symbol insertions to another |
| * scope. See `dmd.attrib.ForwardingAttribDeclaration` for more |
| * details. |
| */ |
| extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol |
| { |
| /************************* |
| * Symbol to forward insertions to. |
| * Can be `null` before being lazily initialized. |
| */ |
| ScopeDsymbol forward; |
| extern (D) this(ScopeDsymbol forward) nothrow |
| { |
| super(null); |
| this.forward = forward; |
| } |
| |
| override Dsymbol symtabInsert(Dsymbol s) nothrow |
| { |
| assert(forward); |
| if (auto d = s.isDeclaration()) |
| { |
| if (d.storage_class & STC.local) |
| { |
| // Symbols with storage class STC.local are not |
| // forwarded, but stored in the local symbol |
| // table. (Those are the `static foreach` variables.) |
| if (!symtab) |
| { |
| symtab = new DsymbolTable(); |
| } |
| return super.symtabInsert(s); // insert locally |
| } |
| } |
| if (!forward.symtab) |
| { |
| forward.symtab = new DsymbolTable(); |
| } |
| // Non-STC.local symbols are forwarded to `forward`. |
| return forward.symtabInsert(s); |
| } |
| |
| /************************ |
| * This override handles the following two cases: |
| * static foreach (i, i; [0]) { ... } |
| * and |
| * static foreach (i; [0]) { enum i = 2; } |
| */ |
| override Dsymbol symtabLookup(Dsymbol s, Identifier id) nothrow |
| { |
| assert(forward); |
| // correctly diagnose clashing foreach loop variables. |
| if (auto d = s.isDeclaration()) |
| { |
| if (d.storage_class & STC.local) |
| { |
| if (!symtab) |
| { |
| symtab = new DsymbolTable(); |
| } |
| return super.symtabLookup(s,id); |
| } |
| } |
| // Declarations within `static foreach` do not clash with |
| // `static foreach` loop variables. |
| if (!forward.symtab) |
| { |
| forward.symtab = new DsymbolTable(); |
| } |
| return forward.symtabLookup(s,id); |
| } |
| |
| override void importScope(Dsymbol s, Visibility visibility) |
| { |
| forward.importScope(s, visibility); |
| } |
| |
| override const(char)* kind()const{ return "local scope"; } |
| |
| override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout nothrow |
| { |
| return this; |
| } |
| |
| } |
| |
| /** |
| * Class that holds an expression in a Dsymbol wrapper. |
| * This is not an AST node, but a class used to pass |
| * an expression as a function parameter of type Dsymbol. |
| */ |
| extern (C++) final class ExpressionDsymbol : Dsymbol |
| { |
| Expression exp; |
| this(Expression exp) nothrow |
| { |
| super(); |
| this.exp = exp; |
| } |
| |
| override inout(ExpressionDsymbol) isExpressionDsymbol() inout nothrow |
| { |
| return this; |
| } |
| } |
| |
| /********************************************** |
| * Encapsulate assigning to an alias: |
| * `identifier = type;` |
| * `identifier = symbol;` |
| * where `identifier` is an AliasDeclaration in scope. |
| */ |
| extern (C++) final class AliasAssign : Dsymbol |
| { |
| Identifier ident; /// Dsymbol's ident will be null, as this class is anonymous |
| Type type; /// replace previous RHS of AliasDeclaration with `type` |
| Dsymbol aliassym; /// replace previous RHS of AliasDeclaration with `aliassym` |
| /// only one of type and aliassym can be != null |
| |
| extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym) nothrow |
| { |
| super(loc, null); |
| this.ident = ident; |
| this.type = type; |
| this.aliassym = aliassym; |
| } |
| |
| override AliasAssign syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| AliasAssign aa = new AliasAssign(loc, ident, |
| type ? type.syntaxCopy() : null, |
| aliassym ? aliassym.syntaxCopy(null) : null); |
| return aa; |
| } |
| |
| override inout(AliasAssign) isAliasAssign() inout |
| { |
| return this; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "alias assignment"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Table of Dsymbol's |
| */ |
| extern (C++) final class DsymbolTable : RootObject |
| { |
| AssocArray!(Identifier, Dsymbol) tab; |
| |
| nothrow: |
| |
| /*************************** |
| * Look up Identifier in symbol table |
| * Params: |
| * ident = identifer to look up |
| * Returns: |
| * Dsymbol if found, null if not |
| */ |
| Dsymbol lookup(const Identifier ident) |
| { |
| //printf("DsymbolTable::lookup(%s)\n", ident.toChars()); |
| return tab[ident]; |
| } |
| |
| /********** |
| * Replace existing symbol in symbol table with `s`. |
| * If it's not there, add it. |
| * Params: |
| * s = replacement symbol with same identifier |
| */ |
| void update(Dsymbol s) |
| { |
| *tab.getLvalue(s.ident) = s; |
| } |
| |
| /************************** |
| * Insert Dsymbol in table. |
| * Params: |
| * s = symbol to add |
| * Returns: |
| * null if already in table, `s` if inserted |
| */ |
| Dsymbol insert(Dsymbol s) |
| { |
| return insert(s.ident, s); |
| } |
| |
| /************************** |
| * Insert Dsymbol in table. |
| * Params: |
| * ident = identifier to serve as index |
| * s = symbol to add |
| * Returns: |
| * null if already in table, `s` if inserted |
| */ |
| Dsymbol insert(const Identifier ident, Dsymbol s) |
| { |
| //printf("DsymbolTable.insert(this = %p, '%s')\n", this, s.ident.toChars()); |
| Dsymbol* ps = tab.getLvalue(ident); |
| if (*ps) |
| return null; // already in table |
| *ps = s; |
| return s; |
| } |
| |
| /***************** |
| * Returns: |
| * number of symbols in symbol table |
| */ |
| size_t length() const pure |
| { |
| return tab.length; |
| } |
| } |
| |
| /********************************************** |
| * ImportC tag symbols sit in a parallel symbol table, |
| * so that this C code works: |
| * --- |
| * struct S { a; }; |
| * int S; |
| * struct S s; |
| * --- |
| * But there are relatively few such tag symbols, so that would be |
| * a waste of memory and complexity. An additional problem is we'd like the D side |
| * to find the tag symbols with ordinary lookup, not lookup in both |
| * tables, if the tag symbol is not conflicting with an ordinary symbol. |
| * The solution is to put the tag symbols that conflict into an associative |
| * array, indexed by the address of the ordinary symbol that conflicts with it. |
| * C has no modules, so this associative array is tagSymTab[] in ModuleDeclaration. |
| * A side effect of our approach is that D code cannot access a tag symbol that is |
| * hidden by an ordinary symbol. This is more of a theoretical problem, as nobody |
| * has mentioned it when importing C headers. If someone wants to do it, |
| * too bad so sad. Change the C code. |
| * This function fixes up the symbol table when faced with adding a new symbol |
| * `s` when there is an existing symbol `s2` with the same name. |
| * C also allows forward and prototype declarations of tag symbols, |
| * this function merges those. |
| * Params: |
| * sc = context |
| * s = symbol to add to symbol table |
| * s2 = existing declaration |
| * sds = symbol table |
| * Returns: |
| * if s and s2 are successfully put in symbol table then return the merged symbol, |
| * null if they conflict |
| */ |
| Dsymbol handleTagSymbols(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds) |
| { |
| enum log = false; |
| if (log) printf("handleTagSymbols('%s') add %p existing %p\n", s.toChars(), s, s2); |
| if (log) printf(" add %s %s, existing %s %s\n", s.kind(), s.toChars(), s2.kind(), s2.toChars()); |
| auto sd = s.isScopeDsymbol(); // new declaration |
| auto sd2 = s2.isScopeDsymbol(); // existing declaration |
| |
| if (!sd2) |
| { |
| /* Look in tag table |
| */ |
| if (log) printf(" look in tag table\n"); |
| if (auto p = cast(void*)s2 in sc._module.tagSymTab) |
| { |
| Dsymbol s2tag = *p; |
| sd2 = s2tag.isScopeDsymbol(); |
| assert(sd2); // only tags allowed in tag symbol table |
| } |
| } |
| |
| if (sd && sd2) // `s` is a tag, `sd2` is the same tag |
| { |
| if (log) printf(" tag is already defined\n"); |
| |
| if (sd.kind() != sd2.kind()) // being enum/struct/union must match |
| return null; // conflict |
| |
| /* Not a redeclaration if one is a forward declaration. |
| * Move members to the first declared type, which is sd2. |
| */ |
| if (sd2.members) |
| { |
| if (!sd.members) |
| return sd2; // ignore the sd redeclaration |
| } |
| else if (sd.members) |
| { |
| sd2.members = sd.members; // transfer definition to sd2 |
| sd.members = null; |
| return sd2; |
| } |
| else |
| return sd2; // ignore redeclaration |
| } |
| else if (sd) // `s` is a tag, `s2` is not |
| { |
| if (log) printf(" s is tag, s2 is not\n"); |
| /* add `s` as tag indexed by s2 |
| */ |
| sc._module.tagSymTab[cast(void*)s2] = s; |
| return s; |
| } |
| else if (s2 is sd2) // `s2` is a tag, `s` is not |
| { |
| if (log) printf(" s2 is tag, s is not\n"); |
| /* replace `s2` in symbol table with `s`, |
| * then add `s2` as tag indexed by `s` |
| */ |
| sds.symtab.update(s); |
| sc._module.tagSymTab[cast(void*)s] = s2; |
| return s; |
| } |
| // neither s2 nor s is a tag |
| if (log) printf(" collision\n"); |
| return null; |
| } |
| |
| |
| /********************************************** |
| * ImportC allows redeclarations of C variables, functions and typedefs. |
| * extern int x; |
| * int x = 3; |
| * and: |
| * extern void f(); |
| * void f() { } |
| * Attempt to merge them. |
| * Params: |
| * sc = context |
| * s = symbol to add to symbol table |
| * s2 = existing declaration |
| * sds = symbol table |
| * Returns: |
| * if s and s2 are successfully put in symbol table then return the merged symbol, |
| * null if they conflict |
| */ |
| Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds) |
| { |
| enum log = false; |
| if (log) printf("handleSymbolRedeclarations('%s')\n", s.toChars()); |
| if (log) printf(" add %s %s, existing %s %s\n", s.kind(), s.toChars(), s2.kind(), s2.toChars()); |
| |
| static Dsymbol collision() |
| { |
| if (log) printf(" collision\n"); |
| return null; |
| } |
| |
| auto vd = s.isVarDeclaration(); // new declaration |
| auto vd2 = s2.isVarDeclaration(); // existing declaration |
| if (vd && vd2) |
| { |
| /* if one is `static` and the other isn't, the result is undefined |
| * behavior, C11 6.2.2.7 |
| */ |
| if ((vd.storage_class ^ vd2.storage_class) & STC.static_) |
| return collision(); |
| |
| const i1 = vd._init && ! vd._init.isVoidInitializer(); |
| const i2 = vd2._init && !vd2._init.isVoidInitializer(); |
| |
| if (i1 && i2) |
| return collision(); // can't both have initializers |
| |
| if (i1) // vd is the definition |
| { |
| vd2.storage_class |= STC.extern_; // so toObjFile() won't emit it |
| sds.symtab.update(vd); // replace vd2 with the definition |
| return vd; |
| } |
| |
| /* BUG: the types should match, which needs semantic() to be run on it |
| * extern int x; |
| * int x; // match |
| * typedef int INT; |
| * INT x; // match |
| * long x; // collision |
| * We incorrectly ignore these collisions |
| */ |
| return vd2; |
| } |
| |
| auto fd = s.isFuncDeclaration(); // new declaration |
| auto fd2 = s2.isFuncDeclaration(); // existing declaration |
| if (fd && fd2) |
| { |
| /* if one is `static` and the other isn't, the result is undefined |
| * behavior, C11 6.2.2.7 |
| * However, match what gcc allows: |
| * static int sun1(); int sun1() { return 0; } |
| * and: |
| * static int sun2() { return 0; } int sun2(); |
| * Both produce a static function. |
| * |
| * Both of these should fail: |
| * int sun3(); static int sun3() { return 0; } |
| * and: |
| * int sun4() { return 0; } static int sun4(); |
| */ |
| // if adding `static` |
| if ( fd.storage_class & STC.static_ && |
| !(fd2.storage_class & STC.static_)) |
| { |
| return collision(); |
| } |
| |
| if (fd.fbody && fd2.fbody) |
| return collision(); // can't both have bodies |
| |
| if (fd.fbody) // fd is the definition |
| { |
| if (log) printf(" replace existing with new\n"); |
| sds.symtab.update(fd); // replace fd2 in symbol table with fd |
| fd.overnext = fd2; |
| |
| /* If fd2 is covering a tag symbol, then fd has to cover the same one |
| */ |
| auto ps = cast(void*)fd2 in sc._module.tagSymTab; |
| if (ps) |
| sc._module.tagSymTab[cast(void*)fd] = *ps; |
| |
| return fd; |
| } |
| |
| /* Just like with VarDeclaration, the types should match, which needs semantic() to be run on it. |
| * FuncDeclaration::semantic() detects this, but it relies on .overnext being set. |
| */ |
| fd2.overloadInsert(fd); |
| |
| return fd2; |
| } |
| |
| auto td = s.isAliasDeclaration(); // new declaration |
| auto td2 = s2.isAliasDeclaration(); // existing declaration |
| if (td && td2) |
| { |
| /* BUG: just like with variables and functions, the types should match, which needs semantic() to be run on it. |
| * FuncDeclaration::semantic2() can detect this, but it relies overnext being set. |
| */ |
| return td2; |
| } |
| |
| return collision(); |
| } |