view gcc/d/dmd/dscope.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/scope.c
 */

#include "root/dsystem.h"               // strlen()
#include "root/root.h"
#include "root/rmem.h"
#include "root/speller.h"

#include "mars.h"
#include "init.h"
#include "identifier.h"
#include "scope.h"
#include "attrib.h"
#include "dsymbol.h"
#include "declaration.h"
#include "statement.h"
#include "aggregate.h"
#include "module.h"
#include "id.h"
#include "template.h"

Scope *Scope::freelist = NULL;

void allocFieldinit(Scope *sc, size_t dim)
{
    sc->fieldinit = (unsigned *)mem.xcalloc(sizeof(unsigned), dim);
    sc->fieldinit_dim = dim;
}

void freeFieldinit(Scope *sc)
{
    if (sc->fieldinit)
        mem.xfree(sc->fieldinit);
    sc->fieldinit = NULL;
    sc->fieldinit_dim = 0;
}

Scope *Scope::alloc()
{
    if (freelist)
    {
        Scope *s = freelist;
        freelist = s->enclosing;
        //printf("freelist %p\n", s);
        assert(s->flags & SCOPEfree);
        s->flags &= ~SCOPEfree;
        return s;
    }

    return new Scope();
}

Scope::Scope()
{
    // Create root scope

    //printf("Scope::Scope() %p\n", this);
    this->_module = NULL;
    this->scopesym = NULL;
    this->sds = NULL;
    this->enclosing = NULL;
    this->parent = NULL;
    this->sw = NULL;
    this->tf = NULL;
    this->os = NULL;
    this->tinst = NULL;
    this->minst = NULL;
    this->sbreak = NULL;
    this->scontinue = NULL;
    this->fes = NULL;
    this->callsc = NULL;
    this->aligndecl = NULL;
    this->func = NULL;
    this->slabel = NULL;
    this->linkage = LINKd;
    this->cppmangle = CPPMANGLEdefault;
    this->inlining = PINLINEdefault;
    this->protection = Prot(PROTpublic);
    this->explicitProtection = 0;
    this->stc = 0;
    this->depdecl = NULL;
    this->inunion = 0;
    this->nofree = 0;
    this->noctor = 0;
    this->intypeof = 0;
    this->lastVar = NULL;
    this->callSuper = 0;
    this->fieldinit = NULL;
    this->fieldinit_dim = 0;
    this->flags = 0;
    this->lastdc = NULL;
    this->anchorCounts = NULL;
    this->prevAnchor = NULL;
    this->userAttribDecl = NULL;
}

Scope *Scope::copy()
{
    Scope *sc = Scope::alloc();
    *sc = *this;    // memcpy

    /* Bugzilla 11777: The copied scope should not inherit fieldinit.
     */
    sc->fieldinit = NULL;

    return sc;
}

Scope *Scope::createGlobal(Module *_module)
{
    Scope *sc = Scope::alloc();
    *sc = Scope();  // memset

    sc->aligndecl = NULL;
    sc->linkage = LINKd;
    sc->inlining = PINLINEdefault;
    sc->protection = Prot(PROTpublic);

    sc->_module = _module;

    sc->tinst = NULL;
    sc->minst = _module;

    sc->scopesym = new ScopeDsymbol();
    sc->scopesym->symtab = new DsymbolTable();

    // Add top level package as member of this global scope
    Dsymbol *m = _module;
    while (m->parent)
        m = m->parent;
    m->addMember(NULL, sc->scopesym);
    m->parent = NULL;                   // got changed by addMember()

    // Create the module scope underneath the global scope
    sc = sc->push(_module);
    sc->parent = _module;
    return sc;
}

Scope *Scope::push()
{
    Scope *s = copy();

    //printf("Scope::push(this = %p) new = %p\n", this, s);
    assert(!(flags & SCOPEfree));
    s->scopesym = NULL;
    s->sds = NULL;
    s->enclosing = this;
    s->slabel = NULL;
    s->nofree = 0;
    s->fieldinit = saveFieldInit();
    s->flags = (flags & (SCOPEcontract | SCOPEdebug | SCOPEctfe | SCOPEcompile | SCOPEconstraint |
                         SCOPEnoaccesscheck | SCOPEignoresymbolvisibility));
    s->lastdc = NULL;

    assert(this != s);
    return s;
}

