/* Compiler implementation of the D programming language * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved * written by Walter Bright * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt * https://github.com/D-Programming-Language/dmd/blob/master/src/dsymbol.c */ #include "root/dsystem.h" #include "root/rmem.h" #include "root/speller.h" #include "root/aav.h" #include "mars.h" #include "dsymbol.h" #include "aggregate.h" #include "identifier.h" #include "module.h" #include "mtype.h" #include "expression.h" #include "statement.h" #include "declaration.h" #include "id.h" #include "scope.h" #include "init.h" #include "import.h" #include "template.h" #include "attrib.h" #include "enum.h" #include "lexer.h" #include "nspace.h" bool symbolIsVisible(Dsymbol *origin, Dsymbol *s); typedef int (*ForeachDg)(void *ctx, size_t idx, Dsymbol *s); int ScopeDsymbol_foreach(Scope *sc, Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn = NULL); /****************************** Dsymbol ******************************/ Dsymbol::Dsymbol() { //printf("Dsymbol::Dsymbol(%p)\n", this); this->ident = NULL; this->parent = NULL; this->csym = NULL; this->isym = NULL; this->loc = Loc(); this->comment = NULL; this->_scope = NULL; this->prettystring = NULL; this->semanticRun = PASSinit; this->errors = false; this->depdecl = NULL; this->userAttribDecl = NULL; this->ddocUnittest = NULL; } Dsymbol::Dsymbol(Identifier *ident) { //printf("Dsymbol::Dsymbol(%p, ident)\n", this); this->ident = ident; this->parent = NULL; this->csym = NULL; this->isym = NULL; this->loc = Loc(); this->comment = NULL; this->_scope = NULL; this->prettystring = NULL; this->semanticRun = PASSinit; this->errors = false; this->depdecl = NULL; this->userAttribDecl = NULL; this->ddocUnittest = NULL; } Dsymbol *Dsymbol::create(Identifier *ident) { return new Dsymbol(ident); } bool Dsymbol::equals(RootObject *o) { if (this == o) return true; Dsymbol *s = (Dsymbol *)(o); // Overload sets don't have an ident if (s && ident && s->ident && ident->equals(s->ident)) return true; return false; } /************************************** * Copy the syntax. * Used for template instantiations. * If s is NULL, allocate the new object, otherwise fill it in. */ Dsymbol *Dsymbol::syntaxCopy(Dsymbol *) { print(); printf("%s %s\n", kind(), toChars()); assert(0); return NULL; } /************************************** * 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 Dsymbol::oneMember(Dsymbol **ps, Identifier *) { //printf("Dsymbol::oneMember()\n"); *ps = this; return true; } /***************************************** * Same as Dsymbol::oneMember(), but look at an array of Dsymbols. */ bool Dsymbol::oneMembers(Dsymbols *members, Dsymbol **ps, Identifier *ident) { //printf("Dsymbol::oneMembers() %d\n", members ? members->length : 0); Dsymbol *s = NULL; if (members) { for (size_t i = 0; i < members->length; 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 == 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 == 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; } /***************************************** * Is Dsymbol a variable that contains pointers? */ bool Dsymbol::hasPointers() { //printf("Dsymbol::hasPointers() %s\n", toChars()); return false; } bool Dsymbol::hasStaticCtorOrDtor() { //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); return false; } void Dsymbol::setFieldOffset(AggregateDeclaration *, unsigned *, bool) { } Identifier *Dsymbol::getIdent() { return ident; } const char *Dsymbol::toChars() { return ident ? ident->toChars() : "__anonymous"; } const char *Dsymbol::toPrettyCharsHelper() { return toChars(); } const char *Dsymbol::toPrettyChars(bool QualifyTypes) { if (prettystring && !QualifyTypes) return (const char *)prettystring; //printf("Dsymbol::toPrettyChars() '%s'\n", toChars()); if (!parent) { const char *s = toChars(); if (!QualifyTypes) prettystring = (const utf8_t *)s; return s; } // Computer number of components size_t complength = 0; for (Dsymbol *p = this; p; p = p->parent) ++complength; // Allocate temporary array comp[] const char **comp = (const char **)mem.xmalloc(complength * sizeof(char**)); // Fill in comp[] and compute length of final result size_t length = 0; int i = 0; for (Dsymbol *p = this; p; p = p->parent) { const char *s = QualifyTypes ? p->toPrettyCharsHelper() : p->toChars(); const size_t len = strlen(s); comp[i] = s; ++i; length += len + 1; } char *s = (char *)mem.xmalloc(length); char *q = s + length - 1; *q = 0; for (size_t j = 0; j < complength; j++) { const char *t = comp[j]; const size_t len = strlen(t); q -= len; memcpy(q, t, len); if (q == s) break; *--q = '.'; } free(comp); if (!QualifyTypes) prettystring = (utf8_t *)s; return s; } Loc& Dsymbol::getLoc() { if (!loc.filename) // avoid bug 5861. { Module *m = getModule(); if (m && m->srcfile) loc.filename = m->srcfile->toChars(); } return loc; } const char *Dsymbol::locToChars() { return getLoc().toChars(); } const char *Dsymbol::kind() const { 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 *Dsymbol::toAlias() { return this; } /********************************* * Resolve recursive tuple expansion in eponymous template. */ Dsymbol *Dsymbol::toAlias2() { return toAlias(); } /** * `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`, `toParent2` and `toParent3`. */ Dsymbol *Dsymbol::pastMixin() { //printf("Dsymbol::pastMixin() %s\n", toChars()); if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol()) return this; if (!parent) return NULL; return parent->pastMixin(); } /// ditto Dsymbol *Dsymbol::pastMixinAndNspace() { //printf("Dsymbol::pastMixinAndNspace() %s\n", toChars()); Nspace *ns = isNspace(); if (!(ns && ns->mangleOnly) && !isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol()) return this; if (!parent) return NULL; return parent->pastMixinAndNspace(); } /********************************** * `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 and Nspaces that are mangleOnly. * * `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. * * `toParent3()` returns a logically enclosing scope symbol this is a member of. * It skips over TemplateMixin's. * * Examples: * module mod; * template Foo(alias a) { mixin Bar!(); } * mixin template Bar() { * public { // ProtDeclaration * 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') */ Dsymbol *Dsymbol::toParent() { return parent ? parent->pastMixinAndNspace() : NULL; } /// ditto Dsymbol *Dsymbol::toParent2() { if (!parent || (!parent->isTemplateInstance() && !parent->isForwardingAttribDeclaration() && !parent->isForwardingScopeDsymbol())) return parent; return parent->toParent2(); } /// ditto Dsymbol *Dsymbol::toParent3() { return parent ? parent->pastMixin() : NULL; } TemplateInstance *Dsymbol::isInstantiated() { for (Dsymbol *s = parent; s; s = s->parent) { TemplateInstance *ti = s->isTemplateInstance(); if (ti && !ti->isTemplateMixin()) return ti; } return NULL; } // 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. TemplateInstance *Dsymbol::isSpeculative() { Dsymbol *par = parent; while (par) { TemplateInstance *ti = par->isTemplateInstance(); if (ti && ti->gagged) return ti; par = par->toParent(); } return NULL; } Ungag Dsymbol::ungagSpeculative() { unsigned oldgag = global.gag; if (global.gag && !isSpeculative() && !toParent2()->isFuncDeclaration()) global.gag = 0; return Ungag(oldgag); } bool Dsymbol::isAnonymous() { return ident == NULL; } /************************************* * Set scope for future semantic analysis so we can * deal better with forward references. */ void Dsymbol::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 Dsymbol::importAll(Scope *) { } /********************************************* * 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 *Dsymbol::search(const Loc &, Identifier *, int) { //printf("Dsymbol::search(this=%p,%s, ident='%s')\n", this, toChars(), ident->toChars()); return NULL; } /*************************************************** * Search for symbol with correct spelling. */ void *symbol_search_fp(void *arg, const char *seed, 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. */ size_t len = strlen(seed); if (!len) return NULL; Identifier *id = Identifier::lookup(seed, len); if (!id) return NULL; *cost = 0; Dsymbol *s = (Dsymbol *)arg; Module::clearCache(); return (void *)s->search(Loc(), id, IgnoreErrors); } Dsymbol *Dsymbol::search_correct(Identifier *ident) { if (global.gag) return NULL; // don't do it for speculative compiles; too time consuming // search for exact name first if (Dsymbol *s = search(Loc(), ident, IgnoreErrors)) return s; return (Dsymbol *)speller(ident->toChars(), &symbol_search_fp, (void *)this, idchars); } /*************************************** * Search for identifier id as a member of 'this'. * id may be a template instance. * Returns: * symbol found, NULL if not */ Dsymbol *Dsymbol::searchX(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, (Identifier *)id, flags); break; case DYNCAST_DSYMBOL: { // It's a template instance //printf("\ttemplate instance id\n"); Dsymbol *st = (Dsymbol *)id; TemplateInstance *ti = st->isTemplateInstance(); sm = s->search(loc, ti->name); if (!sm) { sm = s->search_correct(ti->name); if (sm) ::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()); else ::error(loc, "template identifier `%s` is not a member of %s `%s`", ti->name->toChars(), s->kind(), s->toPrettyChars()); 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) dsymbolSemantic(ti, sc); sm = ti->toAlias(); break; } case DYNCAST_TYPE: case DYNCAST_EXPRESSION: default: assert(0); } return sm; } bool Dsymbol::overloadInsert(Dsymbol *) { //printf("Dsymbol::overloadInsert('%s')\n", s->toChars()); return false; } d_uns64 Dsymbol::size(Loc) { error("Dsymbol `%s` has no size", toChars()); return SIZE_INVALID; } bool Dsymbol::isforwardRef() { return false; } AggregateDeclaration *Dsymbol::isThis() { return NULL; } bool Dsymbol::isExport() const { return false; } bool Dsymbol::isImportedSymbol() const { return false; } bool Dsymbol::isDeprecated() { return false; } bool Dsymbol::isOverloadable() { return false; } LabelDsymbol *Dsymbol::isLabel() // is this a LabelDsymbol()? { return NULL; } /// Returns an AggregateDeclaration when toParent() is that. AggregateDeclaration *Dsymbol::isMember() { //printf("Dsymbol::isMember() %s\n", toChars()); Dsymbol *parent = toParent(); //printf("parent is %s %s\n", parent->kind(), parent->toChars()); return parent ? parent->isAggregateDeclaration() : NULL; } /// Returns an AggregateDeclaration when toParent2() is that. AggregateDeclaration *Dsymbol::isMember2() { //printf("Dsymbol::isMember2() %s\n", toChars()); Dsymbol *parent = toParent2(); //printf("parent is %s %s\n", parent->kind(), parent->toChars()); return parent ? parent->isAggregateDeclaration() : NULL; } // is this a member of a ClassDeclaration? ClassDeclaration *Dsymbol::isClassMember() { AggregateDeclaration *ad = isMember(); return ad ? ad->isClassDeclaration() : NULL; } Type *Dsymbol::getType() { return NULL; } bool Dsymbol::needThis() { return false; } /********************************* * Iterate this dsymbol or members of this scoped dsymbol, then * call `fp` with the found symbol and `param`. * Params: * fp = function pointer to process the iterated symbol. * If it returns nonzero, the iteration will be aborted. * param = a parameter passed to fp. * Returns: * nonzero if the iteration is aborted by the return value of fp, * or 0 if it's completed. */ int Dsymbol::apply(Dsymbol_apply_ft_t fp, void *param) { return (*fp)(this, param); } void Dsymbol::addMember(Scope *, 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 { if (!sds->symtabInsert(this)) // if name is already defined { Dsymbol *s2 = sds->symtabLookup(this, ident); if (!s2->overloadInsert(this)) { sds->multiplyDefined(Loc(), this, s2); errors = true; } } if (sds->isAggregateDeclaration() || sds->isEnumDeclaration()) { if (ident == Id::__sizeof || ident == Id::__xalignof || ident == Id::_mangleof) { error(".%s property cannot be redefined", ident->toChars()); errors = true; } } } } void Dsymbol::error(const char *format, ...) { va_list ap; va_start(ap, format); ::verror(getLoc(), format, ap, kind(), toPrettyChars()); va_end(ap); } void Dsymbol::error(Loc loc, const char *format, ...) { va_list ap; va_start(ap, format); ::verror(loc, format, ap, kind(), toPrettyChars()); va_end(ap); } void Dsymbol::deprecation(Loc loc, const char *format, ...) { va_list ap; va_start(ap, format); ::vdeprecation(loc, format, ap, kind(), toPrettyChars()); va_end(ap); } void Dsymbol::deprecation(const char *format, ...) { va_list ap; va_start(ap, format); ::vdeprecation(getLoc(), format, ap, kind(), toPrettyChars()); va_end(ap); } bool Dsymbol::checkDeprecated(Loc loc, Scope *sc) { if (global.params.useDeprecated != DIAGNOSTICoff && isDeprecated()) { // Don't complain if we're inside a deprecated symbol's scope for (Dsymbol *sp = sc->parent; sp; sp = sp->parent) { if (sp->isDeprecated()) return false; } for (Scope *sc2 = sc; sc2; sc2 = sc2->enclosing) { if (sc2->scopesym && sc2->scopesym->isDeprecated()) return false; // If inside a StorageClassDeclaration that is deprecated if (sc2->stc & STCdeprecated) 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"); return true; } return false; } /********************************** * Determine which Module a Dsymbol is in. */ Module *Dsymbol::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; } /********************************** * Determine which Module a Dsymbol is in, as far as access rights go. */ Module *Dsymbol::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; } /************************************* */ Prot Dsymbol::prot() { return Prot(Prot::public_); } /************************************* * Do syntax copy of an array of Dsymbol's. */ Dsymbols *Dsymbol::arraySyntaxCopy(Dsymbols *a) { Dsymbols *b = NULL; if (a) { b = a->copy(); for (size_t i = 0; i < b->length; i++) { (*b)[i] = (*b)[i]->syntaxCopy(NULL); } } return b; } /**************************************** * Add documentation comment to Dsymbol. * Ignore NULL comments. */ void Dsymbol::addComment(const utf8_t *comment) { //if (comment) //printf("adding comment '%s' to symbol %p '%s'\n", comment, this, toChars()); if (!this->comment) this->comment = comment; else if (comment && strcmp((const char *)comment, (const char *)this->comment) != 0) { // Concatenate the two this->comment = Lexer::combineComments(this->comment, comment); } } /**************************************** * Returns true if this symbol is defined in a non-root module without instantiation. */ bool Dsymbol::inNonRoot() { Dsymbol *s = parent; for (; s; s = s->toParent()) { if (s->isTemplateInstance()) { return false; } if (Module *m = s->isModule()) { if (!m->isRoot()) return true; break; } } return false; } /********************************* OverloadSet ****************************/ OverloadSet::OverloadSet(Identifier *ident, OverloadSet *os) : Dsymbol(ident) { if (os) { for (size_t i = 0; i < os->a.length; i++) { a.push(os->a[i]); } } } void OverloadSet::push(Dsymbol *s) { a.push(s); } const char *OverloadSet::kind() const { return "overloadset"; } /********************************* ForwardingScopeDsymbol ******************/ ForwardingScopeDsymbol::ForwardingScopeDsymbol(ScopeDsymbol *forward) : ScopeDsymbol() { this->forward = forward; } Dsymbol *ForwardingScopeDsymbol::symtabInsert(Dsymbol *s) { assert(forward); if (Declaration *d = s->isDeclaration()) { if (d->storage_class & STClocal) { // Symbols with storage class STClocal are not // forwarded, but stored in the local symbol // table. (Those are the `static foreach` variables.) if (!symtab) { symtab = new DsymbolTable(); } return ScopeDsymbol::symtabInsert(s); // insert locally } } if (!forward->symtab) { forward->symtab = new DsymbolTable(); } // Non-STClocal 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; } */ Dsymbol *ForwardingScopeDsymbol::symtabLookup(Dsymbol *s, Identifier *id) { assert(forward); // correctly diagnose clashing foreach loop variables. if (Declaration *d = s->isDeclaration()) { if (d->storage_class & STClocal) { if (!symtab) { symtab = new DsymbolTable(); } return ScopeDsymbol::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); } void ForwardingScopeDsymbol::importScope(Dsymbol *s, Prot protection) { forward->importScope(s, protection); } const char *ForwardingScopeDsymbol::kind() const { return "local scope"; } /********************************* ScopeDsymbol ****************************/ ScopeDsymbol::ScopeDsymbol() : Dsymbol() { members = NULL; symtab = NULL; endlinnum = 0; importedScopes = NULL; prots = NULL; } ScopeDsymbol::ScopeDsymbol(Identifier *id) : Dsymbol(id) { members = NULL; symtab = NULL; endlinnum = 0; importedScopes = NULL; prots = NULL; } Dsymbol *ScopeDsymbol::syntaxCopy(Dsymbol *s) { //printf("ScopeDsymbol::syntaxCopy('%s')\n", toChars()); ScopeDsymbol *sds = s ? (ScopeDsymbol *)s : new ScopeDsymbol(ident); 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. */ Dsymbol *ScopeDsymbol::search(const Loc &loc, Identifier *ident, int flags) { //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"); Dsymbol *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) { //printf(" look in imports\n"); Dsymbol *s = NULL; OverloadSet *a = NULL; // Look in imported modules for (size_t i = 0; i < importedScopes->length; i++) { // If private import, don't search it if ((flags & IgnorePrivateImports) && prots[i] == Prot::private_) continue; int sflags = flags & (IgnoreErrors | IgnoreAmbiguous); // remember these in recursive searches Dsymbol *ss = (*importedScopes)[i]; //printf("\tscanning import '%s', prots = %d, isModule = %p, isImport = %p\n", ss->toChars(), prots[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)); 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->prot().isMoreRestrictiveThan(s2->prot()) && s2->prot().kind != Prot::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)) ) ) ) { /* Bugzilla 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) */ if ((s2->isOverloadSet() || s2->isOverloadable()) && (a || s->isOverloadable())) { a = mergeOverloadSet(ident, a, s2); continue; } 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; } OverloadSet *ScopeDsymbol::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.length == 0) { os->a.setDim(os2->a.length); memcpy(os->a.tdata(), os2->a.tdata(), sizeof(os->a[0]) * os2->a.length); } else { for (size_t i = 0; i < os2->a.length; 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.length; j++) { Dsymbol *s2 = os->a[j]; if (s->toAlias() == s2->toAlias()) { if (s2->isDeprecated() || (s2->prot().isMoreRestrictiveThan(s->prot()) && s->prot().kind != Prot::none)) { os->a[j] = s; } goto Lcontinue; } } os->push(s); Lcontinue: ; } return os; } void ScopeDsymbol::importScope(Dsymbol *s, Prot protection) { //printf("%s->ScopeDsymbol::importScope(%s, %d)\n", toChars(), s->toChars(), protection); // No circular or redundant import's if (s != this) { if (!importedScopes) importedScopes = new Dsymbols(); else { for (size_t i = 0; i < importedScopes->length; i++) { Dsymbol *ss = (*importedScopes)[i]; if (ss == s) // if already imported { if (protection.kind > prots[i]) prots[i] = protection.kind; // upgrade access return; } } } importedScopes->push(s); prots = (Prot::Kind *)mem.xrealloc(prots, importedScopes->length * sizeof(prots[0])); prots[importedScopes->length - 1] = protection.kind; } } #define BITS_PER_INDEX (sizeof(size_t) * CHAR_BIT) static void bitArraySet(BitArray *array, size_t idx) { array->ptr[idx / BITS_PER_INDEX] |= 1ULL << (idx % BITS_PER_INDEX); } static bool bitArrayGet(BitArray *array, size_t idx) { const size_t boffset = idx % BITS_PER_INDEX; return (array->ptr[idx / BITS_PER_INDEX] & (1ULL << boffset)) >> boffset; } static void bitArrayLength(BitArray *array, size_t len) { if (array->len < len) { const size_t obytes = (array->len + BITS_PER_INDEX - 1) / BITS_PER_INDEX; const size_t nbytes = (len + BITS_PER_INDEX - 1) / BITS_PER_INDEX; if (!array->ptr) array->ptr = (size_t *)mem.xmalloc(nbytes * sizeof(size_t)); else array->ptr = (size_t *)mem.xrealloc(array->ptr, nbytes * sizeof(size_t)); for (size_t i = obytes; i < nbytes; i++) array->ptr[i] = 0; array->len = nbytes * BITS_PER_INDEX; } } void ScopeDsymbol::addAccessiblePackage(Package *p, Prot protection) { BitArray *pary = protection.kind == Prot::private_ ? &privateAccessiblePackages : &accessiblePackages; if (pary->len <= p->tag) bitArrayLength(pary, p->tag + 1); bitArraySet(pary, p->tag); } bool ScopeDsymbol::isPackageAccessible(Package *p, Prot protection, int) { if ((p->tag < accessiblePackages.len && bitArrayGet(&accessiblePackages, p->tag)) || (protection.kind == Prot::private_ && p->tag < privateAccessiblePackages.len && bitArrayGet(&privateAccessiblePackages, p->tag))) return true; if (importedScopes) { for (size_t i = 0; i < importedScopes->length; i++) { // only search visible scopes && imported modules should ignore private imports Dsymbol *ss = (*importedScopes)[i]; if (protection.kind <= prots[i] && ss->isScopeDsymbol()->isPackageAccessible(p, protection, IgnorePrivateImports)) return true; } } return false; } bool ScopeDsymbol::isforwardRef() { return (members == NULL); } void ScopeDsymbol::multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2) { if (loc.filename) { ::error(loc, "%s at %s conflicts with %s at %s", s1->toPrettyChars(), s1->locToChars(), s2->toPrettyChars(), s2->locToChars()); } else { s1->error(s1->loc, "conflicts with %s %s at %s", s2->kind(), s2->toPrettyChars(), s2->locToChars()); } } const char *ScopeDsymbol::kind() const { return "ScopeDsymbol"; } Dsymbol *ScopeDsymbol::symtabInsert(Dsymbol *s) { return symtab->insert(s); } /**************************************** * Look up identifier in symbol table. */ Dsymbol *ScopeDsymbol::symtabLookup(Dsymbol *, Identifier *id) { 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. */ bool ScopeDsymbol::hasStaticCtorOrDtor() { if (members) { for (size_t i = 0; i < members->length; i++) { Dsymbol *member = (*members)[i]; if (member->hasStaticCtorOrDtor()) return true; } } return false; } /*************************************** * Determine number of Dsymbols, folding in AttribDeclaration members. */ static int dimDg(void *ctx, size_t, Dsymbol *) { ++*(size_t *)ctx; return 0; } size_t ScopeDsymbol::dim(Dsymbols *members) { size_t n = 0; ScopeDsymbol_foreach(NULL, members, &dimDg, &n); return n; } /*************************************** * Get nth Dsymbol, folding in AttribDeclaration members. * Returns: * Dsymbol* nth Dsymbol * NULL not found, *pn gets incremented by the number * of Dsymbols */ struct GetNthSymbolCtx { size_t nth; Dsymbol *sym; }; static int getNthSymbolDg(void *ctx, size_t n, Dsymbol *sym) { GetNthSymbolCtx *p = (GetNthSymbolCtx *)ctx; if (n == p->nth) { p->sym = sym; return 1; } return 0; } Dsymbol *ScopeDsymbol::getNth(Dsymbols *members, size_t nth, size_t *) { GetNthSymbolCtx ctx = { nth, NULL }; int res = ScopeDsymbol_foreach(NULL, members, &getNthSymbolDg, &ctx); return res ? ctx.sym : NULL; } /*************************************** * Expands attribute declarations in members in depth first * order. Calls dg(void *ctx, 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. */ int ScopeDsymbol_foreach(Scope *sc, Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn) { assert(dg); if (!members) return 0; size_t n = pn ? *pn : 0; // take over index int result = 0; for (size_t i = 0; i < members->length; i++) { Dsymbol *s = (*members)[i]; if (AttribDeclaration *a = s->isAttribDeclaration()) result = ScopeDsymbol_foreach(sc, a->include(sc), dg, ctx, &n); else if (TemplateMixin *tm = s->isTemplateMixin()) result = ScopeDsymbol_foreach(sc, tm->members, dg, ctx, &n); else if (s->isTemplateInstance()) ; else if (s->isUnitTestDeclaration()) ; else result = dg(ctx, n++, s); if (result) break; } if (pn) *pn = n; // update index return result; } /******************************************* * Look for member of the form: * const(MemberInfo)[] getMembers(string); * Returns NULL if not found */ FuncDeclaration *ScopeDsymbol::findGetMembers() { Dsymbol *s = search_function(this, Id::getmembers); FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; if (fdx && fdx->isVirtual()) fdx = NULL; return fdx; } /****************************** WithScopeSymbol ******************************/ WithScopeSymbol::WithScopeSymbol(WithStatement *withstate) : ScopeDsymbol() { this->withstate = withstate; } Dsymbol *WithScopeSymbol::search(const Loc &loc, Identifier *ident, int flags) { //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 != eold; e = resolveAliasThis(_scope, e)) { if (e->op == TOKscope) { s = ((ScopeExp *)e)->sds; } else if (e->op == TOKtype) { 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; } /****************************** ArrayScopeSymbol ******************************/ ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, Expression *e) : ScopeDsymbol() { assert(e->op == TOKindex || e->op == TOKslice || e->op == TOKarray); exp = e; type = NULL; td = NULL; this->sc = sc; } ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, TypeTuple *t) : ScopeDsymbol() { exp = NULL; type = t; td = NULL; this->sc = sc; } ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, TupleDeclaration *s) : ScopeDsymbol() { exp = NULL; type = NULL; td = s; this->sc = sc; } Dsymbol *ArrayScopeSymbol::search(const Loc &loc, Identifier *ident, int) { //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags); if (ident == Id::dollar) { VarDeclaration **pvar; Expression *ce; L1: if (td) { /* $ gives the number of elements in the tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(Loc(), td->objects->length, Type::tsize_t); v->_init = new ExpInitializer(Loc(), e); v->storage_class |= STCtemp | STCstatic | STCconst; dsymbolSemantic(v, sc); return v; } if (type) { /* $ gives the number of type entries in the type tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(Loc(), type->arguments->length, Type::tsize_t); v->_init = new ExpInitializer(Loc(), e); v->storage_class |= STCtemp | STCstatic | STCconst; dsymbolSemantic(v, sc); return v; } if (exp->op == TOKindex) { /* array[index] where index is some function of $ */ IndexExp *ie = (IndexExp *)exp; pvar = &ie->lengthVar; ce = ie->e1; } else if (exp->op == TOKslice) { /* array[lwr .. upr] where lwr or upr is some function of $ */ SliceExp *se = (SliceExp *)exp; pvar = &se->lengthVar; ce = se->e1; } else if (exp->op == TOKarray) { /* 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,...) */ ArrayExp *ae = (ArrayExp *)exp; pvar = &ae->lengthVar; ce = ae->e1; } else { /* Didn't find $, look in enclosing scope(s). */ return NULL; } while (ce->op == TOKcomma) ce = ((CommaExp *)ce)->e2; /* 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 (ce->op == TOKtype) { Type *t = ((TypeExp *)ce)->type; if (t->ty == Ttuple) { type = (TypeTuple *)t; goto L1; } } /* *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 (ce->op == TOKtuple) { /* It is for an expression tuple, so the * length will be a const. */ Expression *e = new IntegerExp(Loc(), ((TupleExp *)ce)->exps->length, Type::tsize_t); v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, new ExpInitializer(Loc(), e)); v->storage_class |= STCtemp | STCstatic | STCconst; } else if (ce->type && (t = ce->type->toBasetype()) != NULL && (t->ty == Tstruct || t->ty == Tclass)) { // Look for opDollar assert(exp->op == TOKarray || exp->op == TOKslice); 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 == TOKarray) { dim = ((ArrayExp *)exp)->currentDimension; } else if (exp->op == TOKslice) { dim = 0; // slices are currently always one-dimensional } else { assert(0); } Objects *tiargs = new Objects(); Expression *edim = new IntegerExp(Loc(), dim, Type::tsize_t); edim = expressionSemantic(edim, 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 == TOKarray && ((ArrayExp *)exp)->arguments->length != 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 = expressionSemantic(e, 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(), e)); v->storage_class |= STCtemp | STCctfe | STCrvalue; } 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). */ VoidInitializer *e = new VoidInitializer(Loc()); e->type = Type::tsize_t; v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, e); v->storage_class |= STCtemp | STCctfe; // it's never a true static variable } *pvar = v; } dsymbolSemantic(*pvar, sc); return (*pvar); } return NULL; } /****************************** DsymbolTable ******************************/ DsymbolTable::DsymbolTable() { tab = NULL; } Dsymbol *DsymbolTable::lookup(Identifier const * const ident) { //printf("DsymbolTable::lookup(%s)\n", (char*)ident->string); return (Dsymbol *)dmd_aaGetRvalue(tab, const_cast((const void *)ident)); } Dsymbol *DsymbolTable::insert(Dsymbol *s) { //printf("DsymbolTable::insert(this = %p, '%s')\n", this, s->ident->toChars()); Identifier *ident = s->ident; Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, (void *)ident); if (*ps) return NULL; // already in table *ps = s; return s; } Dsymbol *DsymbolTable::insert(Identifier const * const ident, Dsymbol *s) { //printf("DsymbolTable::insert()\n"); Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, const_cast((const void *)ident)); if (*ps) return NULL; // already in table *ps = s; return s; } Dsymbol *DsymbolTable::update(Dsymbol *s) { Identifier *ident = s->ident; Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, (void *)ident); *ps = s; return s; } /****************************** Prot ******************************/ Prot::Prot() { this->kind = Prot::undefined; this->pkg = NULL; } Prot::Prot(Prot::Kind kind) { this->kind = kind; this->pkg = NULL; } /** * Checks if `this` is superset of `other` restrictions. * For example, "protected" is more restrictive than "public". */ bool Prot::isMoreRestrictiveThan(const Prot other) const { return this->kind < other.kind; } /** * Checks if `this` is absolutely identical protection attribute to `other` */ bool Prot::operator==(const Prot& other) const { if (this->kind == other.kind) { if (this->kind == Prot::package_) return this->pkg == other.pkg; return true; } return false; }