blob: f0c1cf6d93cfe0e7b9052accd98cc81918872825 [file] [log] [blame]
/* 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<void *>((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<void *>((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;
}