Scope *Scope::push(ScopeDsymbol *ss)
{
    //printf("Scope::push(%s)\n", ss->toChars());
    Scope *s = push();
    s->scopesym = ss;
    return s;
}

Scope *Scope::pop()
{
    //printf("Scope::pop() %p nofree = %d\n", this, nofree);
    Scope *enc = enclosing;

    if (enclosing)
    {
        enclosing->callSuper |= callSuper;
        if (fieldinit)
        {
            if (enclosing->fieldinit)
            {
                assert(fieldinit != enclosing->fieldinit);
                size_t dim = fieldinit_dim;
                for (size_t i = 0; i < dim; i++)
                    enclosing->fieldinit[i] |= fieldinit[i];
            }
            freeFieldinit(this);
        }
    }

    if (!nofree)
    {
        enclosing = freelist;
        freelist = this;
        flags |= SCOPEfree;
    }

    return enc;
}

Scope *Scope::startCTFE()
{
    Scope *sc = this->push();
    sc->flags = this->flags | SCOPEctfe;
    return sc;
}

Scope *Scope::endCTFE()
{
    assert(flags & SCOPEctfe);
    return pop();
}

void Scope::mergeCallSuper(Loc loc, unsigned cs)
{
    // This does a primitive flow analysis to support the restrictions
    // regarding when and how constructors can appear.
    // It merges the results of two paths.
    // The two paths are callSuper and cs; the result is merged into callSuper.

    if (cs != callSuper)
    {
        // Have ALL branches called a constructor?
        int aAll = (cs        & (CSXthis_ctor | CSXsuper_ctor)) != 0;
        int bAll = (callSuper & (CSXthis_ctor | CSXsuper_ctor)) != 0;

        // Have ANY branches called a constructor?
        bool aAny = (cs        & CSXany_ctor) != 0;
        bool bAny = (callSuper & CSXany_ctor) != 0;

        // Have any branches returned?
        bool aRet = (cs        & CSXreturn) != 0;
        bool bRet = (callSuper & CSXreturn) != 0;

        // Have any branches halted?
        bool aHalt = (cs        & CSXhalt) != 0;
        bool bHalt = (callSuper & CSXhalt) != 0;

        bool ok = true;

        if (aHalt && bHalt)
        {
            callSuper = CSXhalt;
        }
        else if ((!aHalt && aRet && !aAny && bAny) ||
                 (!bHalt && bRet && !bAny && aAny))
        {
            // If one has returned without a constructor call, there must be never
            // have been ctor calls in the other.
            ok = false;
        }
        else if (aHalt || (aRet && aAll))
        {
            // If one branch has called a ctor and then exited, anything the
            // other branch has done is OK (except returning without a
            // ctor call, but we already checked that).
            callSuper |= cs & (CSXany_ctor | CSXlabel);
        }
        else if (bHalt || (bRet && bAll))
        {
            callSuper = cs | (callSuper & (CSXany_ctor | CSXlabel));
        }
        else
        {
            // Both branches must have called ctors, or both not.
            ok = (aAll == bAll);
            // If one returned without a ctor, we must remember that
            // (Don't bother if we've already found an error)
            if (ok && aRet && !aAny)
                callSuper |= CSXreturn;
            callSuper |= cs & (CSXany_ctor | CSXlabel);
        }
        if (!ok)
            error(loc, "one path skips constructor");
    }
}

unsigned *Scope::saveFieldInit()
{
    unsigned *fi = NULL;
    if (fieldinit)  // copy
    {
        size_t dim = fieldinit_dim;
        fi = (unsigned *)mem.xmalloc(sizeof(unsigned) * dim);
        for (size_t i = 0; i < dim; i++)
            fi[i] = fieldinit[i];
    }
    return fi;
}

