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

#include "root/dsystem.h"               // memcmp()
#include "root/rmem.h"

#include "mars.h"
#include "init.h"
#include "declaration.h"
#include "attrib.h"
#include "cond.h"
#include "scope.h"
#include "id.h"
#include "expression.h"
#include "dsymbol.h"
#include "aggregate.h"
#include "module.h"
#include "parse.h"
#include "target.h"
#include "template.h"
#include "utf.h"
#include "mtype.h"

bool definitelyValueParameter(Expression *e);
Expression *semantic(Expression *e, Scope *sc);
StringExp *semanticString(Scope *sc, Expression *exp, const char *s);

/********************************* AttribDeclaration ****************************/

AttribDeclaration::AttribDeclaration(Dsymbols *decl)
        : Dsymbol()
{
    this->decl = decl;
}

Dsymbols *AttribDeclaration::include(Scope *, ScopeDsymbol *)
{
    return decl;
}

int AttribDeclaration::apply(Dsymbol_apply_ft_t fp, void *param)
{
    Dsymbols *d = include(_scope, NULL);

    if (d)
    {
        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            if (s)
            {
                if (s->apply(fp, param))
                    return 1;
            }
        }
    }
    return 0;
}

/****************************************
 * Create a new scope if one or more given attributes
 * are different from the sc's.
 * If the returned scope != sc, the caller should pop
 * the scope after it used.
 */
Scope *AttribDeclaration::createNewScope(Scope *sc,
        StorageClass stc, LINK linkage, CPPMANGLE cppmangle, Prot protection,
        int explicitProtection, AlignDeclaration *aligndecl, PINLINE inlining)
{
    Scope *sc2 = sc;
    if (stc != sc->stc ||
        linkage != sc->linkage ||
        cppmangle != sc->cppmangle ||
        !protection.isSubsetOf(sc->protection) ||
        explicitProtection != sc->explicitProtection ||
        aligndecl != sc->aligndecl ||
        inlining != sc->inlining)
    {
        // create new one for changes
        sc2 = sc->copy();
        sc2->stc = stc;
        sc2->linkage = linkage;
        sc2->cppmangle = cppmangle;
        sc2->protection = protection;
        sc2->explicitProtection = explicitProtection;
        sc2->aligndecl = aligndecl;
        sc2->inlining = inlining;
    }
    return sc2;
}

/****************************************
 * A hook point to supply scope for members.
 * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this.
 */
Scope *AttribDeclaration::newScope(Scope *sc)
{
    return sc;
}

void AttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
{
    Dsymbols *d = include(sc, sds);

    if (d)
    {
        Scope *sc2 = newScope(sc);

        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            //printf("\taddMember %s to %s\n", s->toChars(), sds->toChars());
            s->addMember(sc2, sds);
        }

        if (sc2 != sc)
            sc2->pop();
    }
}

void AttribDeclaration::setScope(Scope *sc)
{
    Dsymbols *d = include(sc, NULL);

    //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d);
    if (d)
    {
        Scope *sc2 = newScope(sc);

        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            s->setScope(sc2);
        }

        if (sc2 != sc)
            sc2->pop();
    }
}

void AttribDeclaration::importAll(Scope *sc)
{
    Dsymbols *d = include(sc, NULL);

    //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d);
    if (d)
    {
        Scope *sc2 = newScope(sc);

        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            s->importAll(sc2);
        }

        if (sc2 != sc)
            sc2->pop();
    }
}

void AttribDeclaration::semantic(Scope *sc)
{
    if (semanticRun != PASSinit)
        return;
    semanticRun = PASSsemantic;
    Dsymbols *d = include(sc, NULL);

    //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
    if (d)
    {
        Scope *sc2 = newScope(sc);

        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            s->semantic(sc2);
        }

        if (sc2 != sc)
            sc2->pop();
    }
    semanticRun = PASSsemanticdone;
}

void AttribDeclaration::semantic2(Scope *sc)
{
    Dsymbols *d = include(sc, NULL);

    if (d)
    {
        Scope *sc2 = newScope(sc);

        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            s->semantic2(sc2);
        }

        if (sc2 != sc)
            sc2->pop();
    }
}

void AttribDeclaration::semantic3(Scope *sc)
{
    Dsymbols *d = include(sc, NULL);

    if (d)
    {
        Scope *sc2 = newScope(sc);

        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            s->semantic3(sc2);
        }

        if (sc2 != sc)
            sc2->pop();
    }
}

void AttribDeclaration::addComment(const utf8_t *comment)
{
    //printf("AttribDeclaration::addComment %s\n", comment);
    if (comment)
    {
        Dsymbols *d = include(NULL, NULL);

        if (d)
        {
            for (size_t i = 0; i < d->dim; i++)
            {
                Dsymbol *s = (*d)[i];
                //printf("AttribDeclaration::addComment %s\n", s->toChars());
                s->addComment(comment);
            }
        }
    }
}

void AttribDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
{
    Dsymbols *d = include(NULL, NULL);

    if (d)
    {
        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            s->setFieldOffset(ad, poffset, isunion);
        }
    }
}

