view gcc/d/dmd/access.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents
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/access.c
 */

#include "root/dsystem.h"
#include "root/root.h"
#include "root/rmem.h"

#include "errors.h"
#include "enum.h"
#include "aggregate.h"
#include "init.h"
#include "attrib.h"
#include "scope.h"
#include "id.h"
#include "mtype.h"
#include "declaration.h"
#include "aggregate.h"
#include "expression.h"
#include "module.h"
#include "template.h"

/* Code to do access checks
 */

bool hasPackageAccess(Scope *sc, Dsymbol *s);
bool hasPackageAccess(Module *mod, Dsymbol *s);
bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember);
bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd);
static Dsymbol *mostVisibleOverload(Dsymbol *s);

/****************************************
 * Return Prot access for Dsymbol smember in this declaration.
 */
Prot getAccess(AggregateDeclaration *ad, Dsymbol *smember)
{
    Prot access_ret = Prot(PROTnone);

    assert(ad->isStructDeclaration() || ad->isClassDeclaration());
    if (smember->toParent() == ad)
    {
        access_ret = smember->prot();
    }
    else if (smember->isDeclaration()->isStatic())
    {
        access_ret = smember->prot();
    }
    if (ClassDeclaration *cd = ad->isClassDeclaration())
    {
        for (size_t i = 0; i < cd->baseclasses->dim; i++)
        {
            BaseClass *b = (*cd->baseclasses)[i];

            Prot access = getAccess(b->sym, smember);
            switch (access.kind)
            {
                case PROTnone:
                    break;

                case PROTprivate:
                    access_ret = Prot(PROTnone);  // private members of base class not accessible
                    break;

                case PROTpackage:
                case PROTprotected:
                case PROTpublic:
                case PROTexport:
                    // If access is to be tightened
                    if (PROTpublic < access.kind)
                        access = Prot(PROTpublic);

                    // Pick path with loosest access
                    if (access_ret.isMoreRestrictiveThan(access))
                        access_ret = access;
                    break;

                default:
                    assert(0);
            }
        }
    }

    return access_ret;
}

/********************************************************
 * Helper function for checkAccess()
 * Returns:
 *      false   is not accessible
 *      true    is accessible
 */
static bool isAccessible(
        Dsymbol *smember,
        Dsymbol *sfunc,
        AggregateDeclaration *dthis,
        AggregateDeclaration *cdscope)
{
    assert(dthis);

    if (hasPrivateAccess(dthis, sfunc) ||
        isFriendOf(dthis, cdscope))
    {
        if (smember->toParent() == dthis)
            return true;

        if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
        {
            for (size_t i = 0; i < cdthis->baseclasses->dim; i++)
            {
                BaseClass *b = (*cdthis->baseclasses)[i];
                Prot access = getAccess(b->sym, smember);
                if (access.kind >= PROTprotected ||
                    isAccessible(smember, sfunc, b->sym, cdscope))
                {
                    return true;
                }
            }
        }
    }
    else
    {
        if (smember->toParent() != dthis)
        {
            if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
            {
                for (size_t i = 0; i < cdthis->baseclasses->dim; i++)
                {
                    BaseClass *b = (*cdthis->baseclasses)[i];
                    if (isAccessible(smember, sfunc, b->sym, cdscope))
                        return true;
                }
            }
        }
    }
    return false;
}

/*******************************
 * Do access check for member of this class, this class being the
 * type of the 'this' pointer used to access smember.
 * Returns true if the member is not accessible.
 */
bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember)
{
    FuncDeclaration *f = sc->func;
    AggregateDeclaration *cdscope = sc->getStructClassScope();

    Dsymbol *smemberparent = smember->toParent();
    if (!smemberparent || !smemberparent->isAggregateDeclaration())
    {
        return false;                   // then it is accessible
    }

    // BUG: should enable this check
    //assert(smember->parent->isBaseOf(this, NULL));

    bool result;
    Prot access;
    if (smemberparent == ad)
    {
        access = smember->prot();
        result = access.kind >= PROTpublic ||
                 hasPrivateAccess(ad, f) ||
                 isFriendOf(ad, cdscope) ||
                 (access.kind == PROTpackage && hasPackageAccess(sc, smember)) ||
                 ad->getAccessModule() == sc->_module;
    }
    else if ((access = getAccess(ad, smember)).kind >= PROTpublic)
    {
        result = true;
    }
    else if (access.kind == PROTpackage && hasPackageAccess(sc, ad))
    {
        result = true;
    }
    else
    {
        result = isAccessible(smember, f, ad, cdscope);
    }
    if (!result)
    {
        ad->error(loc, "member %s is not accessible", smember->toChars());
        //printf("smember = %s %s, prot = %d, semanticRun = %d\n",
        //        smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun);
        return true;
    }
    return false;
}

/****************************************
 * Determine if this is the same or friend of cd.
 */
bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd)
{
    if (ad == cd)
        return true;

    // Friends if both are in the same module
    //if (toParent() == cd->toParent())
    if (cd && ad->getAccessModule() == cd->getAccessModule())
    {
        return true;
    }

    return false;
}

/****************************************
 * Determine if scope sc has package level access to s.
 */
bool hasPackageAccess(Scope *sc, Dsymbol *s)
{
    return hasPackageAccess(sc->_module, s);
}

bool hasPackageAccess(Module *mod, Dsymbol *s)
{
    Package *pkg = NULL;

    if (s->prot().pkg)
        pkg = s->prot().pkg;
    else
    {
        // no explicit package for protection, inferring most qualified one
        for (; s; s = s->parent)
        {
            if (Module *m = s->isModule())
            {
                DsymbolTable *dst = Package::resolve(m->md ? m->md->packages : NULL, NULL, NULL);
                assert(dst);
                Dsymbol *s2 = dst->lookup(m->ident);
                assert(s2);
                Package *p = s2->isPackage();
                if (p && p->isPackageMod())
                {
                    pkg = p;
                    break;
                }
            }
            else if ((pkg = s->isPackage()) != NULL)
                break;
        }
    }

    if (pkg)
    {
        if (pkg == mod->parent)
        {
            return true;
        }
        if (pkg->isPackageMod() == mod)
        {
            return true;
        }
        Dsymbol* ancestor = mod->parent;
        for (; ancestor; ancestor = ancestor->parent)
        {
            if (ancestor == pkg)
            {
                return true;
            }
        }
    }

    return false;
}

/****************************************
 * Determine if scope sc has protected level access to cd.
 */
bool hasProtectedAccess(Scope *sc, Dsymbol *s)
{
    if (ClassDeclaration *cd = s->isClassMember()) // also includes interfaces
    {
        for (Scope *scx = sc; scx; scx = scx->enclosing)
        {
            if (!scx->scopesym)
                continue;
            ClassDeclaration *cd2 = scx->scopesym->isClassDeclaration();
            if (cd2 && cd->isBaseOf(cd2, NULL))
                return true;
        }
    }
    return sc->_module == s->getAccessModule();
}

/**********************************
 * Determine if smember has access to private members of this declaration.
 */
bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember)
{
    if (smember)
    {
        AggregateDeclaration *cd = NULL;
        Dsymbol *smemberparent = smember->toParent();
        if (smemberparent)
            cd = smemberparent->isAggregateDeclaration();

        if (ad == cd)         // smember is a member of this class
        {
            return true;           // so we get private access
        }

        // If both are members of the same module, grant access
        while (1)
        {
            Dsymbol *sp = smember->toParent();
            if (sp->isFuncDeclaration() && smember->isFuncDeclaration())
                smember = sp;
            else
                break;
        }
        if (!cd && ad->toParent() == smember->toParent())
        {
            return true;
        }
        if (!cd && ad->getAccessModule() == smember->getAccessModule())
        {
            return true;
        }
    }
    return false;
}

/****************************************
 * Check access to d for expression e.d
 * Returns true if the declaration is not accessible.
 */
bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d)
{
    if (sc->flags & SCOPEnoaccesscheck)
        return false;

    if (d->isUnitTestDeclaration())
    {
        // Unittests are always accessible.
        return false;
    }
    if (!e)
    {
        if ((d->prot().kind == PROTprivate && d->getAccessModule() != sc->_module) ||
            (d->prot().kind == PROTpackage && !hasPackageAccess(sc, d)))
        {
            error(loc, "%s %s is not accessible from module %s",
                d->kind(), d->toPrettyChars(), sc->_module->toChars());
            return true;
        }
    }
    else if (e->type->ty == Tclass)
    {
        // Do access check
        ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym);
        if (e->op == TOKsuper)
        {
            ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration();
            if (cd2)
                cd = cd2;
        }
        return checkAccess(cd, loc, sc, d);
    }
    else if (e->type->ty == Tstruct)
    {
        // Do access check
        StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym);
        return checkAccess(cd, loc, sc, d);
    }
    return false;
}

/****************************************
 * Check access to package/module `p` from scope `sc`.
 *
 * Params:
 *   loc = source location for issued error message
 *   sc = scope from which to access to a fully qualified package name
 *   p = the package/module to check access for
 * Returns: true if the package is not accessible.
 *
 * Because a global symbol table tree is used for imported packages/modules,
 * access to them needs to be checked based on the imports in the scope chain
 * (see Bugzilla 313).
 *
 */
bool checkAccess(Loc loc, Scope *sc, Package *p)
{
    if (sc->_module == p)
        return false;
    for (; sc; sc = sc->enclosing)
    {
        if (sc->scopesym && sc->scopesym->isPackageAccessible(p, Prot(PROTprivate)))
            return false;
    }
    const char *name = p->toPrettyChars();
    if (p->isPkgMod == PKGmodule || p->isModule())
        deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", p->kind(), name, name);
    else
        deprecation(loc, "%s %s is not accessible here", p->kind(), name);
    return true;
}