static bool mergeFieldInit(unsigned &fieldInit, unsigned fi, bool mustInit)
{
    if (fi != fieldInit)
    {
        // Have any branches returned?
        bool aRet = (fi        & CSXreturn) != 0;
        bool bRet = (fieldInit & CSXreturn) != 0;

        // Have any branches halted?
        bool aHalt = (fi        & CSXhalt) != 0;
        bool bHalt = (fieldInit & CSXhalt) != 0;

        bool ok;

        if (aHalt && bHalt)
        {
            ok = true;
            fieldInit = CSXhalt;
        }
        else if (!aHalt && aRet)
        {
            ok = !mustInit || (fi & CSXthis_ctor);
            fieldInit = fieldInit;
        }
        else if (!bHalt && bRet)
        {
            ok = !mustInit || (fieldInit & CSXthis_ctor);
            fieldInit = fi;
        }
        else if (aHalt)
        {
            ok = !mustInit || (fieldInit & CSXthis_ctor);
            fieldInit = fieldInit;
        }
        else if (bHalt)
        {
            ok = !mustInit || (fi & CSXthis_ctor);
            fieldInit = fi;
        }
        else
        {
            ok = !mustInit || !((fieldInit ^ fi) & CSXthis_ctor);
            fieldInit |= fi;
        }

        return ok;
    }
    return true;
}

void Scope::mergeFieldInit(Loc loc, unsigned *fies)
{
    if (fieldinit && fies)
    {
        FuncDeclaration *f = func;
        if (fes) f = fes->func;
        AggregateDeclaration *ad = f->isMember2();
        assert(ad);

        for (size_t i = 0; i < ad->fields.dim; i++)
        {
            VarDeclaration *v = ad->fields[i];
            bool mustInit = (v->storage_class & STCnodefaultctor ||
                             v->type->needsNested());

            if (!::mergeFieldInit(fieldinit[i], fies[i], mustInit))
            {
                ::error(loc, "one path skips field %s", ad->fields[i]->toChars());
            }
        }
    }
}

Module *Scope::instantiatingModule()
{
    // TODO: in speculative context, returning 'module' is correct?
    return minst ? minst : _module;
}

static Dsymbol *searchScopes(Scope *scope, Loc loc, Identifier *ident, Dsymbol **pscopesym, int flags)
{
    for (Scope *sc = scope; sc; sc = sc->enclosing)
    {
        assert(sc != sc->enclosing);
        if (!sc->scopesym)
            continue;
        //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc->scopesym->toChars(), sc->scopesym->kind(), flags);

        if (sc->scopesym->isModule())
            flags |= SearchUnqualifiedModule;        // tell Module.search() that SearchLocalsOnly is to be obeyed

        if (Dsymbol *s = sc->scopesym->search(loc, ident, flags))
        {
            if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
                ident == Id::length && sc->scopesym->isArrayScopeSymbol() &&
                sc->enclosing && sc->enclosing->search(loc, ident, NULL, flags))
            {
                warning(s->loc, "array 'length' hides other 'length' name in outer scope");
            }
            if (pscopesym)
                *pscopesym = sc->scopesym;
            return s;
        }
        // Stop when we hit a module, but keep going if that is not just under the global scope
        if (sc->scopesym->isModule() && !(sc->enclosing && !sc->enclosing->enclosing))
            break;
    }
    return NULL;
}

/************************************
 * Perform unqualified name lookup by following the chain of scopes up
 * until found.
 *
 * Params:
 *  loc = location to use for error messages
 *  ident = name to look up
 *  pscopesym = if supplied and name is found, set to scope that ident was found in
 *  flags = modify search based on flags
 *
 * Returns:
 *  symbol if found, null if not
 */