bool AttribDeclaration::hasPointers()
{
    Dsymbols *d = include(NULL, NULL);

    if (d)
    {
        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            if (s->hasPointers())
                return true;
        }
    }
    return false;
}

bool AttribDeclaration::hasStaticCtorOrDtor()
{
    Dsymbols *d = include(NULL, NULL);

    if (d)
    {
        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            if (s->hasStaticCtorOrDtor())
                return true;
        }
    }
    return false;
}

const char *AttribDeclaration::kind() const
{
    return "attribute";
}

bool AttribDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
{
    Dsymbols *d = include(NULL, NULL);

    return Dsymbol::oneMembers(d, ps, ident);
}

void AttribDeclaration::checkCtorConstInit()
{
    Dsymbols *d = include(NULL, NULL);

    if (d)
    {
        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            s->checkCtorConstInit();
        }
    }
}

/****************************************
 */

void AttribDeclaration::addLocalClass(ClassDeclarations *aclasses)
{
    Dsymbols *d = include(NULL, NULL);

    if (d)
    {
        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            s->addLocalClass(aclasses);
        }
    }
}

/************************* StorageClassDeclaration ****************************/

StorageClassDeclaration::StorageClassDeclaration(StorageClass stc, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    this->stc = stc;
}

Dsymbol *StorageClassDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    return new StorageClassDeclaration(stc, Dsymbol::arraySyntaxCopy(decl));
}

bool StorageClassDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
{
    bool t = Dsymbol::oneMembers(decl, ps, ident);
    if (t && *ps)
    {
        /* This is to deal with the following case:
         * struct Tick {
         *   template to(T) { const T to() { ... } }
         * }
         * For eponymous function templates, the 'const' needs to get attached to 'to'
         * before the semantic analysis of 'to', so that template overloading based on the
         * 'this' pointer can be successful.
         */

        FuncDeclaration *fd = (*ps)->isFuncDeclaration();
        if (fd)
        {
            /* Use storage_class2 instead of storage_class otherwise when we do .di generation
             * we'll wind up with 'const const' rather than 'const'.
             */
            /* Don't think we need to worry about mutually exclusive storage classes here
             */
            fd->storage_class2 |= stc;
        }
    }
    return t;
}

void StorageClassDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
{
    Dsymbols *d = include(sc, sds);
    if (d)
    {
        Scope *sc2 = newScope(sc);
        for (size_t i = 0; i < d->dim; i++)
        {
            Dsymbol *s = (*d)[i];
            //printf("\taddMember %s to %s\n", s->toChars(), sds->toChars());
            // STClocal needs to be attached before the member is added to the scope (because it influences the parent symbol)
            if (Declaration *decl = s->isDeclaration())
            {
                decl->storage_class |= stc & STClocal;
                if (StorageClassDeclaration *sdecl = s->isStorageClassDeclaration())
                {
                    sdecl->stc |= stc & STClocal;
                }
            }
            s->addMember(sc2, sds);
        }
        if (sc2 != sc)
            sc2->pop();
    }
}

Scope *StorageClassDeclaration::newScope(Scope *sc)
{
    StorageClass scstc = sc->stc;

    /* These sets of storage classes are mutually exclusive,
     * so choose the innermost or most recent one.
     */
    if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest))
        scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest);
    if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared))
        scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared);
    if (stc & (STCconst | STCimmutable | STCmanifest))
        scstc &= ~(STCconst | STCimmutable | STCmanifest);
    if (stc & (STCgshared | STCshared | STCtls))
        scstc &= ~(STCgshared | STCshared | STCtls);
    if (stc & (STCsafe | STCtrusted | STCsystem))
        scstc &= ~(STCsafe | STCtrusted | STCsystem);
    scstc |= stc;
    //printf("scstc = x%llx\n", scstc);

    return createNewScope(sc, scstc, sc->linkage, sc->cppmangle,
        sc->protection, sc->explicitProtection, sc->aligndecl,
        sc->inlining);
}

/********************************* DeprecatedDeclaration ****************************/

DeprecatedDeclaration::DeprecatedDeclaration(Expression *msg, Dsymbols *decl)
        : StorageClassDeclaration(STCdeprecated, decl)
{
    this->msg = msg;
    this->msgstr = NULL;
}

Dsymbol *DeprecatedDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    return new DeprecatedDeclaration(msg->syntaxCopy(), Dsymbol::arraySyntaxCopy(decl));
}

/**
 * Provides a new scope with `STCdeprecated` and `Scope.depdecl` set
 *
 * Calls `StorageClassDeclaration.newScope` (as it must be called or copied
 * in any function overriding `newScope`), then set the `Scope`'s depdecl.
 *
 * Returns:
 *   Always a new scope, to use for this `DeprecatedDeclaration`'s members.
 */
Scope *DeprecatedDeclaration::newScope(Scope *sc)
{
    Scope *scx = StorageClassDeclaration::newScope(sc);
    // The enclosing scope is deprecated as well
    if (scx == sc)
        scx = sc->push();
    scx->depdecl = this;
    return scx;
}

