| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2021 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 |
| */ |
| |
| #include "root/dsystem.h" |
| #include "root/aav.h" |
| |
| #include "dsymbol.h" |
| #include "aggregate.h" |
| #include "aliasthis.h" |
| #include "attrib.h" |
| #include "cond.h" |
| #include "declaration.h" |
| #include "enum.h" |
| #include "errors.h" |
| #include "hdrgen.h" |
| #include "id.h" |
| #include "import.h" |
| #include "init.h" |
| #include "mars.h" |
| #include "module.h" |
| #include "nspace.h" |
| #include "objc.h" |
| #include "parse.h" |
| #include "scope.h" |
| #include "statement.h" |
| #include "staticassert.h" |
| #include "target.h" |
| #include "template.h" |
| #include "utf.h" |
| #include "version.h" |
| #include "visitor.h" |
| |
| bool allowsContractWithoutBody(FuncDeclaration *funcdecl); |
| bool checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad, size_t istart = 0); |
| VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e); |
| Initializer *inferType(Initializer *init, Scope *sc); |
| void MODtoBuffer(OutBuffer *buf, MOD mod); |
| bool reliesOnTident(Type *t, TemplateParameters *tparams = NULL, size_t iStart = 0); |
| bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps); |
| bool symbolIsVisible(Scope *sc, Dsymbol *s); |
| Objc *objc(); |
| |
| static unsigned setMangleOverride(Dsymbol *s, char *sym) |
| { |
| AttribDeclaration *ad = s->isAttribDeclaration(); |
| |
| if (ad) |
| { |
| Dsymbols *decls = ad->include(NULL); |
| unsigned nestedCount = 0; |
| |
| if (decls && decls->length) |
| for (size_t i = 0; i < decls->length; ++i) |
| nestedCount += setMangleOverride((*decls)[i], sym); |
| |
| return nestedCount; |
| } |
| else if (s->isFuncDeclaration() || s->isVarDeclaration()) |
| { |
| s->isDeclaration()->mangleOverride = sym; |
| return 1; |
| } |
| else |
| return 0; |
| } |
| |
| /********************************** |
| * Decide if attributes for this function can be inferred from examining |
| * the function body. |
| * Returns: |
| * true if can |
| */ |
| static bool canInferAttributes(FuncDeclaration *fd, Scope *sc) |
| { |
| if (!fd->fbody) |
| return false; |
| |
| if (fd->isVirtualMethod()) |
| return false; // since they may be overridden |
| |
| if (sc->func && |
| /********** this is for backwards compatibility for the moment ********/ |
| (!fd->isMember() || (sc->func->isSafeBypassingInference() && !fd->isInstantiated()))) |
| return true; |
| |
| if (fd->isFuncLiteralDeclaration() || // externs are not possible with literals |
| (fd->storage_class & STCinference) || // do attribute inference |
| (fd->inferRetType && !fd->isCtorDeclaration())) |
| return true; |
| |
| if (fd->isInstantiated()) |
| { |
| TemplateInstance *ti = fd->parent->isTemplateInstance(); |
| if (ti == NULL || ti->isTemplateMixin() || ti->tempdecl->ident == fd->ident) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /***************************************** |
| * Initialize for inferring the attributes of this function. |
| */ |
| static void initInferAttributes(FuncDeclaration *fd) |
| { |
| //printf("initInferAttributes() for %s\n", toPrettyChars()); |
| TypeFunction *tf = fd->type->toTypeFunction(); |
| if (tf->purity == PUREimpure) // purity not specified |
| fd->flags |= FUNCFLAGpurityInprocess; |
| |
| if (tf->trust == TRUSTdefault) |
| fd->flags |= FUNCFLAGsafetyInprocess; |
| |
| if (!tf->isnothrow) |
| fd->flags |= FUNCFLAGnothrowInprocess; |
| |
| if (!tf->isnogc) |
| fd->flags |= FUNCFLAGnogcInprocess; |
| |
| if (!fd->isVirtual() || fd->introducing) |
| fd->flags |= FUNCFLAGreturnInprocess; |
| |
| // Initialize for inferring STCscope |
| if (global.params.vsafe) |
| fd->flags |= FUNCFLAGinferScope; |
| } |
| |
| static void badObjectDotD(ClassDeclaration *cd) |
| { |
| cd->error("missing or corrupt object.d"); |
| fatal(); |
| } |
| |
| /* Bugzilla 12078, 12143 and 15733: |
| * While resolving base classes and interfaces, a base may refer |
| * the member of this derived class. In that time, if all bases of |
| * this class can be determined, we can go forward the semantc process |
| * beyond the Lancestorsdone. To do the recursive semantic analysis, |
| * temporarily set and unset `_scope` around exp(). |
| */ |
| static Type *resolveBase(ClassDeclaration *cd, Scope *sc, Scope *&scx, Type *type) |
| { |
| if (!scx) |
| { |
| scx = sc->copy(); |
| scx->setNoFree(); |
| } |
| cd->_scope = scx; |
| Type *t = typeSemantic(type, cd->loc, sc); |
| cd->_scope = NULL; |
| return t; |
| } |
| |
| static void resolveBase(ClassDeclaration *cd, Scope *sc, Scope *&scx, ClassDeclaration *sym) |
| { |
| if (!scx) |
| { |
| scx = sc->copy(); |
| scx->setNoFree(); |
| } |
| cd->_scope = scx; |
| dsymbolSemantic(sym, NULL); |
| cd->_scope = NULL; |
| } |
| |
| class DsymbolSemanticVisitor : public Visitor |
| { |
| public: |
| Scope *sc; |
| |
| DsymbolSemanticVisitor(Scope *sc) |
| { |
| this->sc = sc; |
| } |
| |
| void visit(Dsymbol *dsym) |
| { |
| dsym->error("%p has no semantic routine", dsym); |
| } |
| |
| void visit(ScopeDsymbol *) { } |
| void visit(Declaration *) { } |
| |
| void visit(AliasThis *dsym) |
| { |
| if (dsym->semanticRun != PASSinit) |
| return; |
| |
| if (dsym->_scope) |
| { |
| sc = dsym->_scope; |
| dsym->_scope = NULL; |
| } |
| |
| if (!sc) |
| return; |
| |
| dsym->semanticRun = PASSsemantic; |
| |
| Dsymbol *p = sc->parent->pastMixin(); |
| AggregateDeclaration *ad = p->isAggregateDeclaration(); |
| if (!ad) |
| { |
| error(dsym->loc, "alias this can only be a member of aggregate, not %s %s", |
| p->kind(), p->toChars()); |
| return; |
| } |
| |
| assert(ad->members); |
| Dsymbol *s = ad->search(dsym->loc, dsym->ident); |
| if (!s) |
| { |
| s = sc->search(dsym->loc, dsym->ident, NULL); |
| if (s) |
| error(dsym->loc, "%s is not a member of %s", s->toChars(), ad->toChars()); |
| else |
| error(dsym->loc, "undefined identifier %s", dsym->ident->toChars()); |
| return; |
| } |
| else if (ad->aliasthis && s != ad->aliasthis) |
| { |
| error(dsym->loc, "there can be only one alias this"); |
| return; |
| } |
| |
| if (ad->type->ty == Tstruct && ((TypeStruct *)ad->type)->sym != ad) |
| { |
| AggregateDeclaration *ad2 = ((TypeStruct *)ad->type)->sym; |
| assert(ad2->type == Type::terror); |
| ad->aliasthis = ad2->aliasthis; |
| return; |
| } |
| |
| /* disable the alias this conversion so the implicit conversion check |
| * doesn't use it. |
| */ |
| ad->aliasthis = NULL; |
| |
| Dsymbol *sx = s; |
| if (sx->isAliasDeclaration()) |
| sx = sx->toAlias(); |
| Declaration *d = sx->isDeclaration(); |
| if (d && !d->isTupleDeclaration()) |
| { |
| Type *t = d->type; |
| assert(t); |
| if (ad->type->implicitConvTo(t) > MATCHnomatch) |
| { |
| error(dsym->loc, "alias this is not reachable as %s already converts to %s", ad->toChars(), t->toChars()); |
| } |
| } |
| |
| ad->aliasthis = s; |
| dsym->semanticRun = PASSsemanticdone; |
| } |
| |
| void visit(AliasDeclaration *dsym) |
| { |
| if (dsym->semanticRun >= PASSsemanticdone) |
| return; |
| assert(dsym->semanticRun <= PASSsemantic); |
| |
| dsym->storage_class |= sc->stc & STCdeprecated; |
| dsym->protection = sc->protection; |
| dsym->userAttribDecl = sc->userAttribDecl; |
| |
| if (!sc->func && dsym->inNonRoot()) |
| return; |
| |
| aliasSemantic(dsym, sc); |
| } |
| |
| void visit(VarDeclaration *dsym) |
| { |
| //if (dsym->semanticRun > PASSinit) |
| // return; |
| //dsym->semanticRun = PASSsemantic; |
| |
| if (dsym->semanticRun >= PASSsemanticdone) |
| return; |
| |
| Scope *scx = NULL; |
| if (dsym->_scope) |
| { |
| sc = dsym->_scope; |
| scx = sc; |
| dsym->_scope = NULL; |
| } |
| |
| if (!sc) |
| return; |
| |
| dsym->semanticRun = PASSsemantic; |
| |
| /* Pick up storage classes from context, but except synchronized, |
| * override, abstract, and final. |
| */ |
| dsym->storage_class |= (sc->stc & ~(STCsynchronized | STCoverride | STCabstract | STCfinal)); |
| if (dsym->storage_class & STCextern && dsym->_init) |
| dsym->error("extern symbols cannot have initializers"); |
| |
| dsym->userAttribDecl = sc->userAttribDecl; |
| |
| AggregateDeclaration *ad = dsym->isThis(); |
| if (ad) |
| dsym->storage_class |= ad->storage_class & STC_TYPECTOR; |
| |
| /* If auto type inference, do the inference |
| */ |
| int inferred = 0; |
| if (!dsym->type) |
| { |
| dsym->inuse++; |
| |
| // Infering the type requires running semantic, |
| // so mark the scope as ctfe if required |
| bool needctfe = (dsym->storage_class & (STCmanifest | STCstatic)) != 0; |
| if (needctfe) sc = sc->startCTFE(); |
| |
| //printf("inferring type for %s with init %s\n", dsym->toChars(), dsym->_init->toChars()); |
| dsym->_init = inferType(dsym->_init, sc); |
| dsym->type = initializerToExpression(dsym->_init)->type; |
| |
| if (needctfe) sc = sc->endCTFE(); |
| |
| dsym->inuse--; |
| inferred = 1; |
| |
| /* This is a kludge to support the existing syntax for RAII |
| * declarations. |
| */ |
| dsym->storage_class &= ~STCauto; |
| dsym->originalType = dsym->type->syntaxCopy(); |
| } |
| else |
| { |
| if (!dsym->originalType) |
| dsym->originalType = dsym->type->syntaxCopy(); |
| |
| /* Prefix function attributes of variable declaration can affect |
| * its type: |
| * pure nothrow void function() fp; |
| * static assert(is(typeof(fp) == void function() pure nothrow)); |
| */ |
| Scope *sc2 = sc->push(); |
| sc2->stc |= (dsym->storage_class & STC_FUNCATTR); |
| dsym->inuse++; |
| dsym->type = typeSemantic(dsym->type, dsym->loc, sc2); |
| dsym->inuse--; |
| sc2->pop(); |
| } |
| //printf(" semantic type = %s\n", dsym->type ? dsym->type->toChars() : "null"); |
| if (dsym->type->ty == Terror) |
| dsym->errors = true; |
| |
| dsym->type->checkDeprecated(dsym->loc, sc); |
| dsym->linkage = sc->linkage; |
| dsym->parent = sc->parent; |
| //printf("this = %p, parent = %p, '%s'\n", dsym, dsym->parent, dsym->parent->toChars()); |
| dsym->protection = sc->protection; |
| |
| /* If scope's alignment is the default, use the type's alignment, |
| * otherwise the scope overrrides. |
| */ |
| dsym->alignment = sc->alignment(); |
| if (dsym->alignment == STRUCTALIGN_DEFAULT) |
| dsym->alignment = dsym->type->alignment(); // use type's alignment |
| |
| //printf("sc->stc = %x\n", sc->stc); |
| //printf("storage_class = x%x\n", dsym->storage_class); |
| |
| if (global.params.vcomplex) |
| dsym->type->checkComplexTransition(dsym->loc); |
| |
| // Calculate type size + safety checks |
| if (sc->func && !sc->intypeof) |
| { |
| if ((dsym->storage_class & STCgshared) && !dsym->isMember()) |
| { |
| if (sc->func->setUnsafe()) |
| dsym->error("__gshared not allowed in safe functions; use shared"); |
| } |
| } |
| |
| Dsymbol *parent = dsym->toParent(); |
| |
| Type *tb = dsym->type->toBasetype(); |
| Type *tbn = tb->baseElemOf(); |
| if (tb->ty == Tvoid && !(dsym->storage_class & STClazy)) |
| { |
| if (inferred) |
| { |
| dsym->error("type %s is inferred from initializer %s, and variables cannot be of type void", |
| dsym->type->toChars(), dsym->_init->toChars()); |
| } |
| else |
| dsym->error("variables cannot be of type void"); |
| dsym->type = Type::terror; |
| tb = dsym->type; |
| } |
| if (tb->ty == Tfunction) |
| { |
| dsym->error("cannot be declared to be a function"); |
| dsym->type = Type::terror; |
| tb = dsym->type; |
| } |
| if (tb->ty == Tstruct) |
| { |
| TypeStruct *ts = (TypeStruct *)tb; |
| if (!ts->sym->members) |
| { |
| dsym->error("no definition of struct `%s`", ts->toChars()); |
| |
| // Explain why the definition is required when it's part of another type |
| if (!dsym->type->isTypeStruct()) |
| { |
| // Prefer Loc of the dependant type |
| Dsymbol *s = dsym->type->toDsymbol(sc); |
| Loc loc = s ? s->loc : dsym->loc; |
| errorSupplemental(loc, "required by type `%s`", dsym->type->toChars()); |
| } |
| |
| // Flag variable as error to avoid invalid error messages due to unknown size |
| dsym->type = Type::terror; |
| } |
| } |
| if ((dsym->storage_class & STCauto) && !inferred) |
| dsym->error("storage class `auto` has no effect if type is not inferred, did you mean `scope`?"); |
| |
| if (tb->ty == Ttuple) |
| { |
| /* Instead, declare variables for each of the tuple elements |
| * and add those. |
| */ |
| TypeTuple *tt = (TypeTuple *)tb; |
| size_t nelems = Parameter::dim(tt->arguments); |
| Expression *ie = (dsym->_init && !dsym->_init->isVoidInitializer()) ? initializerToExpression(dsym->_init) : NULL; |
| if (ie) |
| ie = expressionSemantic(ie, sc); |
| |
| if (nelems > 0 && ie) |
| { |
| Expressions *iexps = new Expressions(); |
| iexps->push(ie); |
| |
| Expressions *exps = new Expressions(); |
| |
| for (size_t pos = 0; pos < iexps->length; pos++) |
| { |
| Lexpand1: |
| Expression *e = (*iexps)[pos]; |
| Parameter *arg = Parameter::getNth(tt->arguments, pos); |
| arg->type = typeSemantic(arg->type, dsym->loc, sc); |
| //printf("[%d] iexps->length = %d, ", pos, iexps->length); |
| //printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars()); |
| //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars()); |
| |
| if (e != ie) |
| { |
| if (iexps->length > nelems) |
| goto Lnomatch; |
| if (e->type->implicitConvTo(arg->type)) |
| continue; |
| } |
| |
| if (e->op == TOKtuple) |
| { |
| TupleExp *te = (TupleExp *)e; |
| if (iexps->length - 1 + te->exps->length > nelems) |
| goto Lnomatch; |
| |
| iexps->remove(pos); |
| iexps->insert(pos, te->exps); |
| (*iexps)[pos] = Expression::combine(te->e0, (*iexps)[pos]); |
| goto Lexpand1; |
| } |
| else if (isAliasThisTuple(e)) |
| { |
| VarDeclaration *v = copyToTemp(0, "__tup", e); |
| dsymbolSemantic(v, sc); |
| VarExp *ve = new VarExp(dsym->loc, v); |
| ve->type = e->type; |
| |
| exps->setDim(1); |
| (*exps)[0] = ve; |
| expandAliasThisTuples(exps, 0); |
| |
| for (size_t u = 0; u < exps->length ; u++) |
| { |
| Lexpand2: |
| Expression *ee = (*exps)[u]; |
| arg = Parameter::getNth(tt->arguments, pos + u); |
| arg->type = typeSemantic(arg->type, dsym->loc, sc); |
| //printf("[%d+%d] exps->length = %d, ", pos, u, exps->length); |
| //printf("ee = (%s %s, %s), ", Token::tochars[ee->op], ee->toChars(), ee->type->toChars()); |
| //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars()); |
| |
| size_t iexps_dim = iexps->length - 1 + exps->length; |
| if (iexps_dim > nelems) |
| goto Lnomatch; |
| if (ee->type->implicitConvTo(arg->type)) |
| continue; |
| |
| if (expandAliasThisTuples(exps, u) != -1) |
| goto Lexpand2; |
| } |
| |
| if ((*exps)[0] != ve) |
| { |
| Expression *e0 = (*exps)[0]; |
| (*exps)[0] = new CommaExp(dsym->loc, new DeclarationExp(dsym->loc, v), e0); |
| (*exps)[0]->type = e0->type; |
| |
| iexps->remove(pos); |
| iexps->insert(pos, exps); |
| goto Lexpand1; |
| } |
| } |
| } |
| if (iexps->length < nelems) |
| goto Lnomatch; |
| |
| ie = new TupleExp(dsym->_init->loc, iexps); |
| } |
| Lnomatch: |
| |
| if (ie && ie->op == TOKtuple) |
| { |
| TupleExp *te = (TupleExp *)ie; |
| size_t tedim = te->exps->length; |
| if (tedim != nelems) |
| { |
| error(dsym->loc, "tuple of %d elements cannot be assigned to tuple of %d elements", (int)tedim, (int)nelems); |
| for (size_t u = tedim; u < nelems; u++) // fill dummy expression |
| te->exps->push(new ErrorExp()); |
| } |
| } |
| |
| Objects *exps = new Objects(); |
| exps->setDim(nelems); |
| for (size_t i = 0; i < nelems; i++) |
| { |
| Parameter *arg = Parameter::getNth(tt->arguments, i); |
| |
| OutBuffer buf; |
| buf.printf("__%s_field_%llu", dsym->ident->toChars(), (ulonglong)i); |
| const char *name = buf.extractChars(); |
| Identifier *id = Identifier::idPool(name); |
| |
| Initializer *ti; |
| if (ie) |
| { |
| Expression *einit = ie; |
| if (ie->op == TOKtuple) |
| { |
| TupleExp *te = (TupleExp *)ie; |
| einit = (*te->exps)[i]; |
| if (i == 0) |
| einit = Expression::combine(te->e0, einit); |
| } |
| ti = new ExpInitializer(einit->loc, einit); |
| } |
| else |
| ti = dsym->_init ? dsym->_init->syntaxCopy() : NULL; |
| |
| VarDeclaration *v = new VarDeclaration(dsym->loc, arg->type, id, ti); |
| v->storage_class |= STCtemp | STClocal | dsym->storage_class; |
| if (arg->storageClass & STCparameter) |
| v->storage_class |= arg->storageClass; |
| //printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars()); |
| dsymbolSemantic(v, sc); |
| |
| if (sc->scopesym) |
| { |
| //printf("adding %s to %s\n", v->toChars(), sc->scopesym->toChars()); |
| if (sc->scopesym->members) |
| sc->scopesym->members->push(v); |
| } |
| |
| Expression *e = new DsymbolExp(dsym->loc, v); |
| (*exps)[i] = e; |
| } |
| TupleDeclaration *v2 = new TupleDeclaration(dsym->loc, dsym->ident, exps); |
| v2->parent = dsym->parent; |
| v2->isexp = true; |
| dsym->aliassym = v2; |
| dsym->semanticRun = PASSsemanticdone; |
| return; |
| } |
| |
| /* Storage class can modify the type |
| */ |
| dsym->type = dsym->type->addStorageClass(dsym->storage_class); |
| |
| /* Adjust storage class to reflect type |
| */ |
| if (dsym->type->isConst()) |
| { |
| dsym->storage_class |= STCconst; |
| if (dsym->type->isShared()) |
| dsym->storage_class |= STCshared; |
| } |
| else if (dsym->type->isImmutable()) |
| dsym->storage_class |= STCimmutable; |
| else if (dsym->type->isShared()) |
| dsym->storage_class |= STCshared; |
| else if (dsym->type->isWild()) |
| dsym->storage_class |= STCwild; |
| |
| if (StorageClass stc = dsym->storage_class & (STCsynchronized | STCoverride | STCabstract | STCfinal)) |
| { |
| if (stc == STCfinal) |
| dsym->error("cannot be final, perhaps you meant const?"); |
| else |
| { |
| OutBuffer buf; |
| stcToBuffer(&buf, stc); |
| dsym->error("cannot be %s", buf.peekChars()); |
| } |
| dsym->storage_class &= ~stc; // strip off |
| } |
| |
| if (dsym->storage_class & STCscope) |
| { |
| StorageClass stc = dsym->storage_class & (STCstatic | STCextern | STCmanifest | STCtls | STCgshared); |
| if (stc) |
| { |
| OutBuffer buf; |
| stcToBuffer(&buf, stc); |
| dsym->error("cannot be `scope` and `%s`", buf.peekChars()); |
| } |
| else if (dsym->isMember()) |
| { |
| dsym->error("field cannot be `scope`"); |
| } |
| else if (!dsym->type->hasPointers()) |
| { |
| dsym->storage_class &= ~STCscope; // silently ignore; may occur in generic code |
| } |
| } |
| |
| if (dsym->storage_class & (STCstatic | STCextern | STCmanifest | STCtemplateparameter | STCtls | STCgshared | STCctfe)) |
| { |
| } |
| else |
| { |
| AggregateDeclaration *aad = parent->isAggregateDeclaration(); |
| if (aad) |
| { |
| if (global.params.vfield && |
| dsym->storage_class & (STCconst | STCimmutable) && dsym->_init && !dsym->_init->isVoidInitializer()) |
| { |
| const char *s = (dsym->storage_class & STCimmutable) ? "immutable" : "const"; |
| message(dsym->loc, "`%s.%s` is `%s` field", ad->toPrettyChars(), dsym->toChars(), s); |
| } |
| dsym->storage_class |= STCfield; |
| if (tbn->ty == Tstruct && ((TypeStruct *)tbn)->sym->noDefaultCtor) |
| { |
| if (!dsym->isThisDeclaration() && !dsym->_init) |
| aad->noDefaultCtor = true; |
| } |
| } |
| |
| InterfaceDeclaration *id = parent->isInterfaceDeclaration(); |
| if (id) |
| { |
| dsym->error("field not allowed in interface"); |
| } |
| else if (aad && aad->sizeok == SIZEOKdone) |
| { |
| dsym->error("cannot be further field because it will change the determined %s size", aad->toChars()); |
| } |
| |
| /* Templates cannot add fields to aggregates |
| */ |
| TemplateInstance *ti = parent->isTemplateInstance(); |
| if (ti) |
| { |
| // Take care of nested templates |
| while (1) |
| { |
| TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance(); |
| if (!ti2) |
| break; |
| ti = ti2; |
| } |
| |
| // If it's a member template |
| AggregateDeclaration *ad2 = ti->tempdecl->isMember(); |
| if (ad2 && dsym->storage_class != STCundefined) |
| { |
| dsym->error("cannot use template to add field to aggregate `%s`", ad2->toChars()); |
| } |
| } |
| } |
| |
| if ((dsym->storage_class & (STCref | STCparameter | STCforeach | STCtemp | STCresult)) == STCref && dsym->ident != Id::This) |
| { |
| dsym->error("only parameters or foreach declarations can be ref"); |
| } |
| |
| if (dsym->type->hasWild()) |
| { |
| if (dsym->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCfield) || |
| dsym->isDataseg() |
| ) |
| { |
| dsym->error("only parameters or stack based variables can be inout"); |
| } |
| FuncDeclaration *func = sc->func; |
| if (func) |
| { |
| if (func->fes) |
| func = func->fes->func; |
| bool isWild = false; |
| for (FuncDeclaration *fd = func; fd; fd = fd->toParent2()->isFuncDeclaration()) |
| { |
| if (((TypeFunction *)fd->type)->iswild) |
| { |
| isWild = true; |
| break; |
| } |
| } |
| if (!isWild) |
| { |
| dsym->error("inout variables can only be declared inside inout functions"); |
| } |
| } |
| } |
| |
| if (!(dsym->storage_class & (STCctfe | STCref | STCresult)) && tbn->ty == Tstruct && |
| ((TypeStruct *)tbn)->sym->noDefaultCtor) |
| { |
| if (!dsym->_init) |
| { |
| if (dsym->isField()) |
| { |
| /* For fields, we'll check the constructor later to make sure it is initialized |
| */ |
| dsym->storage_class |= STCnodefaultctor; |
| } |
| else if (dsym->storage_class & STCparameter) |
| ; |
| else |
| dsym->error("default construction is disabled for type %s", dsym->type->toChars()); |
| } |
| } |
| |
| FuncDeclaration *fd = parent->isFuncDeclaration(); |
| if (dsym->type->isscope() && !(dsym->storage_class & STCnodtor)) |
| { |
| if (dsym->storage_class & (STCfield | STCout | STCref | STCstatic | STCmanifest | STCtls | STCgshared) || !fd) |
| { |
| dsym->error("globals, statics, fields, manifest constants, ref and out parameters cannot be scope"); |
| } |
| |
| if (!(dsym->storage_class & STCscope)) |
| { |
| if (!(dsym->storage_class & STCparameter) && dsym->ident != Id::withSym) |
| dsym->error("reference to scope class must be scope"); |
| } |
| } |
| |
| // Calculate type size + safety checks |
| if (sc->func && !sc->intypeof) |
| { |
| if (dsym->_init && dsym->_init->isVoidInitializer() && dsym->type->hasPointers()) // get type size |
| { |
| if (sc->func->setUnsafe()) |
| dsym->error("void initializers for pointers not allowed in safe functions"); |
| } |
| else if (!dsym->_init && |
| !(dsym->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCfield | STCparameter)) && |
| dsym->type->hasVoidInitPointers()) |
| { |
| if (sc->func->setUnsafe()) |
| dsym->error("void initializers for pointers not allowed in safe functions"); |
| } |
| } |
| |
| if (!dsym->_init && !fd) |
| { |
| // If not mutable, initializable by constructor only |
| dsym->storage_class |= STCctorinit; |
| } |
| |
| if (dsym->_init) |
| dsym->storage_class |= STCinit; // remember we had an explicit initializer |
| else if (dsym->storage_class & STCmanifest) |
| dsym->error("manifest constants must have initializers"); |
| |
| bool isBlit = false; |
| d_uns64 sz = 0; |
| if (!dsym->_init && !sc->inunion && !(dsym->storage_class & (STCstatic | STCgshared | STCextern)) && fd && |
| (!(dsym->storage_class & (STCfield | STCin | STCforeach | STCparameter | STCresult)) |
| || (dsym->storage_class & STCout)) && |
| (sz = dsym->type->size()) != 0) |
| { |
| // Provide a default initializer |
| //printf("Providing default initializer for '%s'\n", dsym->toChars()); |
| if (sz == SIZE_INVALID && dsym->type->ty != Terror) |
| dsym->error("size of type %s is invalid", dsym->type->toChars()); |
| |
| Type *tv = dsym->type; |
| while (tv->ty == Tsarray) // Don't skip Tenum |
| tv = tv->nextOf(); |
| if (tv->needsNested()) |
| { |
| /* Nested struct requires valid enclosing frame pointer. |
| * In StructLiteralExp::toElem(), it's calculated. |
| */ |
| assert(tv->toBasetype()->ty == Tstruct); |
| checkFrameAccess(dsym->loc, sc, ((TypeStruct *)tbn)->sym); |
| |
| Expression *e = tv->defaultInitLiteral(dsym->loc); |
| e = new BlitExp(dsym->loc, new VarExp(dsym->loc, dsym), e); |
| e = expressionSemantic(e, sc); |
| dsym->_init = new ExpInitializer(dsym->loc, e); |
| goto Ldtor; |
| } |
| if (tv->ty == Tstruct && ((TypeStruct *)tv)->sym->zeroInit == 1) |
| { |
| /* If a struct is all zeros, as a special case |
| * set it's initializer to the integer 0. |
| * In AssignExp::toElem(), we check for this and issue |
| * a memset() to initialize the struct. |
| * Must do same check in interpreter. |
| */ |
| Expression *e = new IntegerExp(dsym->loc, 0, Type::tint32); |
| e = new BlitExp(dsym->loc, new VarExp(dsym->loc, dsym), e); |
| e->type = dsym->type; // don't type check this, it would fail |
| dsym->_init = new ExpInitializer(dsym->loc, e); |
| goto Ldtor; |
| } |
| if (dsym->type->baseElemOf()->ty == Tvoid) |
| { |
| dsym->error("%s does not have a default initializer", dsym->type->toChars()); |
| } |
| else if (Expression *e = dsym->type->defaultInit(dsym->loc)) |
| { |
| dsym->_init = new ExpInitializer(dsym->loc, e); |
| } |
| // Default initializer is always a blit |
| isBlit = true; |
| } |
| |
| if (dsym->_init) |
| { |
| sc = sc->push(); |
| sc->stc &= ~(STC_TYPECTOR | STCpure | STCnothrow | STCnogc | STCref | STCdisable); |
| |
| ExpInitializer *ei = dsym->_init->isExpInitializer(); |
| if (ei) // Bugzilla 13424: Preset the required type to fail in FuncLiteralDeclaration::semantic3 |
| ei->exp = inferType(ei->exp, dsym->type); |
| |
| // If inside function, there is no semantic3() call |
| if (sc->func || sc->intypeof == 1) |
| { |
| // If local variable, use AssignExp to handle all the various |
| // possibilities. |
| if (fd && |
| !(dsym->storage_class & (STCmanifest | STCstatic | STCtls | STCgshared | STCextern)) && |
| !dsym->_init->isVoidInitializer()) |
| { |
| //printf("fd = '%s', var = '%s'\n", fd->toChars(), dsym->toChars()); |
| if (!ei) |
| { |
| ArrayInitializer *ai = dsym->_init->isArrayInitializer(); |
| Expression *e; |
| if (ai && tb->ty == Taarray) |
| e = ai->toAssocArrayLiteral(); |
| else |
| e = initializerToExpression(dsym->_init); |
| if (!e) |
| { |
| // Run semantic, but don't need to interpret |
| dsym->_init = initializerSemantic(dsym->_init, sc, dsym->type, INITnointerpret); |
| e = initializerToExpression(dsym->_init); |
| if (!e) |
| { |
| dsym->error("is not a static and cannot have static initializer"); |
| e = new ErrorExp(); |
| } |
| } |
| ei = new ExpInitializer(dsym->_init->loc, e); |
| dsym->_init = ei; |
| } |
| |
| Expression *exp = ei->exp; |
| Expression *e1 = new VarExp(dsym->loc, dsym); |
| if (isBlit) |
| exp = new BlitExp(dsym->loc, e1, exp); |
| else |
| exp = new ConstructExp(dsym->loc, e1, exp); |
| dsym->canassign++; |
| exp = expressionSemantic(exp, sc); |
| dsym->canassign--; |
| exp = exp->optimize(WANTvalue); |
| |
| if (exp->op == TOKerror) |
| { |
| dsym->_init = new ErrorInitializer(); |
| ei = NULL; |
| } |
| else |
| ei->exp = exp; |
| |
| if (ei && dsym->isScope()) |
| { |
| Expression *ex = ei->exp; |
| while (ex->op == TOKcomma) |
| ex = ((CommaExp *)ex)->e2; |
| if (ex->op == TOKblit || ex->op == TOKconstruct) |
| ex = ((AssignExp *)ex)->e2; |
| if (ex->op == TOKnew) |
| { |
| // See if initializer is a NewExp that can be allocated on the stack |
| NewExp *ne = (NewExp *)ex; |
| if (dsym->type->toBasetype()->ty == Tclass) |
| { |
| if (ne->newargs && ne->newargs->length > 1) |
| { |
| dsym->mynew = true; |
| } |
| else |
| { |
| ne->onstack = true; |
| dsym->onstack = true; |
| } |
| } |
| } |
| else if (ex->op == TOKfunction) |
| { |
| // or a delegate that doesn't escape a reference to the function |
| FuncDeclaration *f = ((FuncExp *)ex)->fd; |
| f->tookAddressOf--; |
| } |
| } |
| } |
| else |
| { |
| // Bugzilla 14166: Don't run CTFE for the temporary variables inside typeof |
| dsym->_init = initializerSemantic(dsym->_init, sc, dsym->type, sc->intypeof == 1 ? INITnointerpret : INITinterpret); |
| } |
| } |
| else if (parent->isAggregateDeclaration()) |
| { |
| dsym->_scope = scx ? scx : sc->copy(); |
| dsym->_scope->setNoFree(); |
| } |
| else if (dsym->storage_class & (STCconst | STCimmutable | STCmanifest) || |
| dsym->type->isConst() || dsym->type->isImmutable()) |
| { |
| /* Because we may need the results of a const declaration in a |
| * subsequent type, such as an array dimension, before semantic2() |
| * gets ordinarily run, try to run semantic2() now. |
| * Ignore failure. |
| */ |
| |
| if (!inferred) |
| { |
| unsigned errors = global.errors; |
| dsym->inuse++; |
| if (ei) |
| { |
| Expression *exp = ei->exp->syntaxCopy(); |
| |
| bool needctfe = dsym->isDataseg() || (dsym->storage_class & STCmanifest); |
| if (needctfe) sc = sc->startCTFE(); |
| exp = expressionSemantic(exp, sc); |
| exp = resolveProperties(sc, exp); |
| if (needctfe) sc = sc->endCTFE(); |
| |
| Type *tb2 = dsym->type->toBasetype(); |
| Type *ti = exp->type->toBasetype(); |
| |
| /* The problem is the following code: |
| * struct CopyTest { |
| * double x; |
| * this(double a) { x = a * 10.0;} |
| * this(this) { x += 2.0; } |
| * } |
| * const CopyTest z = CopyTest(5.3); // ok |
| * const CopyTest w = z; // not ok, postblit not run |
| * static assert(w.x == 55.0); |
| * because the postblit doesn't get run on the initialization of w. |
| */ |
| if (ti->ty == Tstruct) |
| { |
| StructDeclaration *sd = ((TypeStruct *)ti)->sym; |
| /* Look to see if initializer involves a copy constructor |
| * (which implies a postblit) |
| */ |
| // there is a copy constructor |
| // and exp is the same struct |
| if (sd->postblit && |
| tb2->toDsymbol(NULL) == sd) |
| { |
| // The only allowable initializer is a (non-copy) constructor |
| if (exp->isLvalue()) |
| dsym->error("of type struct %s uses this(this), which is not allowed in static initialization", tb2->toChars()); |
| } |
| } |
| ei->exp = exp; |
| } |
| dsym->_init = initializerSemantic(dsym->_init, sc, dsym->type, INITinterpret); |
| dsym->inuse--; |
| if (global.errors > errors) |
| { |
| dsym->_init = new ErrorInitializer(); |
| dsym->type = Type::terror; |
| } |
| } |
| else |
| { |
| dsym->_scope = scx ? scx : sc->copy(); |
| dsym->_scope->setNoFree(); |
| } |
| } |
| sc = sc->pop(); |
| } |
| |
| Ldtor: |
| /* Build code to execute destruction, if necessary |
| */ |
| dsym->edtor = dsym->callScopeDtor(sc); |
| if (dsym->edtor) |
| { |
| if (sc->func && dsym->storage_class & (STCstatic | STCgshared)) |
| dsym->edtor = expressionSemantic(dsym->edtor, sc->_module->_scope); |
| else |
| dsym->edtor = expressionSemantic(dsym->edtor, sc); |
| |
| #if 0 // currently disabled because of std.stdio.stdin, stdout and stderr |
| if (dsym->isDataseg() && !(dsym->storage_class & STCextern)) |
| dsym->error("static storage variables cannot have destructors"); |
| #endif |
| } |
| |
| dsym->semanticRun = PASSsemanticdone; |
| |
| if (dsym->type->toBasetype()->ty == Terror) |
| dsym->errors = true; |
| |
| if (sc->scopesym && !sc->scopesym->isAggregateDeclaration()) |
| { |
| for (ScopeDsymbol *sym = sc->scopesym; sym && dsym->endlinnum == 0; |
| sym = sym->parent ? sym->parent->isScopeDsymbol() : NULL) |
| dsym->endlinnum = sym->endlinnum; |
| } |
| } |
| |
| void visit(TypeInfoDeclaration *dsym) |
| { |
| assert(dsym->linkage == LINKc); |
| } |
| |
| void visit(Import *imp) |
| { |
| //printf("Import::semantic('%s') %s\n", toPrettyChars(), imp->id->toChars()); |
| if (imp->semanticRun > PASSinit) |
| return; |
| |
| if (imp->_scope) |
| { |
| sc = imp->_scope; |
| imp->_scope = NULL; |
| } |
| if (!sc) |
| return; |
| |
| imp->semanticRun = PASSsemantic; |
| |
| // Load if not already done so |
| if (!imp->mod) |
| { |
| imp->load(sc); |
| if (imp->mod) |
| imp->mod->importAll(NULL); |
| } |
| |
| if (imp->mod) |
| { |
| // Modules need a list of each imported module |
| //printf("%s imports %s\n", sc->_module->toChars(), imp->mod->toChars()); |
| sc->_module->aimports.push(imp->mod); |
| |
| if (sc->explicitProtection) |
| imp->protection = sc->protection; |
| |
| if (!imp->aliasId && !imp->names.length) // neither a selective nor a renamed import |
| { |
| ScopeDsymbol *scopesym = NULL; |
| if (sc->explicitProtection) |
| imp->protection = sc->protection.kind; |
| for (Scope *scd = sc; scd; scd = scd->enclosing) |
| { |
| if (!scd->scopesym) |
| continue; |
| scopesym = scd->scopesym; |
| break; |
| } |
| |
| if (!imp->isstatic) |
| { |
| scopesym->importScope(imp->mod, imp->protection); |
| } |
| |
| imp->addPackageAccess(scopesym); |
| } |
| |
| dsymbolSemantic(imp->mod, NULL); |
| |
| if (imp->mod->needmoduleinfo) |
| { |
| //printf("module4 %s because of %s\n", sc->_module->toChars(), imp->mod->toChars()); |
| sc->_module->needmoduleinfo = 1; |
| } |
| |
| sc = sc->push(imp->mod); |
| sc->protection = imp->protection; |
| for (size_t i = 0; i < imp->aliasdecls.length; i++) |
| { |
| AliasDeclaration *ad = imp->aliasdecls[i]; |
| //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), imp->aliases[i]->toChars(), imp->names[i]->toChars(), ad->_scope); |
| Dsymbol *sym = imp->mod->search(imp->loc, imp->names[i], IgnorePrivateImports); |
| if (sym) |
| { |
| if (!symbolIsVisible(sc, sym)) |
| imp->mod->error(imp->loc, "member `%s` is not visible from module `%s`", |
| imp->names[i]->toChars(), sc->_module->toChars()); |
| dsymbolSemantic(ad, sc); |
| // If the import declaration is in non-root module, |
| // analysis of the aliased symbol is deferred. |
| // Therefore, don't see the ad->aliassym or ad->type here. |
| } |
| else |
| { |
| Dsymbol *s = imp->mod->search_correct(imp->names[i]); |
| if (s) |
| imp->mod->error(imp->loc, "import `%s` not found, did you mean %s `%s`?", imp->names[i]->toChars(), s->kind(), s->toPrettyChars()); |
| else |
| imp->mod->error(imp->loc, "import `%s` not found", imp->names[i]->toChars()); |
| ad->type = Type::terror; |
| } |
| } |
| sc = sc->pop(); |
| } |
| |
| imp->semanticRun = PASSsemanticdone; |
| |
| // object self-imports itself, so skip that (Bugzilla 7547) |
| // don't list pseudo modules __entrypoint.d, __main.d (Bugzilla 11117, 11164) |
| if (global.params.moduleDeps != NULL && |
| !(imp->id == Id::object && sc->_module->ident == Id::object) && |
| sc->_module->ident != Id::entrypoint && |
| strcmp(sc->_module->ident->toChars(), "__main") != 0) |
| { |
| /* The grammar of the file is: |
| * ImportDeclaration |
| * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " |
| * ModuleAliasIdentifier ] "\n" |
| * |
| * BasicImportDeclaration |
| * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string" |
| * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" |
| * |
| * FilePath |
| * - any string with '(', ')' and '\' escaped with the '\' character |
| */ |
| |
| OutBuffer *ob = global.params.moduleDeps; |
| Module* imod = sc->instantiatingModule(); |
| if (!global.params.moduleDepsFile.length) |
| ob->writestring("depsImport "); |
| ob->writestring(imod->toPrettyChars()); |
| ob->writestring(" ("); |
| escapePath(ob, imod->srcfile->toChars()); |
| ob->writestring(") : "); |
| |
| // use protection instead of sc->protection because it couldn't be |
| // resolved yet, see the comment above |
| protectionToBuffer(ob, imp->protection); |
| ob->writeByte(' '); |
| if (imp->isstatic) |
| { |
| stcToBuffer(ob, STCstatic); |
| ob->writeByte(' '); |
| } |
| ob->writestring(": "); |
| |
| if (imp->packages) |
| { |
| for (size_t i = 0; i < imp->packages->length; i++) |
| { |
| Identifier *pid = (*imp->packages)[i]; |
| ob->printf("%s.", pid->toChars()); |
| } |
| } |
| |
| ob->writestring(imp->id->toChars()); |
| ob->writestring(" ("); |
| if (imp->mod) |
| escapePath(ob, imp->mod->srcfile->toChars()); |
| else |
| ob->writestring("???"); |
| ob->writeByte(')'); |
| |
| for (size_t i = 0; i < imp->names.length; i++) |
| { |
| if (i == 0) |
| ob->writeByte(':'); |
| else |
| ob->writeByte(','); |
| |
| Identifier *name = imp->names[i]; |
| Identifier *alias = imp->aliases[i]; |
| |
| if (!alias) |
| { |
| ob->printf("%s", name->toChars()); |
| alias = name; |
| } |
| else |
| ob->printf("%s=%s", alias->toChars(), name->toChars()); |
| } |
| |
| if (imp->aliasId) |
| ob->printf(" -> %s", imp->aliasId->toChars()); |
| |
| ob->writenl(); |
| } |
| |
| //printf("-Import::semantic('%s'), pkg = %p\n", imp->toChars(), imp->pkg); |
| } |
| |
| void attribSemantic(AttribDeclaration *ad) |
| { |
| if (ad->semanticRun != PASSinit) |
| return; |
| ad->semanticRun = PASSsemantic; |
| Dsymbols *d = ad->include(sc); |
| //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); |
| if (d) |
| { |
| Scope *sc2 = ad->newScope(sc); |
| bool errors = false; |
| for (size_t i = 0; i < d->length; i++) |
| { |
| Dsymbol *s = (*d)[i]; |
| dsymbolSemantic(s, sc2); |
| errors |= s->errors; |
| } |
| ad->errors |= errors; |
| if (sc2 != sc) |
| sc2->pop(); |
| } |
| ad->semanticRun = PASSsemanticdone; |
| } |
| |
| void visit(AttribDeclaration *atd) |
| { |
| attribSemantic(atd); |
| } |
| |
| void visit(AnonDeclaration *scd) |
| { |
| //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", scd); |
| assert(sc->parent); |
| Dsymbol *p = sc->parent->pastMixin(); |
| AggregateDeclaration *ad = p->isAggregateDeclaration(); |
| if (!ad) |
| { |
| error(scd->loc, "%s can only be a part of an aggregate, not %s %s", |
| scd->kind(), p->kind(), p->toChars()); |
| scd->errors = true; |
| return; |
| } |
| |
| if (scd->decl) |
| { |
| sc = sc->push(); |
| sc->stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared); |
| sc->inunion = scd->isunion; |
| sc->flags = 0; |
| |
| for (size_t i = 0; i < scd->decl->length; i++) |
| { |
| Dsymbol *s = (*scd->decl)[i]; |
| dsymbolSemantic(s, sc); |
| } |
| sc = sc->pop(); |
| } |
| } |
| |
| void visit(PragmaDeclaration *pd) |
| { |
| // Should be merged with PragmaStatement |
| //printf("\tPragmaDeclaration::semantic '%s'\n",toChars()); |
| if (pd->ident == Id::msg) |
| { |
| if (pd->args) |
| { |
| for (size_t i = 0; i < pd->args->length; i++) |
| { |
| Expression *e = (*pd->args)[i]; |
| |
| sc = sc->startCTFE(); |
| e = expressionSemantic(e, sc); |
| e = resolveProperties(sc, e); |
| sc = sc->endCTFE(); |
| e = ctfeInterpretForPragmaMsg(e); |
| if (e->op == TOKerror) |
| { |
| errorSupplemental(pd->loc, "while evaluating pragma(msg, %s)", (*pd->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 (pd->ident == Id::lib) |
| { |
| if (!pd->args || pd->args->length != 1) |
| pd->error("string expected for library name"); |
| else |
| { |
| StringExp *se = semanticString(sc, (*pd->args)[0], "library name"); |
| if (!se) |
| goto Lnodecl; |
| (*pd->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.length) |
| { |
| 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 (pd->ident == Id::startaddress) |
| { |
| if (!pd->args || pd->args->length != 1) |
| pd->error("function name expected for start address"); |
| else |
| { |
| /* Bugzilla 11980: |
| * resolveProperties and ctfeInterpret call are not necessary. |
| */ |
| Expression *e = (*pd->args)[0]; |
| |
| sc = sc->startCTFE(); |
| e = expressionSemantic(e, sc); |
| sc = sc->endCTFE(); |
| |
| (*pd->args)[0] = e; |
| Dsymbol *sa = getDsymbol(e); |
| if (!sa || !sa->isFuncDeclaration()) |
| pd->error("function name expected for start address, not `%s`", e->toChars()); |
| } |
| goto Lnodecl; |
| } |
| else if (pd->ident == Id::Pinline) |
| { |
| goto Ldecl; |
| } |
| else if (pd->ident == Id::mangle) |
| { |
| if (!pd->args) |
| pd->args = new Expressions(); |
| if (pd->args->length != 1) |
| { |
| pd->error("string expected for mangled name"); |
| pd->args->setDim(1); |
| (*pd->args)[0] = new ErrorExp(); // error recovery |
| goto Ldecl; |
| } |
| |
| StringExp *se = semanticString(sc, (*pd->args)[0], "mangled name"); |
| if (!se) |
| goto Ldecl; |
| (*pd->args)[0] = se; // Will be used for later |
| |
| if (!se->len) |
| { |
| pd->error("zero-length string not allowed for mangled name"); |
| goto Ldecl; |
| } |
| if (se->sz != 1) |
| { |
| pd->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 |
| { |
| pd->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)) |
| { |
| pd->error("%s", msg); |
| break; |
| } |
| |
| if (!isUniAlpha(c)) |
| { |
| pd->error("char 0x%04x not allowed in mangled name", c); |
| break; |
| } |
| } |
| } |
| else if (pd->ident == Id::printf || pd->ident == Id::scanf) |
| { |
| if (pd->args && pd->args->length != 0) |
| pd->error("takes no argument"); |
| goto Ldecl; |
| } |
| else if (global.params.ignoreUnsupportedPragmas) |
| { |
| if (global.params.verbose) |
| { |
| /* Print unrecognized pragmas |
| */ |
| OutBuffer buf; |
| buf.writestring(pd->ident->toChars()); |
| if (pd->args) |
| { |
| for (size_t i = 0; i < pd->args->length; i++) |
| { |
| Expression *e = (*pd->args)[i]; |
| |
| sc = sc->startCTFE(); |
| e = expressionSemantic(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 (pd->args->length) |
| buf.writeByte(')'); |
| } |
| message("pragma %s", buf.peekChars()); |
| } |
| goto Lnodecl; |
| } |
| else |
| error(pd->loc, "unrecognized pragma(%s)", pd->ident->toChars()); |
| |
| Ldecl: |
| if (pd->decl) |
| { |
| Scope *sc2 = pd->newScope(sc); |
| |
| for (size_t i = 0; i < pd->decl->length; i++) |
| { |
| Dsymbol *s = (*pd->decl)[i]; |
| |
| dsymbolSemantic(s, sc2); |
| |
| if (pd->ident == Id::mangle) |
| { |
| assert(pd->args && pd->args->length == 1); |
| if (StringExp *se = (*pd->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) |
| pd->error("can only apply to a single declaration"); |
| } |
| } |
| } |
| |
| if (sc2 != sc) |
| sc2->pop(); |
| } |
| return; |
| |
| Lnodecl: |
| if (pd->decl) |
| { |
| pd->error("pragma is missing closing `;`"); |
| goto Ldecl; // do them anyway, to avoid segfaults. |
| } |
| } |
| |
| void visit(StaticIfDeclaration *sid) |
| { |
| attribSemantic(sid); |
| } |
| |
| void visit(StaticForeachDeclaration *sfd) |
| { |
| attribSemantic(sfd); |
| } |
| |
| Dsymbols *compileIt(CompileDeclaration *cd) |
| { |
| //printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd->loc.linnum, cd->exp->toChars()); |
| OutBuffer buf; |
| if (expressionsToString(buf, sc, cd->exps)) |
| return NULL; |
| |
| unsigned errors = global.errors; |
| const size_t len = buf.length(); |
| const char *str = buf.extractChars(); |
| Parser p(cd->loc, sc->_module, (const utf8_t *)str, len, false); |
| p.nextToken(); |
| |
| Dsymbols *d = p.parseDeclDefs(0); |
| if (global.errors != errors) |
| return NULL; |
| |
| if (p.token.value != TOKeof) |
| { |
| cd->error("incomplete mixin declaration (%s)", str); |
| return NULL; |
| } |
| return d; |
| } |
| |
| void visit(CompileDeclaration *cd) |
| { |
| //printf("CompileDeclaration::semantic()\n"); |
| if (!cd->compiled) |
| { |
| cd->decl = compileIt(cd); |
| cd->AttribDeclaration::addMember(sc, cd->scopesym); |
| cd->compiled = true; |
| |
| if (cd->_scope && cd->decl) |
| { |
| for (size_t i = 0; i < cd->decl->length; i++) |
| { |
| Dsymbol *s = (*cd->decl)[i]; |
| s->setScope(cd->_scope); |
| } |
| } |
| } |
| attribSemantic(cd); |
| } |
| |
| void visit(UserAttributeDeclaration *uad) |
| { |
| //printf("UserAttributeDeclaration::semantic() %p\n", this); |
| if (uad->decl && !uad->_scope) |
| uad->Dsymbol::setScope(sc); // for function local symbols |
| |
| attribSemantic(uad); |
| } |
| |
| void visit(StaticAssert *sa) |
| { |
| if (sa->semanticRun < PASSsemanticdone) |
| sa->semanticRun = PASSsemanticdone; |
| } |
| |
| void visit(DebugSymbol *ds) |
| { |
| //printf("DebugSymbol::semantic() %s\n", ds->toChars()); |
| if (ds->semanticRun < PASSsemanticdone) |
| ds->semanticRun = PASSsemanticdone; |
| } |
| |
| void visit(VersionSymbol *vs) |
| { |
| if (vs->semanticRun < PASSsemanticdone) |
| vs->semanticRun = PASSsemanticdone; |
| } |
| |
| void visit(Package *pkg) |
| { |
| if (pkg->semanticRun < PASSsemanticdone) |
| pkg->semanticRun = PASSsemanticdone; |
| } |
| |
| void visit(Module *m) |
| { |
| if (m->semanticRun != PASSinit) |
| return; |
| |
| //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, m->toChars(), parent); |
| m->semanticRun = PASSsemantic; |
| |
| // Note that modules get their own scope, from scratch. |
| // This is so regardless of where in the syntax a module |
| // gets imported, it is unaffected by context. |
| Scope *sc = m->_scope; // see if already got one from importAll() |
| if (!sc) |
| { |
| sc = Scope::createGlobal(m); // create root scope |
| } |
| |
| //printf("Module = %p, linkage = %d\n", sc->scopesym, sc->linkage); |
| |
| // Pass 1 semantic routines: do public side of the definition |
| for (size_t i = 0; i < m->members->length; i++) |
| { |
| Dsymbol *s = (*m->members)[i]; |
| |
| //printf("\tModule('%s'): '%s'.semantic()\n", m->toChars(), s->toChars()); |
| dsymbolSemantic(s, sc); |
| m->runDeferredSemantic(); |
| } |
| |
| if (m->userAttribDecl) |
| { |
| dsymbolSemantic(m->userAttribDecl, sc); |
| } |
| |
| if (!m->_scope) |
| { |
| sc = sc->pop(); |
| sc->pop(); // 2 pops because Scope::createGlobal() created 2 |
| } |
| m->semanticRun = PASSsemanticdone; |
| //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", m, m->toChars(), parent); |
| } |
| |
| void visit(EnumDeclaration *ed) |
| { |
| //printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc->scopesym, sc->scopesym->toChars(), ed->toChars()); |
| //printf("EnumDeclaration::semantic() %p %s\n", ed, ed->toChars()); |
| if (ed->semanticRun >= PASSsemanticdone) |
| return; // semantic() already completed |
| if (ed->semanticRun == PASSsemantic) |
| { |
| assert(ed->memtype); |
| error(ed->loc, "circular reference to enum base type %s", ed->memtype->toChars()); |
| ed->errors = true; |
| ed->semanticRun = PASSsemanticdone; |
| return; |
| } |
| unsigned dprogress_save = Module::dprogress; |
| |
| Scope *scx = NULL; |
| if (ed->_scope) |
| { |
| sc = ed->_scope; |
| scx = ed->_scope; // save so we don't make redundant copies |
| ed->_scope = NULL; |
| } |
| |
| if (!sc) |
| return; |
| |
| ed->parent = sc->parent; |
| ed->type = typeSemantic(ed->type, ed->loc, sc); |
| |
| ed->protection = sc->protection; |
| if (sc->stc & STCdeprecated) |
| ed->isdeprecated = true; |
| ed->userAttribDecl = sc->userAttribDecl; |
| |
| ed->semanticRun = PASSsemantic; |
| |
| if (!ed->members && !ed->memtype) // enum ident; |
| { |
| ed->semanticRun = PASSsemanticdone; |
| return; |
| } |
| |
| if (!ed->symtab) |
| ed->symtab = new DsymbolTable(); |
| |
| /* The separate, and distinct, cases are: |
| * 1. enum { ... } |
| * 2. enum : memtype { ... } |
| * 3. enum ident { ... } |
| * 4. enum ident : memtype { ... } |
| * 5. enum ident : memtype; |
| * 6. enum ident; |
| */ |
| |
| if (ed->memtype) |
| { |
| ed->memtype = typeSemantic(ed->memtype, ed->loc, sc); |
| |
| /* Check to see if memtype is forward referenced |
| */ |
| if (ed->memtype->ty == Tenum) |
| { |
| EnumDeclaration *sym = (EnumDeclaration *)ed->memtype->toDsymbol(sc); |
| if (!sym->memtype || !sym->members || !sym->symtab || sym->_scope) |
| { |
| // memtype is forward referenced, so try again later |
| ed->_scope = scx ? scx : sc->copy(); |
| ed->_scope->setNoFree(); |
| Module::addDeferredSemantic(ed); |
| Module::dprogress = dprogress_save; |
| //printf("\tdeferring %s\n", ed->toChars()); |
| ed->semanticRun = PASSinit; |
| return; |
| } |
| else |
| // Ensure that semantic is run to detect. e.g. invalid forward references |
| dsymbolSemantic(sym, sc); |
| } |
| if (ed->memtype->ty == Tvoid) |
| { |
| ed->error("base type must not be void"); |
| ed->memtype = Type::terror; |
| } |
| if (ed->memtype->ty == Terror) |
| { |
| ed->errors = true; |
| if (ed->members) |
| { |
| for (size_t i = 0; i < ed->members->length; i++) |
| { |
| Dsymbol *s = (*ed->members)[i]; |
| s->errors = true; // poison all the members |
| } |
| } |
| ed->semanticRun = PASSsemanticdone; |
| return; |
| } |
| } |
| |
| ed->semanticRun = PASSsemanticdone; |
| |
| if (!ed->members) // enum ident : memtype; |
| return; |
| |
| if (ed->members->length == 0) |
| { |
| ed->error("enum %s must have at least one member", ed->toChars()); |
| ed->errors = true; |
| return; |
| } |
| |
| Module::dprogress++; |
| |
| Scope *sce; |
| if (ed->isAnonymous()) |
| sce = sc; |
| else |
| { |
| sce = sc->push(ed); |
| sce->parent = ed; |
| } |
| sce = sce->startCTFE(); |
| sce->setNoFree(); // needed for getMaxMinValue() |
| |
| /* Each enum member gets the sce scope |
| */ |
| for (size_t i = 0; i < ed->members->length; i++) |
| { |
| EnumMember *em = (*ed->members)[i]->isEnumMember(); |
| if (em) |
| em->_scope = sce; |
| } |
| |
| if (!ed->added) |
| { |
| /* addMember() is not called when the EnumDeclaration appears as a function statement, |
| * so we have to do what addMember() does and install the enum members in the right symbol |
| * table |
| */ |
| ScopeDsymbol *scopesym = NULL; |
| if (ed->isAnonymous()) |
| { |
| /* Anonymous enum members get added to enclosing scope. |
| */ |
| for (Scope *sct = sce; 1; sct = sct->enclosing) |
| { |
| assert(sct); |
| if (sct->scopesym) |
| { |
| scopesym = sct->scopesym; |
| if (!sct->scopesym->symtab) |
| sct->scopesym->symtab = new DsymbolTable(); |
| break; |
| } |
| } |
| } |
| else |
| { |
| // Otherwise enum members are in the EnumDeclaration's symbol table |
| scopesym = ed; |
| } |
| |
| for (size_t i = 0; i < ed->members->length; i++) |
| { |
| EnumMember *em = (*ed->members)[i]->isEnumMember(); |
| if (em) |
| { |
| em->ed = ed; |
| em->addMember(sc, scopesym); |
| } |
| } |
| } |
| |
| for (size_t i = 0; i < ed->members->length; i++) |
| { |
| EnumMember *em = (*ed->members)[i]->isEnumMember(); |
| if (em) |
| dsymbolSemantic(em, em->_scope); |
| } |
| //printf("defaultval = %lld\n", defaultval); |
| |
| //if (defaultval) printf("defaultval: %s %s\n", defaultval->toChars(), defaultval->type->toChars()); |
| //printf("members = %s\n", ed->members->toChars()); |
| } |
| |
| void visit(EnumMember *em) |
| { |
| //printf("EnumMember::semantic() %s\n", em->toChars()); |
| if (em->errors || em->semanticRun >= PASSsemanticdone) |
| return; |
| if (em->semanticRun == PASSsemantic) |
| { |
| em->error("circular reference to enum member"); |
| Lerrors: |
| em->errors = true; |
| em->semanticRun = PASSsemanticdone; |
| return; |
| } |
| assert(em->ed); |
| |
| dsymbolSemantic(em->ed, sc); |
| if (em->ed->errors) |
| goto Lerrors; |
| |
| if (em->errors || em->semanticRun >= PASSsemanticdone) |
| return; |
| |
| if (em->_scope) |
| sc = em->_scope; |
| if (!sc) |
| return; |
| |
| em->semanticRun = PASSsemantic; |
| |
| em->protection = em->ed->isAnonymous() ? em->ed->protection : Prot(Prot::public_); |
| em->linkage = LINKd; |
| em->storage_class |= STCmanifest; |
| |
| // https://issues.dlang.org/show_bug.cgi?id=9701 |
| if (em->ed->isAnonymous()) |
| { |
| if (em->userAttribDecl) |
| em->userAttribDecl->userAttribDecl = em->ed->userAttribDecl; |
| else |
| em->userAttribDecl = em->ed->userAttribDecl; |
| } |
| |
| // The first enum member is special |
| bool first = (em == (*em->ed->members)[0]); |
| |
| if (em->origType) |
| { |
| em->origType = typeSemantic(em->origType, em->loc, sc); |
| em->type = em->origType; |
| assert(em->value()); // "type id;" is not a valid enum member declaration |
| } |
| |
| if (em->value()) |
| { |
| Expression *e = em->value(); |
| assert(e->dyncast() == DYNCAST_EXPRESSION); |
| e = expressionSemantic(e, sc); |
| e = resolveProperties(sc, e); |
| e = e->ctfeInterpret(); |
| if (e->op == TOKerror) |
| goto Lerrors; |
| if (first && !em->ed->memtype && !em->ed->isAnonymous()) |
| { |
| em->ed->memtype = e->type; |
| if (em->ed->memtype->ty == Terror) |
| { |
| em->ed->errors = true; |
| goto Lerrors; |
| } |
| if (em->ed->memtype->ty != Terror) |
| { |
| /* Bugzilla 11746: All of named enum members should have same type |
| * with the first member. If the following members were referenced |
| * during the first member semantic, their types should be unified. |
| */ |
| for (size_t i = 0; i < em->ed->members->length; i++) |
| { |
| EnumMember *enm = (*em->ed->members)[i]->isEnumMember(); |
| if (!enm || enm == em || enm->semanticRun < PASSsemanticdone || enm->origType) |
| continue; |
| |
| //printf("[%d] enm = %s, enm->semanticRun = %d\n", i, enm->toChars(), enm->semanticRun); |
| Expression *ev = enm->value(); |
| ev = ev->implicitCastTo(sc, em->ed->memtype); |
| ev = ev->ctfeInterpret(); |
| ev = ev->castTo(sc, em->ed->type); |
| if (ev->op == TOKerror) |
| em->ed->errors = true; |
| enm->value() = ev; |
| } |
| if (em->ed->errors) |
| { |
| em->ed->memtype = Type::terror; |
| goto Lerrors; |
| } |
| } |
| } |
| |
| if (em->ed->memtype && !em->origType) |
| { |
| e = e->implicitCastTo(sc, em->ed->memtype); |
| e = e->ctfeInterpret(); |
| |
| // save origValue for better json output |
| em->origValue = e; |
| |
| if (!em->ed->isAnonymous()) |
| { |
| e = e->castTo(sc, em->ed->type); |
| e = e->ctfeInterpret(); |
| } |
| } |
| else if (em->origType) |
| { |
| e = e->implicitCastTo(sc, em->origType); |
| e = e->ctfeInterpret(); |
| assert(em->ed->isAnonymous()); |
| |
| // save origValue for better json output |
| em->origValue = e; |
| } |
| em->value() = e; |
| } |
| else if (first) |
| { |
| Type *t; |
| if (em->ed->memtype) |
| t = em->ed->memtype; |
| else |
| { |
| t = Type::tint32; |
| if (!em->ed->isAnonymous()) |
| em->ed->memtype = t; |
| } |
| Expression *e = new IntegerExp(em->loc, 0, Type::tint32); |
| e = e->implicitCastTo(sc, t); |
| e = e->ctfeInterpret(); |
| |
| // save origValue for better json output |
| em->origValue = e; |
| |
| if (!em->ed->isAnonymous()) |
| { |
| e = e->castTo(sc, em->ed->type); |
| e = e->ctfeInterpret(); |
| } |
| em->value() = e; |
| } |
| else |
| { |
| /* Find the previous enum member, |
| * and set this to be the previous value + 1 |
| */ |
| EnumMember *emprev = NULL; |
| for (size_t i = 0; i < em->ed->members->length; i++) |
| { |
| EnumMember *enm = (*em->ed->members)[i]->isEnumMember(); |
| if (enm) |
| { |
| if (enm == em) |
| break; |
| emprev = enm; |
| } |
| } |
| assert(emprev); |
| if (emprev->semanticRun < PASSsemanticdone) // if forward reference |
| dsymbolSemantic(emprev, emprev->_scope); // resolve it |
| if (emprev->errors) |
| goto Lerrors; |
| |
| Expression *eprev = emprev->value(); |
| Type *tprev = eprev->type->equals(em->ed->type) ? em->ed->memtype : eprev->type; |
| |
| Expression *emax = tprev->getProperty(em->ed->loc, Id::max, 0); |
| emax = expressionSemantic(emax, sc); |
| emax = emax->ctfeInterpret(); |
| |
| // Set value to (eprev + 1). |
| // But first check that (eprev != emax) |
| assert(eprev); |
| Expression *e = new EqualExp(TOKequal, em->loc, eprev, emax); |
| e = expressionSemantic(e, sc); |
| e = e->ctfeInterpret(); |
| if (e->toInteger()) |
| { |
| em->error("initialization with (%s.%s + 1) causes overflow for type `%s`", emprev->ed->toChars(), emprev->toChars(), em->ed->type->toBasetype()->toChars()); |
| goto Lerrors; |
| } |
| |
| // Now set e to (eprev + 1) |
| e = new AddExp(em->loc, eprev, new IntegerExp(em->loc, 1, Type::tint32)); |
| e = expressionSemantic(e, sc); |
| e = e->castTo(sc, eprev->type); |
| e = e->ctfeInterpret(); |
| |
| // save origValue (without cast) for better json output |
| if (e->op != TOKerror) // avoid duplicate diagnostics |
| { |
| assert(emprev->origValue); |
| em->origValue = new AddExp(em->loc, emprev->origValue, new IntegerExp(em->loc, 1, Type::tint32)); |
| em->origValue = expressionSemantic(em->origValue, sc); |
| em->origValue = em->origValue->ctfeInterpret(); |
| } |
| |
| if (e->op == TOKerror) |
| goto Lerrors; |
| if (e->type->isfloating()) |
| { |
| // Check that e != eprev (not always true for floats) |
| Expression *etest = new EqualExp(TOKequal, em->loc, e, eprev); |
| etest = expressionSemantic(etest, sc); |
| etest = etest->ctfeInterpret(); |
| if (etest->toInteger()) |
| { |
| em->error("has inexact value, due to loss of precision"); |
| goto Lerrors; |
| } |
| } |
| em->value() = e; |
| } |
| if (!em->origType) |
| em->type = em->value()->type; |
| |
| assert(em->origValue); |
| em->semanticRun = PASSsemanticdone; |
| } |
| |
| void visit(TemplateDeclaration *tempdecl) |
| { |
| if (tempdecl->semanticRun != PASSinit) |
| return; // semantic() already run |
| |
| // Remember templates defined in module object that we need to know about |
| if (sc->_module && sc->_module->ident == Id::object) |
| { |
| if (tempdecl->ident == Id::RTInfo) |
| Type::rtinfo = tempdecl; |
| } |
| |
| /* Remember Scope for later instantiations, but make |
| * a copy since attributes can change. |
| */ |
| if (!tempdecl->_scope) |
| { |
| tempdecl->_scope = sc->copy(); |
| tempdecl->_scope->setNoFree(); |
| } |
| |
| tempdecl->semanticRun = PASSsemantic; |
| |
| tempdecl->parent = sc->parent; |
| tempdecl->protection = sc->protection; |
| tempdecl->isstatic = tempdecl->toParent()->isModule() || (tempdecl->_scope->stc & STCstatic); |
| |
| if (!tempdecl->isstatic) |
| { |
| if (AggregateDeclaration *ad = tempdecl->parent->pastMixin()->isAggregateDeclaration()) |
| ad->makeNested(); |
| } |
| |
| // Set up scope for parameters |
| ScopeDsymbol *paramsym = new ScopeDsymbol(); |
| paramsym->parent = tempdecl->parent; |
| Scope *paramscope = sc->push(paramsym); |
| paramscope->stc = 0; |
| |
| if (global.params.doDocComments) |
| { |
| tempdecl->origParameters = new TemplateParameters(); |
| tempdecl->origParameters->setDim(tempdecl->parameters->length); |
| for (size_t i = 0; i < tempdecl->parameters->length; i++) |
| { |
| TemplateParameter *tp = (*tempdecl->parameters)[i]; |
| (*tempdecl->origParameters)[i] = tp->syntaxCopy(); |
| } |
| } |
| |
| for (size_t i = 0; i < tempdecl->parameters->length; i++) |
| { |
| TemplateParameter *tp = (*tempdecl->parameters)[i]; |
| |
| if (!tp->declareParameter(paramscope)) |
| { |
| error(tp->loc, "parameter `%s` multiply defined", tp->ident->toChars()); |
| tempdecl->errors = true; |
| } |
| if (!tpsemantic(tp, paramscope, tempdecl->parameters)) |
| { |
| tempdecl->errors = true; |
| } |
| if (i + 1 != tempdecl->parameters->length && tp->isTemplateTupleParameter()) |
| { |
| tempdecl->error("template tuple parameter must be last one"); |
| tempdecl->errors = true; |
| } |
| } |
| |
| /* Calculate TemplateParameter::dependent |
| */ |
| TemplateParameters tparams; |
| tparams.setDim(1); |
| for (size_t i = 0; i < tempdecl->parameters->length; i++) |
| { |
| TemplateParameter *tp = (*tempdecl->parameters)[i]; |
| tparams[0] = tp; |
| |
| for (size_t j = 0; j < tempdecl->parameters->length; j++) |
| { |
| // Skip cases like: X(T : T) |
| if (i == j) |
| continue; |
| |
| if (TemplateTypeParameter *ttp = (*tempdecl->parameters)[j]->isTemplateTypeParameter()) |
| { |
| if (reliesOnTident(ttp->specType, &tparams)) |
| tp->dependent = true; |
| } |
| else if (TemplateAliasParameter *tap = (*tempdecl->parameters)[j]->isTemplateAliasParameter()) |
| { |
| if (reliesOnTident(tap->specType, &tparams) || |
| reliesOnTident(isType(tap->specAlias), &tparams)) |
| { |
| tp->dependent = true; |
| } |
| } |
| } |
| } |
| |
| paramscope->pop(); |
| |
| // Compute again |
| tempdecl->onemember = NULL; |
| if (tempdecl->members) |
| { |
| Dsymbol *s; |
| if (Dsymbol::oneMembers(tempdecl->members, &s, tempdecl->ident) && s) |
| { |
| tempdecl->onemember = s; |
| s->parent = tempdecl; |
| } |
| } |
| |
| /* BUG: should check: |
| * o no virtual functions or non-static data members of classes |
| */ |
| tempdecl->semanticRun = PASSsemanticdone; |
| } |
| |
| void visit(TemplateInstance *ti) |
| { |
| templateInstanceSemantic(ti, sc, NULL); |
| } |
| |
| void visit(TemplateMixin *tm) |
| { |
| if (tm->semanticRun != PASSinit) |
| { |
| // When a class/struct contains mixin members, and is done over |
| // because of forward references, never reach here so semanticRun |
| // has been reset to PASSinit. |
| return; |
| } |
| tm->semanticRun = PASSsemantic; |
| |
| Scope *scx = NULL; |
| if (tm->_scope) |
| { |
| sc = tm->_scope; |
| scx = tm->_scope; // save so we don't make redundant copies |
| tm->_scope = NULL; |
| } |
| |
| /* Run semantic on each argument, place results in tiargs[], |
| * then find best match template with tiargs |
| */ |
| if (!tm->findTempDecl(sc) || |
| !tm->semanticTiargs(sc) || |
| !tm->findBestMatch(sc, NULL)) |
| { |
| if (tm->semanticRun == PASSinit) // forward reference had occured |
| { |
| //printf("forward reference - deferring\n"); |
| tm->_scope = scx ? scx : sc->copy(); |
| tm->_scope->setNoFree(); |
| Module::addDeferredSemantic(tm); |
| return; |
| } |
| |
| tm->inst = tm; |
| tm->errors = true; |
| return; // error recovery |
| } |
| TemplateDeclaration *tempdecl = tm->tempdecl->isTemplateDeclaration(); |
| assert(tempdecl); |
| |
| if (!tm->ident) |
| { |
| /* Assign scope local unique identifier, as same as lambdas. |
| */ |
| const char *s = "__mixin"; |
| |
| if (FuncDeclaration *func = sc->parent->isFuncDeclaration()) |
| { |
| tm->symtab = func->localsymtab; |
| if (tm->symtab) |
| { |
| // Inside template constraint, symtab is not set yet. |
| goto L1; |
| } |
| } |
| else |
| { |
| tm->symtab = sc->parent->isScopeDsymbol()->symtab; |
| L1: |
| assert(tm->symtab); |
| int num = (int)dmd_aaLen(tm->symtab->tab) + 1; |
| tm->ident = Identifier::generateId(s, num); |
| tm->symtab->insert(tm); |
| } |
| } |
| |
| tm->inst = tm; |
| tm->parent = sc->parent; |
| |
| /* Detect recursive mixin instantiations. |
| */ |
| for (Dsymbol *s = tm->parent; s; s = s->parent) |
| { |
| //printf("\ts = '%s'\n", s->toChars()); |
| TemplateMixin *tmix = s->isTemplateMixin(); |
| if (!tmix || tempdecl != tmix->tempdecl) |
| continue; |
| |
| /* Different argument list lengths happen with variadic args |
| */ |
| if (tm->tiargs->length != tmix->tiargs->length) |
| continue; |
| |
| for (size_t i = 0; i < tm->tiargs->length; i++) |
| { |
| RootObject *o = (*tm->tiargs)[i]; |
| Type *ta = isType(o); |
| Expression *ea = isExpression(o); |
| Dsymbol *sa = isDsymbol(o); |
| RootObject *tmo = (*tmix->tiargs)[i]; |
| if (ta) |
| { |
| Type *tmta = isType(tmo); |
| if (!tmta) |
| goto Lcontinue; |
| if (!ta->equals(tmta)) |
| goto Lcontinue; |
| } |
| else if (ea) |
| { |
| Expression *tme = isExpression(tmo); |
| if (!tme || !ea->equals(tme)) |
| goto Lcontinue; |
| } |
| else if (sa) |
| { |
| Dsymbol *tmsa = isDsymbol(tmo); |
| if (sa != tmsa) |
| goto Lcontinue; |
| } |
| else |
| assert(0); |
| } |
| tm->error("recursive mixin instantiation"); |
| return; |
| |
| Lcontinue: |
| continue; |
| } |
| |
| // Copy the syntax trees from the TemplateDeclaration |
| tm->members = Dsymbol::arraySyntaxCopy(tempdecl->members); |
| if (!tm->members) |
| return; |
| |
| tm->symtab = new DsymbolTable(); |
| |
| for (Scope *sce = sc; 1; sce = sce->enclosing) |
| { |
| ScopeDsymbol *sds = (ScopeDsymbol *)sce->scopesym; |
| if (sds) |
| { |
| sds->importScope(tm, Prot(Prot::public_)); |
| break; |
| } |
| } |
| |
| Scope *scy = sc->push(tm); |
| scy->parent = tm; |
| |
| tm->argsym = new ScopeDsymbol(); |
| tm->argsym->parent = scy->parent; |
| Scope *argscope = scy->push(tm->argsym); |
| |
| unsigned errorsave = global.errors; |
| |
| // Declare each template parameter as an alias for the argument type |
| tm->declareParameters(argscope); |
| |
| // Add members to enclosing scope, as well as this scope |
| for (size_t i = 0; i < tm->members->length; i++) |
| { |
| Dsymbol *s = (*tm->members)[i]; |
| s->addMember(argscope, tm); |
| //printf("sc->parent = %p, sc->scopesym = %p\n", sc->parent, sc->scopesym); |
| //printf("s->parent = %s\n", s->parent->toChars()); |
| } |
| |
| // Do semantic() analysis on template instance members |
| Scope *sc2 = argscope->push(tm); |
| //size_t deferred_dim = Module::deferred.length; |
| |
| static int nest; |
| //printf("%d\n", nest); |
| if (++nest > global.recursionLimit) |
| { |
| global.gag = 0; // ensure error message gets printed |
| tm->error("recursive expansion"); |
| fatal(); |
| } |
| |
| for (size_t i = 0; i < tm->members->length; i++) |
| { |
| Dsymbol *s = (*tm->members)[i]; |
| s->setScope(sc2); |
| } |
| |
| for (size_t i = 0; i < tm->members->length; i++) |
| { |
| Dsymbol *s = (*tm->members)[i]; |
| s->importAll(sc2); |
| } |
| |
| for (size_t i = 0; i < tm->members->length; i++) |
| { |
| Dsymbol *s = (*tm->members)[i]; |
| dsymbolSemantic(s, sc2); |
| } |
| |
| nest--; |
| |
| /* In DeclDefs scope, TemplateMixin does not have to handle deferred symbols. |
| * Because the members would already call Module::addDeferredSemantic() for themselves. |
| * See Struct, Class, Interface, and EnumDeclaration::semantic(). |
| */ |
| //if (!sc->func && Module::deferred.length > deferred_dim) {} |
| |
| AggregateDeclaration *ad = tm->toParent()->isAggregateDeclaration(); |
| if (sc->func && !ad) |
| { |
| semantic2(tm, sc2); |
| semantic3(tm, sc2); |
| } |
| |
| // Give additional context info if error occurred during instantiation |
| if (global.errors != errorsave) |
| { |
| tm->error("error instantiating"); |
| tm->errors = true; |
| } |
| |
| sc2->pop(); |
| argscope->pop(); |
| scy->pop(); |
| } |
| |
| void visit(Nspace *ns) |
| { |
| if (ns->semanticRun != PASSinit) |
| return; |
| if (ns->_scope) |
| { |
| sc = ns->_scope; |
| ns->_scope = NULL; |
| } |
| if (!sc) |
| return; |
| |
| ns->semanticRun = PASSsemantic; |
| ns->parent = sc->parent; |
| if (ns->members) |
| { |
| assert(sc); |
| sc = sc->push(ns); |
| sc->linkage = LINKcpp; // note that namespaces imply C++ linkage |
| sc->parent = ns; |
| |
| for (size_t i = 0; i < ns->members->length; i++) |
| { |
| Dsymbol *s = (*ns->members)[i]; |
| s->importAll(sc); |
| } |
| |
| for (size_t i = 0; i < ns->members->length; i++) |
| { |
| Dsymbol *s = (*ns->members)[i]; |
| dsymbolSemantic(s, sc); |
| } |
| sc->pop(); |
| } |
| ns->semanticRun = PASSsemanticdone; |
| } |
| |
| |
| private: |
| static bool isPointerToChar(Parameter *p) |
| { |
| if (TypePointer *tptr = p->type->isTypePointer()) |
| { |
| return tptr->next->ty == Tchar; |
| } |
| return false; |
| } |
| |
| static bool isVa_list(Parameter *p, FuncDeclaration *funcdecl, Scope *sc) |
| { |
| return p->type->equals(target.va_listType(funcdecl->loc, sc)); |
| } |
| |
| public: |
| void funcDeclarationSemantic(FuncDeclaration *funcdecl) |
| { |
| TypeFunction *f; |
| AggregateDeclaration *ad; |
| InterfaceDeclaration *id; |
| |
| if (funcdecl->semanticRun != PASSinit && funcdecl->isFuncLiteralDeclaration()) |
| { |
| /* Member functions that have return types that are |
| * forward references can have semantic() run more than |
| * once on them. |
| * See test\interface2.d, test20 |
| */ |
| return; |
| } |
| |
| if (funcdecl->semanticRun >= PASSsemanticdone) |
| return; |
| assert(funcdecl->semanticRun <= PASSsemantic); |
| funcdecl->semanticRun = PASSsemantic; |
| |
| if (funcdecl->_scope) |
| { |
| sc = funcdecl->_scope; |
| funcdecl->_scope = NULL; |
| } |
| |
| if (!sc || funcdecl->errors) |
| return; |
| |
| funcdecl->parent = sc->parent; |
| Dsymbol *parent = funcdecl->toParent(); |
| |
| funcdecl->foverrides.setDim(0); // reset in case semantic() is being retried for this function |
| |
| funcdecl->storage_class |= sc->stc & ~STCref; |
| ad = funcdecl->isThis(); |
| // Don't nest structs b/c of generated methods which should not access the outer scopes. |
| // https://issues.dlang.org/show_bug.cgi?id=16627 |
| if (ad && !funcdecl->generated) |
| { |
| funcdecl->storage_class |= ad->storage_class & (STC_TYPECTOR | STCsynchronized); |
| ad->makeNested(); |
| } |
| if (sc->func) |
| funcdecl->storage_class |= sc->func->storage_class & STCdisable; |
| // Remove prefix storage classes silently. |
| if ((funcdecl->storage_class & STC_TYPECTOR) && !(ad || funcdecl->isNested())) |
| funcdecl->storage_class &= ~STC_TYPECTOR; |
| |
| //printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", funcdecl->storage_class, sc->stc, Declaration::isFinal()); |
| |
| FuncLiteralDeclaration *fld = funcdecl->isFuncLiteralDeclaration(); |
| if (fld && fld->treq) |
| { |
| Type *treq = fld->treq; |
| assert(treq->nextOf()->ty == Tfunction); |
| if (treq->ty == Tdelegate) |
| fld->tok = TOKdelegate; |
| else if (treq->ty == Tpointer && treq->nextOf()->ty == Tfunction) |
| fld->tok = TOKfunction; |
| else |
| assert(0); |
| funcdecl->linkage = treq->nextOf()->toTypeFunction()->linkage; |
| } |
| else |
| funcdecl->linkage = sc->linkage; |
| funcdecl->inlining = sc->inlining; |
| funcdecl->protection = sc->protection; |
| funcdecl->userAttribDecl = sc->userAttribDecl; |
| |
| if (!funcdecl->originalType) |
| funcdecl->originalType = funcdecl->type->syntaxCopy(); |
| if (funcdecl->type->ty != Tfunction) |
| { |
| if (funcdecl->type->ty != Terror) |
| { |
| funcdecl->error("%s must be a function instead of %s", funcdecl->toChars(), funcdecl->type->toChars()); |
| funcdecl->type = Type::terror; |
| } |
| funcdecl->errors = true; |
| return; |
| } |
| if (!funcdecl->type->deco) |
| { |
| sc = sc->push(); |
| sc->stc |= funcdecl->storage_class & (STCdisable | STCdeprecated); // forward to function type |
| TypeFunction *tf = funcdecl->type->toTypeFunction(); |
| |
| if (sc->func) |
| { |
| /* If the nesting parent is pure without inference, |
| * then this function defaults to pure too. |
| * |
| * auto foo() pure { |
| * auto bar() {} // become a weak purity funciton |
| * class C { // nested class |
| * auto baz() {} // become a weak purity funciton |
| * } |
| * |
| * static auto boo() {} // typed as impure |
| * // Even though, boo cannot call any impure functions. |
| * // See also Expression::checkPurity(). |
| * } |
| */ |
| if (tf->purity == PUREimpure && (funcdecl->isNested() || funcdecl->isThis())) |
| { |
| FuncDeclaration *fd = NULL; |
| for (Dsymbol *p = funcdecl->toParent2(); p; p = p->toParent2()) |
| { |
| if (AggregateDeclaration *adx = p->isAggregateDeclaration()) |
| { |
| if (adx->isNested()) |
| continue; |
| break; |
| } |
| if ((fd = p->isFuncDeclaration()) != NULL) |
| break; |
| } |
| |
| /* If the parent's purity is inferred, then this function's purity needs |
| * to be inferred first. |
| */ |
| if (fd && fd->isPureBypassingInference() >= PUREweak && |
| !funcdecl->isInstantiated()) |
| { |
| tf->purity = PUREfwdref; // default to pure |
| } |
| } |
| } |
| |
| if (tf->isref) sc->stc |= STCref; |
| if (tf->isscope) sc->stc |= STCscope; |
| if (tf->isnothrow) sc->stc |= STCnothrow; |
| if (tf->isnogc) sc->stc |= STCnogc; |
| if (tf->isproperty) sc->stc |= STCproperty; |
| if (tf->purity == PUREfwdref) sc->stc |= STCpure; |
| if (tf->trust != TRUSTdefault) |
| sc->stc &= ~(STCsafe | STCsystem | STCtrusted); |
| if (tf->trust == TRUSTsafe) sc->stc |= STCsafe; |
| if (tf->trust == TRUSTsystem) sc->stc |= STCsystem; |
| if (tf->trust == TRUSTtrusted) sc->stc |= STCtrusted; |
| |
| if (funcdecl->isCtorDeclaration()) |
| { |
| sc->flags |= SCOPEctor; |
| |
| Type *tret = ad->handleType(); |
| assert(tret); |
| tret = tret->addStorageClass(funcdecl->storage_class | sc->stc); |
| tret = tret->addMod(funcdecl->type->mod); |
| tf->next = tret; |
| |
| if (ad->isStructDeclaration()) |
| sc->stc |= STCref; |
| } |
| |
| // 'return' on a non-static class member function implies 'scope' as well |
| if (ad && ad->isClassDeclaration() && (tf->isreturn || sc->stc & STCreturn) && !(sc->stc & STCstatic)) |
| sc->stc |= STCscope; |
| |
| // If 'this' has no pointers, remove 'scope' as it has no meaning |
| if (sc->stc & STCscope && ad && ad->isStructDeclaration() && !ad->type->hasPointers()) |
| { |
| sc->stc &= ~STCscope; |
| tf->isscope = false; |
| } |
| |
| sc->linkage = funcdecl->linkage; |
| |
| if (!tf->isNaked() && !(funcdecl->isThis() || funcdecl->isNested())) |
| { |
| OutBuffer buf; |
| MODtoBuffer(&buf, tf->mod); |
| funcdecl->error("without `this` cannot be %s", buf.peekChars()); |
| tf->mod = 0; // remove qualifiers |
| } |
| |
| /* Apply const, immutable, wild and shared storage class |
| * to the function type. Do this before type semantic. |
| */ |
| StorageClass stc = funcdecl->storage_class; |
| if (funcdecl->type->isImmutable()) |
| stc |= STCimmutable; |
| if (funcdecl->type->isConst()) |
| stc |= STCconst; |
| if (funcdecl->type->isShared() || funcdecl->storage_class & STCsynchronized) |
| stc |= STCshared; |
| if (funcdecl->type->isWild()) |
| stc |= STCwild; |
| switch (stc & STC_TYPECTOR) |
| { |
| case STCimmutable: |
| case STCimmutable | STCconst: |
| case STCimmutable | STCwild: |
| case STCimmutable | STCwild | STCconst: |
| case STCimmutable | STCshared: |
| case STCimmutable | STCshared | STCconst: |
| case STCimmutable | STCshared | STCwild: |
| case STCimmutable | STCshared | STCwild | STCconst: |
| // Don't use immutableOf(), as that will do a merge() |
| funcdecl->type = funcdecl->type->makeImmutable(); |
| break; |
| |
| case STCconst: |
| funcdecl->type = funcdecl->type->makeConst(); |
| break; |
| |
| case STCwild: |
| funcdecl->type = funcdecl->type->makeWild(); |
| break; |
| |
| case STCwild | STCconst: |
| funcdecl->type = funcdecl->type->makeWildConst(); |
| break; |
| |
| case STCshared: |
| funcdecl->type = funcdecl->type->makeShared(); |
| break; |
| |
| case STCshared | STCconst: |
| funcdecl->type = funcdecl->type->makeSharedConst(); |
| break; |
| |
| case STCshared | STCwild: |
| funcdecl->type = funcdecl->type->makeSharedWild(); |
| break; |
| |
| case STCshared | STCwild | STCconst: |
| funcdecl->type = funcdecl->type->makeSharedWildConst(); |
| break; |
| |
| case 0: |
| break; |
| |
| default: |
| assert(0); |
| } |
| |
| funcdecl->type = typeSemantic(funcdecl->type, funcdecl->loc, sc); |
| sc = sc->pop(); |
| } |
| if (funcdecl->type->ty != Tfunction) |
| { |
| if (funcdecl->type->ty != Terror) |
| { |
| funcdecl->error("%s must be a function instead of %s", funcdecl->toChars(), funcdecl->type->toChars()); |
| funcdecl->type = Type::terror; |
| } |
| funcdecl->errors = true; |
| return; |
| } |
| else |
| { |
| // Merge back function attributes into 'originalType'. |
| // It's used for mangling, ddoc, and json output. |
| TypeFunction *tfo = funcdecl->originalType->toTypeFunction(); |
| TypeFunction *tfx = funcdecl->type->toTypeFunction(); |
| tfo->mod = tfx->mod; |
| tfo->isscope = tfx->isscope; |
| tfo->isscopeinferred = tfx->isscopeinferred; |
| tfo->isref = tfx->isref; |
| tfo->isnothrow = tfx->isnothrow; |
| tfo->isnogc = tfx->isnogc; |
| tfo->isproperty = tfx->isproperty; |
| tfo->purity = tfx->purity; |
| tfo->trust = tfx->trust; |
| |
| funcdecl->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR); |
| } |
| |
| f = (TypeFunction *)funcdecl->type; |
| |
| if ((funcdecl->storage_class & STCauto) && !f->isref && !funcdecl->inferRetType) |
| funcdecl->error("storage class `auto` has no effect if return type is not inferred"); |
| /* Functions can only be 'scope' if they have a 'this' |
| */ |
| if (f->isscope && !funcdecl->isNested() && !ad) |
| { |
| funcdecl->error("functions cannot be scope"); |
| } |
| |
| if (f->isreturn && !funcdecl->needThis() && !funcdecl->isNested()) |
| { |
| /* Non-static nested functions have a hidden 'this' pointer to which |
| * the 'return' applies |
| */ |
| funcdecl->error("static member has no `this` to which `return` can apply"); |
| } |
| |
| if (funcdecl->isAbstract() && !funcdecl->isVirtual()) |
| { |
| const char *sfunc; |
| if (funcdecl->isStatic()) |
| sfunc = "static"; |
| else if (funcdecl->protection.kind |