Dsymbol *Scope::search(Loc loc, Identifier *ident, Dsymbol **pscopesym, int flags)
{
    // This function is called only for unqualified lookup
    assert(!(flags & (SearchLocalsOnly | SearchImportsOnly)));

    /* If ident is "start at module scope", only look at module scope
     */
    if (ident == Id::empty)
    {
        // Look for module scope
        for (Scope *sc = this; sc; sc = sc->enclosing)
        {
            assert(sc != sc->enclosing);
            if (!sc->scopesym)
                continue;

            if (Dsymbol *s = sc->scopesym->isModule())
            {
                if (pscopesym)
                    *pscopesym = sc->scopesym;
                return s;
            }
        }
        return NULL;
    }

    if (this->flags & SCOPEignoresymbolvisibility)
        flags |= IgnoreSymbolVisibility;

    Dsymbol *sold = NULL;
    if (global.params.bug10378 || global.params.check10378)
    {
        sold = searchScopes(this, loc, ident, pscopesym, flags | IgnoreSymbolVisibility);
        if (!global.params.check10378)
            return sold;

        if (ident == Id::dollar) // Bugzilla 15825
            return sold;

        // Search both ways
    }

    // First look in local scopes
    Dsymbol *s = searchScopes(this, loc, ident, pscopesym, flags | SearchLocalsOnly);
    if (!s)
    {
        // Second look in imported modules
        s = searchScopes(this, loc, ident, pscopesym, flags | SearchImportsOnly);
        /** Still find private symbols, so that symbols that weren't access
         * checked by the compiler remain usable.  Once the deprecation is over,
         * this should be moved to search_correct instead.
         */
        if (!s && !(flags & IgnoreSymbolVisibility))
        {
            s = searchScopes(this, loc, ident, pscopesym, flags | SearchLocalsOnly | IgnoreSymbolVisibility);
            if (!s)
                s = searchScopes(this, loc, ident, pscopesym, flags | SearchImportsOnly | IgnoreSymbolVisibility);

            if (s && !(flags & IgnoreErrors))
                ::deprecation(loc, "%s is not visible from module %s", s->toPrettyChars(), _module->toChars());
        }
    }

    if (global.params.check10378)
    {
        Dsymbol *snew = s;
        if (sold != snew)
            deprecation10378(loc, sold, snew);
        if (global.params.bug10378)
            s = sold;
    }
    return s;
}

Dsymbol *Scope::insert(Dsymbol *s)
{
    if (VarDeclaration *vd = s->isVarDeclaration())
    {
        if (lastVar)
            vd->lastVar = lastVar;
        lastVar = vd;
    }
    else if (WithScopeSymbol *ss = s->isWithScopeSymbol())
    {
        if (VarDeclaration *vd = ss->withstate->wthis)
        {
            if (lastVar)
                vd->lastVar = lastVar;
            lastVar = vd;
        }
        return NULL;
    }
    for (Scope *sc = this; sc; sc = sc->enclosing)
    {
        //printf("\tsc = %p\n", sc);
        if (sc->scopesym)
        {
            //printf("\t\tsc->scopesym = %p\n", sc->scopesym);
            if (!sc->scopesym->symtab)
                sc->scopesym->symtab = new DsymbolTable();
            return sc->scopesym->symtabInsert(s);
        }
    }
    assert(0);
    return NULL;
}

/********************************************
 * Search enclosing scopes for ClassDeclaration.
 */

ClassDeclaration *Scope::getClassScope()
{
    for (Scope *sc = this; sc; sc = sc->enclosing)
    {
        if (!sc->scopesym)
            continue;

        ClassDeclaration *cd = sc->scopesym->isClassDeclaration();
        if (cd)
            return cd;
    }
    return NULL;
}

/********************************************
 * Search enclosing scopes for ClassDeclaration.
 */

AggregateDeclaration *Scope::getStructClassScope()
{
    for (Scope *sc = this; sc; sc = sc->enclosing)
    {
        if (!sc->scopesym)
            continue;

        AggregateDeclaration *ad = sc->scopesym->isClassDeclaration();
        if (ad)
            return ad;
        ad = sc->scopesym->isStructDeclaration();
        if (ad)
            return ad;
    }
    return NULL;
}