void DeprecatedDeclaration::setScope(Scope *sc)
{
    //printf("DeprecatedDeclaration::setScope() %p\n", this);
    if (decl)
        Dsymbol::setScope(sc); // for forward reference
    return AttribDeclaration::setScope(sc);
}

/**
 * Run the DeprecatedDeclaration's semantic2 phase then its members.
 *
 * The message set via a `DeprecatedDeclaration` can be either of:
 * - a string literal
 * - an enum
 * - a static immutable
 * So we need to call ctfe to resolve it.
 * Afterward forwards to the members' semantic2.
 */
void DeprecatedDeclaration::semantic2(Scope *sc)
{
    getMessage();
    StorageClassDeclaration::semantic2(sc);
}

const char *DeprecatedDeclaration::getMessage()
{
    if (Scope *sc = _scope)
    {
        _scope = NULL;

        sc = sc->startCTFE();
        msg = ::semantic(msg, sc);
        msg = resolveProperties(sc, msg);
        sc = sc->endCTFE();
        msg = msg->ctfeInterpret();

        if (StringExp *se = msg->toStringExp())
            msgstr = (char *)se->string;
        else
            msg->error("compile time constant expected, not '%s'", msg->toChars());
    }
    return msgstr;
}

/********************************* LinkDeclaration ****************************/

LinkDeclaration::LinkDeclaration(LINK p, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    //printf("LinkDeclaration(linkage = %d, decl = %p)\n", p, decl);
    linkage = (p == LINKsystem) ? Target::systemLinkage() : p;
}

LinkDeclaration *LinkDeclaration::create(LINK p, Dsymbols *decl)
{
    return new LinkDeclaration(p, decl);
}

Dsymbol *LinkDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    return new LinkDeclaration(linkage, Dsymbol::arraySyntaxCopy(decl));
}

Scope *LinkDeclaration::newScope(Scope *sc)
{
    return createNewScope(sc, sc->stc, this->linkage, sc->cppmangle,
        sc->protection, sc->explicitProtection, sc->aligndecl,
        sc->inlining);
}

const char *LinkDeclaration::toChars()
{
    return "extern ()";
}

/********************************* CPPMangleDeclaration ****************************/

CPPMangleDeclaration::CPPMangleDeclaration(CPPMANGLE p, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    //printf("CPPMangleDeclaration(cppmangle = %d, decl = %p)\n", p, decl);
    cppmangle = p;
}

Dsymbol *CPPMangleDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    return new CPPMangleDeclaration(cppmangle, Dsymbol::arraySyntaxCopy(decl));
}

Scope *CPPMangleDeclaration::newScope(Scope *sc)
{
    return createNewScope(sc, sc->stc, LINKcpp, this->cppmangle,
        sc->protection, sc->explicitProtection, sc->aligndecl,
        sc->inlining);
}

const char *CPPMangleDeclaration::toChars()
{
    return "extern ()";
}

/********************************* ProtDeclaration ****************************/

/**
 * Params:
 *  loc = source location of attribute token
 *  p = protection attribute data
 *  decl = declarations which are affected by this protection attribute
 */
ProtDeclaration::ProtDeclaration(Loc loc, Prot p, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    this->loc = loc;
    this->protection = p;
    this->pkg_identifiers = NULL;
    //printf("decl = %p\n", decl);
}

/**
 * Params:
 *  loc = source location of attribute token
 *  pkg_identifiers = list of identifiers for a qualified package name
 *  decl = declarations which are affected by this protection attribute
 */
ProtDeclaration::ProtDeclaration(Loc loc, Identifiers* pkg_identifiers, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    this->loc = loc;
    this->protection.kind = PROTpackage;
    this->protection.pkg  = NULL;
    this->pkg_identifiers = pkg_identifiers;
}

Dsymbol *ProtDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    if (protection.kind == PROTpackage)
        return new ProtDeclaration(this->loc, pkg_identifiers, Dsymbol::arraySyntaxCopy(decl));
    else
        return new ProtDeclaration(this->loc, protection, Dsymbol::arraySyntaxCopy(decl));
}

Scope *ProtDeclaration::newScope(Scope *sc)
{
    if (pkg_identifiers)
        semantic(sc);
    return createNewScope(sc, sc->stc, sc->linkage, sc->cppmangle,
        this->protection, 1, sc->aligndecl,
        sc->inlining);
}

void ProtDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
{
    if (pkg_identifiers)
    {
        Dsymbol* tmp;
        Package::resolve(pkg_identifiers, &tmp, NULL);
        protection.pkg = tmp ? tmp->isPackage() : NULL;
        pkg_identifiers = NULL;
    }

    if (protection.kind == PROTpackage && protection.pkg && sc->_module)
    {
        Module *m = sc->_module;
        Package* pkg = m->parent ? m->parent->isPackage() : NULL;
        if (!pkg || !protection.pkg->isAncestorPackageOf(pkg))
            error("does not bind to one of ancestor packages of module '%s'",
               m->toPrettyChars(true));
    }

    return AttribDeclaration::addMember(sc, sds);
}

