| |
| /* 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->syntaxCopy(), 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"; |
| } |