/*******************************************
 * For TemplateDeclarations, we need to remember the Scope
 * where it was declared. So mark the Scope as not
 * to be free'd.
 */

void Scope::setNoFree()
{
    //int i = 0;

    //printf("Scope::setNoFree(this = %p)\n", this);
    for (Scope *sc = this; sc; sc = sc->enclosing)
    {
        //printf("\tsc = %p\n", sc);
        sc->nofree = 1;

        assert(!(flags & SCOPEfree));
        //assert(sc != sc->enclosing);
        //assert(!sc->enclosing || sc != sc->enclosing->enclosing);
        //if (++i == 10)
            //assert(0);
    }
}

structalign_t Scope::alignment()
{
    if (aligndecl)
        return aligndecl->getAlignment(this);
    else
        return STRUCTALIGN_DEFAULT;
}

/************************************************
 * Given the failed search attempt, try to find
 * one with a close spelling.
 */

void *scope_search_fp(void *arg, const char *seed, int* cost)
{
    //printf("scope_search_fp('%s')\n", seed);

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

    Scope *sc = (Scope *)arg;
    Module::clearCache();
    Dsymbol *scopesym = NULL;
    Dsymbol *s = sc->search(Loc(), id, &scopesym, IgnoreErrors);
    if (s)
    {
        for (*cost = 0; sc; sc = sc->enclosing, (*cost)++)
            if (sc->scopesym == scopesym)
                break;
        if (scopesym != s->parent)
        {
            (*cost)++; // got to the symbol through an import
            if (s->prot().kind == PROTprivate)
                return NULL;
        }
    }
    return (void*)s;
}

void Scope::deprecation10378(Loc loc, Dsymbol *sold, Dsymbol *snew)
{
    // Bugzilla 15857
    //
    // The overloadset found via the new lookup rules is either
    // equal or a subset of the overloadset found via the old
    // lookup rules, so it suffices to compare the dimension to
    // check for equality.
    OverloadSet *osold = NULL;
    OverloadSet *osnew = NULL;
    if (sold && (osold = sold->isOverloadSet()) != NULL &&
        snew && (osnew = snew->isOverloadSet()) != NULL &&
        osold->a.dim == osnew->a.dim)
        return;

    OutBuffer buf;
    buf.writestring("local import search method found ");
    if (osold)
        buf.printf("%s %s (%d overloads)", sold->kind(), sold->toPrettyChars(), (int)osold->a.dim);
    else if (sold)
        buf.printf("%s %s", sold->kind(), sold->toPrettyChars());
    else
        buf.writestring("nothing");
    buf.writestring(" instead of ");
    if (osnew)
        buf.printf("%s %s (%d overloads)", snew->kind(), snew->toPrettyChars(), (int)osnew->a.dim);
    else if (snew)
        buf.printf("%s %s", snew->kind(), snew->toPrettyChars());
    else
        buf.writestring("nothing");

    deprecation(loc, "%s", buf.peekString());
}

Dsymbol *Scope::search_correct(Identifier *ident)
{
    if (global.gag)
        return NULL;            // don't do it for speculative compiles; too time consuming

    return (Dsymbol *)speller(ident->toChars(), &scope_search_fp, this, idchars);
}

/************************************
 * Maybe `ident` was a C or C++ name. Check for that,
 * and suggest the D equivalent.
 * Params:
 *  ident = unknown identifier
 * Returns:
 *  D identifier string if found, null if not
 */
const char *Scope::search_correct_C(Identifier *ident)
{
    TOK tok;
    if (ident == Id::C_NULL)
        tok = TOKnull;
    else if (ident == Id::C_TRUE)
        tok = TOKtrue;
    else if (ident == Id::C_FALSE)
        tok = TOKfalse;
    else if (ident == Id::C_unsigned)
        tok = TOKuns32;
    else if (ident == Id::C_wchar_t)
        tok = global.params.isWindows ? TOKwchar : TOKdchar;
    else
        return NULL;
    return Token::toChars(tok);
}