const char *ProtDeclaration::kind() const
{
    return "protection attribute";
}

const char *ProtDeclaration::toPrettyChars(bool)
{
    assert(protection.kind > PROTundefined);

    OutBuffer buf;
    buf.writeByte('\'');
    protectionToBuffer(&buf, protection);
    buf.writeByte('\'');
    return buf.extractString();
}

/********************************* AlignDeclaration ****************************/

AlignDeclaration::AlignDeclaration(Loc loc, Expression *ealign, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    this->loc = loc;
    this->ealign = ealign;
    this->salign = 0;
}

Dsymbol *AlignDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    return new AlignDeclaration(loc,
        ealign ? ealign->syntaxCopy() : NULL,
        Dsymbol::arraySyntaxCopy(decl));
}

Scope *AlignDeclaration::newScope(Scope *sc)
{
    return createNewScope(sc, sc->stc, sc->linkage, sc->cppmangle,
        sc->protection, sc->explicitProtection, this,
        sc->inlining);
}

void AlignDeclaration::semantic2(Scope *sc)
{
    getAlignment(sc);
    AttribDeclaration::semantic2(sc);
}

structalign_t AlignDeclaration::getAlignment(Scope *sc)
{
    if (salign != 0)
        return salign;

    if (!ealign)
        return salign = STRUCTALIGN_DEFAULT;

    sc = sc->startCTFE();
    ealign = ::semantic(ealign, sc);
    ealign = resolveProperties(sc, ealign);
    sc = sc->endCTFE();
    ealign = ealign->ctfeInterpret();

    if (ealign->op == TOKerror)
        return salign = STRUCTALIGN_DEFAULT;

    Type *tb = ealign->type->toBasetype();
    sinteger_t n = ealign->toInteger();

    if (n < 1 || n & (n - 1) || STRUCTALIGN_DEFAULT < n || !tb->isintegral())
    {
        ::error(loc, "alignment must be an integer positive power of 2, not %s", ealign->toChars());
        return salign = STRUCTALIGN_DEFAULT;
    }

    return salign = (structalign_t)n;
}

/********************************* AnonDeclaration ****************************/

AnonDeclaration::AnonDeclaration(Loc loc, bool isunion, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    this->loc = loc;
    this->isunion = isunion;
    this->sem = 0;
    this->anonoffset = 0;
    this->anonstructsize = 0;
    this->anonalignsize = 0;
}

Dsymbol *AnonDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    return new AnonDeclaration(loc, isunion, Dsymbol::arraySyntaxCopy(decl));
}

void AnonDeclaration::setScope(Scope *sc)
{
    //printf("AnonDeclaration::setScope() %p\n", this);
    if (decl)
        Dsymbol::setScope(sc);
    AttribDeclaration::setScope(sc);
}

void AnonDeclaration::semantic(Scope *sc)
{
    //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this);

    assert(sc->parent);

    Dsymbol *p = sc->parent->pastMixin();
    AggregateDeclaration *ad = p->isAggregateDeclaration();
    if (!ad)
    {
        ::error(loc, "%s can only be a part of an aggregate, not %s %s",
            kind(), p->kind(), p->toChars());
        return;
    }

    if (decl)
    {
        sc = sc->push();
        sc->stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared);
        sc->inunion = isunion;
        sc->flags = 0;

        for (size_t i = 0; i < decl->dim; i++)
        {
            Dsymbol *s = (*decl)[i];
            s->semantic(sc);
        }
        sc = sc->pop();
    }
}

void AnonDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
{
    //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this);

    if (decl)
    {
        /* This works by treating an AnonDeclaration as an aggregate 'member',
         * so in order to place that member we need to compute the member's
         * size and alignment.
         */

        size_t fieldstart = ad->fields.dim;

        /* Hackishly hijack ad's structsize and alignsize fields
         * for use in our fake anon aggregate member.
         */
        unsigned savestructsize = ad->structsize;
        unsigned savealignsize  = ad->alignsize;
        ad->structsize = 0;
        ad->alignsize = 0;

        unsigned offset = 0;
        for (size_t i = 0; i < decl->dim; i++)
        {
            Dsymbol *s = (*decl)[i];
            s->setFieldOffset(ad, &offset, this->isunion);
            if (this->isunion)
                offset = 0;
        }

        /* Bugzilla 13613: If the fields in this->members had been already
         * added in ad->fields, just update *poffset for the subsequent
         * field offset calculation.
         */
        if (fieldstart == ad->fields.dim)
        {
            ad->structsize = savestructsize;
            ad->alignsize  = savealignsize;
            *poffset = ad->structsize;
            return;
        }

        anonstructsize = ad->structsize;
        anonalignsize  = ad->alignsize;
        ad->structsize = savestructsize;
        ad->alignsize  = savealignsize;

        // 0 sized structs are set to 1 byte
        // TODO: is this corect hebavior?
        if (anonstructsize == 0)
        {
            anonstructsize = 1;
            anonalignsize = 1;
        }

        assert(_scope);
        structalign_t alignment = _scope->alignment();

        /* Given the anon 'member's size and alignment,
         * go ahead and place it.
         */
        anonoffset = AggregateDeclaration::placeField(
                poffset,
                anonstructsize, anonalignsize, alignment,
                &ad->structsize, &ad->alignsize,
                isunion);

        // Add to the anon fields the base offset of this anonymous aggregate
        //printf("anon fields, anonoffset = %d\n", anonoffset);
        for (size_t i = fieldstart; i < ad->fields.dim; i++)
        {
            VarDeclaration *v = ad->fields[i];
            //printf("\t[%d] %s %d\n", i, v->toChars(), v->offset);
            v->offset += anonoffset;
        }
    }
}

