view gcc/d/dmd/dsymbol.c @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
line wrap: on
line source


/* Compiler implementation of the D programming language
 * Copyright (C) 1999-2019 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);
Expression *semantic(Expression *e, Scope *sc);


/****************************** 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->dim : 0);
    Dsymbol *s = NULL;

    if (members)
    {
        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 == 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()
{
    Dsymbol *s = this;

    //printf("Dsymbol::pastMixin() %s\n", toChars());
    while (s && s->isTemplateMixin())
        s = s->parent;
    return s;
}

/// ditto
Dsymbol *Dsymbol::pastMixinAndNspace()
{
    //printf("Dsymbol::pastMixinAndNspace() %s\n", toChars());
    Nspace *ns = isNspace();
    if (!(ns && ns->mangleOnly) && !isTemplateMixin() && !isForwardingAttribDeclaration())
        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()
{
    Dsymbol *s = parent;
    while (s && s->isTemplateInstance())
        s = s->parent;
    return s;
}

/// 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 *)
{
}

/*************************************
 * Does semantic analysis on the public face of declarations.
 */

void Dsymbol::semantic(Scope *)
{
    error("%p has no semantic routine", this);
}

/*************************************
 * Does semantic analysis on initializers and members of aggregates.
 */

void Dsymbol::semantic2(Scope *)
{
    // Most Dsymbols have no further semantic analysis needed
}

/*************************************
 * Does semantic analysis on function bodies.
 */

void Dsymbol::semantic3(Scope *)
{
    // Most Dsymbols have no further semantic analysis needed
}

/*********************************************
 * 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

    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)
{
    //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);
            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)
                ti->semantic(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);
}

void 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())
                goto L1;
        }

        for (Scope *sc2 = sc; sc2; sc2 = sc2->enclosing)
        {
            if (sc2->scopesym && sc2->scopesym->isDeprecated())
                goto L1;

            // If inside a StorageClassDeclaration that is deprecated
            if (sc2->stc & STCdeprecated)
                goto L1;
        }

        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");
    }

  L1:
    Declaration *d = isDeclaration();
    if (d && d->storage_class & STCdisable)
    {
        if (!(sc->func && sc->func->storage_class & STCdisable))
        {
            if (d->toParent() && d->isPostBlitDeclaration())
                d->toParent()->error(loc, "is not copyable because it is annotated with @disable");
            else
                error(loc, "is not callable because it is annotated with @disable");
        }
    }
}

/**********************************
 * 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(PROTpublic);
}

/*************************************
 * 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->dim; 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.dim; i++)
        {
            a.push(os->a[i]);
        }
    }
}

void OverloadSet::push(Dsymbol *s)
{
    a.push(s);
}

const char *OverloadSet::kind() const
{
    return "overloadset";
}


/********************************* 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;
}

void ScopeDsymbol::semantic(Scope *)
{
}

/*****************************************
 * 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->dim; i++)
        {
            // If private import, don't search it
            if ((flags & IgnorePrivateImports) && prots[i] == PROTprivate)
                continue;

            int sflags = flags & (IgnoreErrors | IgnoreAmbiguous | IgnoreSymbolVisibility); // 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;
                // compatibility with -transition=import (Bugzilla 15925)
                // SearchLocalsOnly should always get set for new lookup rules
                sflags |= (flags & 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 != PROTnone))
                        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;
            }

            // TODO: remove once private symbol visibility has been deprecated
            if (!(flags & IgnoreErrors) && s->prot().kind == PROTprivate &&
                !s->isOverloadable() && !s->parent->isTemplateMixin() && !s->parent->isNspace())
            {
                AliasDeclaration *ad;
                // accessing private selective and renamed imports is
                // deprecated by restricting the symbol visibility
                if (s->isImport() || ((ad = s->isAliasDeclaration()) != NULL && ad->_import != NULL))
                {}
                else
                    error(loc, "%s %s is private", s->kind(), s->toPrettyChars());
            }
            //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.dim == 0)
        {
            os->a.setDim(os2->a.dim);
            memcpy(os->a.tdata(), os2->a.tdata(), sizeof(os->a[0]) * 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->prot().isMoreRestrictiveThan(s->prot()) &&
                     s->prot().kind != PROTnone))
                {
                    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->dim; 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 = (PROTKIND *)mem.xrealloc(prots, importedScopes->dim * sizeof(prots[0]));
        prots[importedScopes->dim - 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 == PROTprivate ? &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 == PROTprivate && p->tag < privateAccessiblePackages.len && bitArrayGet(&privateAccessiblePackages, p->tag)))
        return true;
    if (importedScopes)
    {
        for (size_t i = 0; i < importedScopes->dim; 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->dim; 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->dim; i++)
    {   Dsymbol *s = (*members)[i];

        if (AttribDeclaration *a = s->isAttribDeclaration())
            result = ScopeDsymbol_foreach(sc, a->include(sc, NULL), 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->dim, Type::tsize_t);
            v->_init = new ExpInitializer(Loc(), e);
            v->storage_class |= STCtemp | STCstatic | STCconst;
            v->semantic(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->dim, Type::tsize_t);
            v->_init = new ExpInitializer(Loc(), e);
            v->storage_class |= STCtemp | STCstatic | STCconst;
            v->semantic(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->dim, 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 = ::semantic(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->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 = ::semantic(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;
        }
        (*pvar)->semantic(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 = PROTundefined;
    this->pkg = NULL;
}

Prot::Prot(PROTKIND 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 == PROTpackage)
            return this->pkg == other.pkg;
        return true;
    }
    return false;
}

/**
 * Checks if parent defines different access restrictions than this one.
 *
 * Params:
 *  parent = protection attribute for scope that hosts this one
 *
 * Returns:
 *  'true' if parent is already more restrictive than this one and thus
 *  no differentiation is needed.
 */
bool Prot::isSubsetOf(const Prot& parent) const
{
    if (this->kind != parent.kind)
        return false;

    if (this->kind == PROTpackage)
    {
        if (!this->pkg)
            return true;
        if (!parent.pkg)
            return false;
        if (parent.pkg->isAncestorPackageOf(this->pkg))
            return true;
    }

    return true;
}