/**
 * Check whether symbols `s` is visible in `mod`.
 *
 * Params:
 *  mod = lookup origin
 *  s = symbol to check for visibility
 * Returns: true if s is visible in mod
 */
bool symbolIsVisible(Module *mod, Dsymbol *s)
{
    // should sort overloads by ascending protection instead of iterating here
    s = mostVisibleOverload(s);

    switch (s->prot().kind)
    {
        case PROTundefined:
            return true;
        case PROTnone:
            return false; // no access
        case PROTprivate:
            return s->getAccessModule() == mod;
        case PROTpackage:
            return s->getAccessModule() == mod || hasPackageAccess(mod, s);
        case PROTprotected:
            return s->getAccessModule() == mod;
        case PROTpublic:
        case PROTexport:
            return true;
        default:
            assert(0);
    }
}

/**
 * Same as above, but determines the lookup module from symbols `origin`.
 */
bool symbolIsVisible(Dsymbol *origin, Dsymbol *s)
{
    return symbolIsVisible(origin->getAccessModule(), s);
}

/**
 * Same as above but also checks for protected symbols visible from scope `sc`.
 * Used for qualified name lookup.
 *
 * Params:
 *  sc = lookup scope
 *  s = symbol to check for visibility
 * Returns: true if s is visible by origin
 */
bool symbolIsVisible(Scope *sc, Dsymbol *s)
{
    s = mostVisibleOverload(s);

    switch (s->prot().kind)
    {
        case PROTundefined:
            return true;
        case PROTnone:
            return false; // no access
        case PROTprivate:
            return sc->_module == s->getAccessModule();
        case PROTpackage:
            return sc->_module == s->getAccessModule() || hasPackageAccess(sc->_module, s);
        case PROTprotected:
            return hasProtectedAccess(sc, s);
        case PROTpublic:
        case PROTexport:
            return true;
        default:
            assert(0);
    }
}

/**
 * Use the most visible overload to check visibility. Later perform an access
 * check on the resolved overload.  This function is similar to overloadApply,
 * but doesn't recurse nor resolve aliases because protection/visibility is an
 * attribute of the alias not the aliasee.
 */
static Dsymbol *mostVisibleOverload(Dsymbol *s)
{
    if (!s->isOverloadable())
        return s;

    Dsymbol *next = NULL;
    Dsymbol *fstart = s;
    Dsymbol *mostVisible = s;
    for (; s; s = next)
    {
        // void func() {}
        // private void func(int) {}
        if (FuncDeclaration *fd = s->isFuncDeclaration())
            next = fd->overnext;
        // template temp(T) {}
        // private template temp(T:int) {}
        else if (TemplateDeclaration *td = s->isTemplateDeclaration())
            next = td->overnext;
        // alias common = mod1.func1;
        // alias common = mod2.func2;
        else if (FuncAliasDeclaration *fa = s->isFuncAliasDeclaration())
            next = fa->overnext;
        // alias common = mod1.templ1;
        // alias common = mod2.templ2;
        else if (OverDeclaration *od = s->isOverDeclaration())
            next = od->overnext;
        // alias name = sym;
        // private void name(int) {}
        else if (AliasDeclaration *ad = s->isAliasDeclaration())
        {
            if (!ad->isOverloadable())
            {
                //printf("Non overloadable Aliasee in overload list\n");
                assert(0);
            }
            // Yet unresolved aliases store overloads in overnext.
            if (ad->semanticRun < PASSsemanticdone)
                next = ad->overnext;
            else
            {
                /* This is a bit messy due to the complicated implementation of
                 * alias.  Aliases aren't overloadable themselves, but if their
                 * Aliasee is overloadable they can be converted to an overloadable
                 * alias.
                 *
                 * This is done by replacing the Aliasee w/ FuncAliasDeclaration
                 * (for functions) or OverDeclaration (for templates) which are
                 * simply overloadable aliases w/ weird names.
                 *
                 * Usually aliases should not be resolved for visibility checking
                 * b/c public aliases to private symbols are public. But for the
                 * overloadable alias situation, the Alias (_ad_) has been moved
                 * into it's own Aliasee, leaving a shell that we peel away here.
                 */
                Dsymbol *aliasee = ad->toAlias();
                if (aliasee->isFuncAliasDeclaration() || aliasee->isOverDeclaration())
                    next = aliasee;
                else
                {
                    /* A simple alias can be at the end of a function or template overload chain.
                     * It can't have further overloads b/c it would have been
                     * converted to an overloadable alias.
                     */
                    if (ad->overnext)
                    {
                        //printf("Unresolved overload of alias\n");
                        assert(0);
                    }
                    break;
                }
            }

            // handled by overloadApply for unknown reason
            assert(next != ad); // should not alias itself
            assert(next != fstart); // should not alias the overload list itself
        }
        else
            break;

        if (next && mostVisible->prot().isMoreRestrictiveThan(next->prot()))
            mostVisible = next;
    }
    return mostVisible;
}