const char *AnonDeclaration::kind() const
{
    return (isunion ? "anonymous union" : "anonymous struct");
}

/********************************* PragmaDeclaration ****************************/

PragmaDeclaration::PragmaDeclaration(Loc loc, Identifier *ident, Expressions *args, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    this->loc = loc;
    this->ident = ident;
    this->args = args;
}

Dsymbol *PragmaDeclaration::syntaxCopy(Dsymbol *s)
{
    //printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars());
    assert(!s);
    return new PragmaDeclaration(loc, ident,
        Expression::arraySyntaxCopy(args),
        Dsymbol::arraySyntaxCopy(decl));
}

Scope *PragmaDeclaration::newScope(Scope *sc)
{
    if (ident == Id::Pinline)
    {
        PINLINE inlining = PINLINEdefault;
        if (!args || args->dim == 0)
            inlining = PINLINEdefault;
        else if (args->dim != 1)
        {
            error("one boolean expression expected for pragma(inline), not %d", args->dim);
            args->setDim(1);
            (*args)[0] = new ErrorExp();
        }
        else
        {
            Expression *e = (*args)[0];

            if (e->op != TOKint64 || !e->type->equals(Type::tbool))
            {
                if (e->op != TOKerror)
                {
                    error("pragma(inline, true or false) expected, not %s", e->toChars());
                    (*args)[0] = new ErrorExp();
                }
            }
            else if (e->isBool(true))
                inlining = PINLINEalways;
            else if (e->isBool(false))
                inlining = PINLINEnever;
        }

        return createNewScope(sc, sc->stc, sc->linkage, sc->cppmangle,
            sc->protection, sc->explicitProtection, sc->aligndecl,
            inlining);
    }
    return sc;
}

static unsigned setMangleOverride(Dsymbol *s, char *sym)
{
    AttribDeclaration *ad = s->isAttribDeclaration();

    if (ad)
    {
        Dsymbols *decls = ad->include(NULL, NULL);
        unsigned nestedCount = 0;

        if (decls && decls->dim)
            for (size_t i = 0; i < decls->dim; ++i)
                nestedCount += setMangleOverride((*decls)[i], sym);

        return nestedCount;
    }
    else if (s->isFuncDeclaration() || s->isVarDeclaration())
    {
        s->isDeclaration()->mangleOverride = sym;
        return 1;
    }
    else
        return 0;
}

void PragmaDeclaration::semantic(Scope *sc)
{
    // Should be merged with PragmaStatement

    //printf("\tPragmaDeclaration::semantic '%s'\n",toChars());
    if (ident == Id::msg)
    {
        if (args)
        {
            for (size_t i = 0; i < args->dim; i++)
            {
                Expression *e = (*args)[i];

                sc = sc->startCTFE();
                e = ::semantic(e, sc);
                e = resolveProperties(sc, e);
                sc = sc->endCTFE();

                // pragma(msg) is allowed to contain types as well as expressions
                e = ctfeInterpretForPragmaMsg(e);
                if (e->op == TOKerror)
                {
                    errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i]->toChars());
                    return;
                }
                StringExp *se = e->toStringExp();
                if (se)
                {
                    se = se->toUTF8(sc);
                    fprintf(stderr, "%.*s", (int)se->len, (char *)se->string);
                }
                else
                    fprintf(stderr, "%s", e->toChars());
            }
            fprintf(stderr, "\n");
        }
        goto Lnodecl;
    }
    else if (ident == Id::lib)
    {
        if (!args || args->dim != 1)
            error("string expected for library name");
        else
        {
            StringExp *se = semanticString(sc, (*args)[0], "library name");
            if (!se)
                goto Lnodecl;
            (*args)[0] = se;

            char *name = (char *)mem.xmalloc(se->len + 1);
            memcpy(name, se->string, se->len);
            name[se->len] = 0;
            if (global.params.verbose)
                message("library   %s", name);
            if (global.params.moduleDeps && !global.params.moduleDepsFile)
            {
                OutBuffer *ob = global.params.moduleDeps;
                Module *imod = sc->instantiatingModule();
                ob->writestring("depsLib ");
                ob->writestring(imod->toPrettyChars());
                ob->writestring(" (");
                escapePath(ob, imod->srcfile->toChars());
                ob->writestring(") : ");
                ob->writestring((char *) name);
                ob->writenl();
            }
            mem.xfree(name);
        }
        goto Lnodecl;
    }
    else if (ident == Id::startaddress)
    {
        if (!args || args->dim != 1)
            error("function name expected for start address");
        else
        {
            /* Bugzilla 11980:
             * resolveProperties and ctfeInterpret call are not necessary.
             */
            Expression *e = (*args)[0];

            sc = sc->startCTFE();
            e = ::semantic(e, sc);
            sc = sc->endCTFE();

            (*args)[0] = e;
            Dsymbol *sa = getDsymbol(e);
            if (!sa || !sa->isFuncDeclaration())
                error("function name expected for start address, not '%s'", e->toChars());
        }
        goto Lnodecl;
    }
    else if (ident == Id::Pinline)
    {
        goto Ldecl;
    }
    else if (ident == Id::mangle)
    {
        if (!args)
            args = new Expressions();
        if (args->dim != 1)
        {
            error("string expected for mangled name");
            args->setDim(1);
            (*args)[0] = new ErrorExp();    // error recovery
            goto Ldecl;
        }

        StringExp *se = semanticString(sc, (*args)[0], "mangled name");
        if (!se)
            goto Ldecl;
        (*args)[0] = se; // Will be used for later

        if (!se->len)
        {
            error("zero-length string not allowed for mangled name");
            goto Ldecl;
        }
        if (se->sz != 1)
        {
            error("mangled name characters can only be of type char");
            goto Ldecl;
        }

        /* Note: D language specification should not have any assumption about backend
         * implementation. Ideally pragma(mangle) can accept a string of any content.
         *
         * Therefore, this validation is compiler implementation specific.
         */
        for (size_t i = 0; i < se->len; )
        {
            utf8_t *p = (utf8_t *)se->string;
            dchar_t c = p[i];
            if (c < 0x80)
            {
                if ((c >= 'A' && c <= 'Z') ||
                    (c >= 'a' && c <= 'z') ||
                    (c >= '0' && c <= '9') ||
                    (c != 0 && strchr("$%().:?@[]_", c)))
                {
                    ++i;
                    continue;
                }
                else
                {
                    error("char 0x%02x not allowed in mangled name", c);
                    break;
                }
            }

            if (const char* msg = utf_decodeChar((utf8_t *)se->string, se->len, &i, &c))
            {
                error("%s", msg);
                break;
            }

            if (!isUniAlpha(c))
            {
                error("char 0x%04x not allowed in mangled name", c);
                break;
            }
        }
    }
    else if (global.params.ignoreUnsupportedPragmas)
    {
        if (global.params.verbose)
        {
            /* Print unrecognized pragmas
             */
            OutBuffer buf;
            buf.writestring(ident->toChars());
            if (args)
            {
                for (size_t i = 0; i < args->dim; i++)
                {
                    Expression *e = (*args)[i];

                    sc = sc->startCTFE();
                    e = ::semantic(e, sc);
                    e = resolveProperties(sc, e);
                    sc = sc->endCTFE();

                    e = e->ctfeInterpret();
                    if (i == 0)
                        buf.writestring(" (");
                    else
                        buf.writeByte(',');
                    buf.writestring(e->toChars());
                }
                if (args->dim)
                    buf.writeByte(')');
            }
            message("pragma    %s", buf.peekString());
        }
        goto Lnodecl;
    }
    else
        error("unrecognized pragma(%s)", ident->toChars());

Ldecl:
    if (decl)
    {
        Scope *sc2 = newScope(sc);

        for (size_t i = 0; i < decl->dim; i++)
        {
            Dsymbol *s = (*decl)[i];

            s->semantic(sc2);

            if (ident == Id::mangle)
            {
                assert(args && args->dim == 1);
                if (StringExp *se = (*args)[0]->toStringExp())
                {
                    char *name = (char *)mem.xmalloc(se->len + 1);
                    memcpy(name, se->string, se->len);
                    name[se->len] = 0;

                    unsigned cnt = setMangleOverride(s, name);
                    if (cnt > 1)
                        error("can only apply to a single declaration");
                }
            }
        }

        if (sc2 != sc)
            sc2->pop();
    }
    return;

Lnodecl:
    if (decl)
    {
        error("pragma is missing closing ';'");
        goto Ldecl; // do them anyway, to avoid segfaults.
    }
}

const char *PragmaDeclaration::kind() const
{
    return "pragma";
}

/********************************* ConditionalDeclaration ****************************/

ConditionalDeclaration::ConditionalDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl)
        : AttribDeclaration(decl)
{
    //printf("ConditionalDeclaration::ConditionalDeclaration()\n");
    this->condition = condition;
    this->elsedecl = elsedecl;
}

Dsymbol *ConditionalDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    return new ConditionalDeclaration(condition->syntaxCopy(),
        Dsymbol::arraySyntaxCopy(decl),
        Dsymbol::arraySyntaxCopy(elsedecl));
}

bool ConditionalDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
{
    //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition->inc);
    if (condition->inc)
    {
        Dsymbols *d = condition->include(NULL, NULL) ? decl : elsedecl;
        return Dsymbol::oneMembers(d, ps, ident);
    }
    else
    {
        bool res = (Dsymbol::oneMembers(    decl, ps, ident) && *ps == NULL &&
                    Dsymbol::oneMembers(elsedecl, ps, ident) && *ps == NULL);
        *ps = NULL;
        return res;
    }
}

// Decide if 'then' or 'else' code should be included

Dsymbols *ConditionalDeclaration::include(Scope *sc, ScopeDsymbol *sds)
{
    //printf("ConditionalDeclaration::include(sc = %p) _scope = %p\n", sc, _scope);
    assert(condition);
    return condition->include(_scope ? _scope : sc, sds) ? decl : elsedecl;
}

void ConditionalDeclaration::setScope(Scope *sc)
{
    Dsymbols *d = include(sc, NULL);

    //printf("\tConditionalDeclaration::setScope '%s', d = %p\n",toChars(), d);
    if (d)
    {
       for (size_t i = 0; i < d->dim; i++)
       {
           Dsymbol *s = (*d)[i];
           s->setScope(sc);
       }
    }
}

void ConditionalDeclaration::addComment(const utf8_t *comment)
{
    /* Because addComment is called by the parser, if we called
     * include() it would define a version before it was used.
     * But it's no problem to drill down to both decl and elsedecl,
     * so that's the workaround.
     */

    if (comment)
    {
        Dsymbols *d = decl;

        for (int j = 0; j < 2; j++)
        {
            if (d)
            {
                for (size_t i = 0; i < d->dim; i++)
                {
                    Dsymbol *s = (*d)[i];
                    //printf("ConditionalDeclaration::addComment %s\n", s->toChars());
                    s->addComment(comment);
                }
            }
            d = elsedecl;
        }
    }
}

/***************************** StaticIfDeclaration ****************************/

StaticIfDeclaration::StaticIfDeclaration(Condition *condition,
        Dsymbols *decl, Dsymbols *elsedecl)
        : ConditionalDeclaration(condition, decl, elsedecl)
{
    //printf("StaticIfDeclaration::StaticIfDeclaration()\n");
    scopesym = NULL;
    addisdone = false;
}

Dsymbol *StaticIfDeclaration::syntaxCopy(Dsymbol *s)
{
    assert(!s);
    return new StaticIfDeclaration(condition->syntaxCopy(),
        Dsymbol::arraySyntaxCopy(decl),
        Dsymbol::arraySyntaxCopy(elsedecl));
}

/****************************************
 * Different from other AttribDeclaration subclasses, include() call requires
 * the completion of addMember and setScope phases.
 */
Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *)
{
    //printf("StaticIfDeclaration::include(sc = %p) _scope = %p\n", sc, _scope);

    if (condition->inc == 0)
    {
        assert(scopesym);   // addMember is already done
        assert(_scope);      // setScope is already done

        Dsymbols *d = ConditionalDeclaration::include(_scope, scopesym);

        if (d && !addisdone)
        {
            // Add members lazily.
            for (size_t i = 0; i < d->dim; i++)
            {
                Dsymbol *s = (*d)[i];
                s->addMember(_scope, scopesym);
            }

            // Set the member scopes lazily.
            for (size_t i = 0; i < d->dim; i++)
            {
                Dsymbol *s = (*d)[i];
                s->setScope(_scope);
            }

            addisdone = true;
        }
        return d;
    }
    else
    {
        return ConditionalDeclaration::include(sc, scopesym);
    }
}

void StaticIfDeclaration::addMember(Scope *, ScopeDsymbol *sds)
{
    //printf("StaticIfDeclaration::addMember() '%s'\n", toChars());
    /* This is deferred until the condition evaluated later (by the include() call),
     * so that expressions in the condition can refer to declarations
     * in the same scope, such as:
     *
     * template Foo(int i)
     * {
     *     const int j = i + 1;
     *     static if (j == 3)
     *         const int k;
     * }
     */
    this->scopesym = sds;
}

void StaticIfDeclaration::importAll(Scope *)
{
    // do not evaluate condition before semantic pass
}

void StaticIfDeclaration::setScope(Scope *sc)
{
    // do not evaluate condition before semantic pass

    // But do set the scope, in case we need it for forward referencing
    Dsymbol::setScope(sc);
}

void StaticIfDeclaration::semantic(Scope *sc)
{
    AttribDeclaration::semantic(sc);
}

const char *StaticIfDeclaration::kind() const
{
    return "static if";
}

/***************************** CompileDeclaration *****************************/

// These are mixin declarations, like mixin("int x");

CompileDeclaration::CompileDeclaration(Loc loc, Expression *exp)
    : AttribDeclaration(NULL)
{
    //printf("CompileDeclaration(loc = %d)\n", loc.linnum);
    this->loc = loc;
    this->exp = exp;
    this->scopesym = NULL;
    this->compiled = false;
}

Dsymbol *CompileDeclaration::syntaxCopy(Dsymbol *)
{
    //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars());
    return new CompileDeclaration(loc, exp->syntaxCopy());
}

void CompileDeclaration::addMember(Scope *, ScopeDsymbol *sds)
{
    //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum);
    this->scopesym = sds;
}

void CompileDeclaration::setScope(Scope *sc)
{
    Dsymbol::setScope(sc);
}

void CompileDeclaration::compileIt(Scope *sc)
{
    //printf("CompileDeclaration::compileIt(loc = %d) %s\n", loc.linnum, exp->toChars());
    StringExp *se = semanticString(sc, exp, "argument to mixin");
    if (!se)
        return;
    se = se->toUTF8(sc);

    unsigned errors = global.errors;
    Parser p(loc, sc->_module, (utf8_t *)se->string, se->len, 0);
    p.nextToken();

    decl = p.parseDeclDefs(0);
    if (p.token.value != TOKeof)
        exp->error("incomplete mixin declaration (%s)", se->toChars());
    if (p.errors)
    {
        assert(global.errors != errors);
        decl = NULL;
    }
}

void CompileDeclaration::semantic(Scope *sc)
{
    //printf("CompileDeclaration::semantic()\n");

    if (!compiled)
    {
        compileIt(sc);
        AttribDeclaration::addMember(sc, scopesym);
        compiled = true;

        if (_scope && decl)
        {
            for (size_t i = 0; i < decl->dim; i++)
            {
                Dsymbol *s = (*decl)[i];
                s->setScope(_scope);
            }
        }
    }
    AttribDeclaration::semantic(sc);
}

const char *CompileDeclaration::kind() const
{
    return "mixin";
}

/***************************** UserAttributeDeclaration *****************************/

UserAttributeDeclaration::UserAttributeDeclaration(Expressions *atts, Dsymbols *decl)
        : AttribDeclaration(decl)
{
    //printf("UserAttributeDeclaration()\n");
    this->atts = atts;
}

Dsymbol *UserAttributeDeclaration::syntaxCopy(Dsymbol *s)
{
    //printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars());
    assert(!s);
    return new UserAttributeDeclaration(
        Expression::arraySyntaxCopy(this->atts),
        Dsymbol::arraySyntaxCopy(decl));
}

Scope *UserAttributeDeclaration::newScope(Scope *sc)
{
    Scope *sc2 = sc;
    if (atts && atts->dim)
    {
        // create new one for changes
        sc2 = sc->copy();
        sc2->userAttribDecl = this;
    }
    return sc2;
}

void UserAttributeDeclaration::setScope(Scope *sc)
{
    //printf("UserAttributeDeclaration::setScope() %p\n", this);
    if (decl)
        Dsymbol::setScope(sc);  // for forward reference of UDAs

    return AttribDeclaration::setScope(sc);
}

void UserAttributeDeclaration::semantic(Scope *sc)
{
    //printf("UserAttributeDeclaration::semantic() %p\n", this);
    if (decl && !_scope)
        Dsymbol::setScope(sc);  // for function local symbols

    return AttribDeclaration::semantic(sc);
}

static void udaExpressionEval(Scope *sc, Expressions *exps)
{
    for (size_t i = 0; i < exps->dim; i++)
    {
        Expression *e = (*exps)[i];
        if (e)
        {
            e = ::semantic(e, sc);
            if (definitelyValueParameter(e))
                e = e->ctfeInterpret();
            if (e->op == TOKtuple)
            {
                TupleExp *te = (TupleExp *)e;
                udaExpressionEval(sc, te->exps);
            }
            (*exps)[i] = e;
        }
    }
}

void UserAttributeDeclaration::semantic2(Scope *sc)
{
    if (decl && atts && atts->dim && _scope)
    {
        _scope = NULL;
        udaExpressionEval(sc, atts);
    }

    AttribDeclaration::semantic2(sc);
}

Expressions *UserAttributeDeclaration::concat(Expressions *udas1, Expressions *udas2)
{
    Expressions *udas;
    if (!udas1 || udas1->dim == 0)
        udas = udas2;
    else if (!udas2 || udas2->dim == 0)
        udas = udas1;
    else
    {
        /* Create a new tuple that combines them
         * (do not append to left operand, as this is a copy-on-write operation)
         */
        udas = new Expressions();
        udas->push(new TupleExp(Loc(), udas1));
        udas->push(new TupleExp(Loc(), udas2));
    }
    return udas;
}

Expressions *UserAttributeDeclaration::getAttributes()
{
    if (Scope *sc = _scope)
    {
        _scope = NULL;
        arrayExpressionSemantic(atts, sc);
    }

    Expressions *exps = new Expressions();
    if (userAttribDecl)
        exps->push(new TupleExp(Loc(), userAttribDecl->getAttributes()));
    if (atts && atts->dim)
        exps->push(new TupleExp(Loc(), atts));

    return exps;
}

const char *UserAttributeDeclaration::kind() const
{
    return "UserAttribute";
}