| |
| /* 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 == Prot::private_ || funcdecl->protection.kind == Prot::package_) |
| sfunc = protectionToChars(funcdecl->protection.kind); |
| else |
| sfunc = "non-virtual"; |
| funcdecl->error("%s functions cannot be abstract", sfunc); |
| } |
| |
| if (funcdecl->isOverride() && !funcdecl->isVirtual()) |
| { |
| Prot::Kind kind = funcdecl->prot().kind; |
| if ((kind == Prot::private_ || kind == Prot::package_) && funcdecl->isMember()) |
| funcdecl->error("%s method is not virtual and cannot override", protectionToChars(kind)); |
| else |
| funcdecl->error("cannot override a non-virtual function"); |
| } |
| |
| if (funcdecl->isAbstract() && funcdecl->isFinalFunc()) |
| funcdecl->error("cannot be both final and abstract"); |
| |
| if (const unsigned pors = sc->flags & (SCOPEprintf | SCOPEscanf)) |
| { |
| /* printf/scanf-like functions must be of the form: |
| * extern (C/C++) T printf([parameters...], const(char)* format, ...); |
| * or: |
| * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list); |
| */ |
| const size_t nparams = f->parameterList.length(); |
| if ((f->linkage == LINKc || f->linkage == LINKcpp) && |
| |
| ((f->parameterList.varargs == VARARGvariadic && |
| nparams >= 1 && |
| isPointerToChar(f->parameterList[nparams - 1])) || |
| (f->parameterList.varargs == VARARGnone && |
| nparams >= 2 && |
| isPointerToChar(f->parameterList[nparams - 2]) && |
| isVa_list(f->parameterList[nparams - 1], funcdecl, sc)) |
| ) |
| ) |
| { |
| funcdecl->flags |= (pors == SCOPEprintf) ? FUNCFLAGprintf : FUNCFLAGscanf; |
| } |
| else |
| { |
| const char *p = (pors == SCOPEprintf ? Id::printf : Id::scanf)->toChars(); |
| if (f->parameterList.varargs == VARARGvariadic) |
| { |
| funcdecl->error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, ...)`" |
| " not `%s`", |
| p, f->next->toChars(), funcdecl->toChars(), funcdecl->type->toChars()); |
| } |
| else |
| { |
| funcdecl->error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, va_list)`", |
| p, f->next->toChars(), funcdecl->toChars()); |
| } |
| } |
| } |
| |
| id = parent->isInterfaceDeclaration(); |
| if (id) |
| { |
| funcdecl->storage_class |= STCabstract; |
| |
| if (funcdecl->isCtorDeclaration() || |
| funcdecl->isPostBlitDeclaration() || |
| funcdecl->isDtorDeclaration() || |
| funcdecl->isInvariantDeclaration() || |
| funcdecl->isNewDeclaration() || funcdecl->isDelete()) |
| funcdecl->error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface %s", id->toChars()); |
| if (funcdecl->fbody && funcdecl->isVirtual()) |
| funcdecl->error("function body only allowed in final functions in interface %s", id->toChars()); |
| } |
| |
| if (UnionDeclaration *ud = parent->isUnionDeclaration()) |
| { |
| if (funcdecl->isPostBlitDeclaration() || |
| funcdecl->isDtorDeclaration() || |
| funcdecl->isInvariantDeclaration()) |
| funcdecl->error("destructors, postblits and invariants are not allowed in union %s", ud->toChars()); |
| } |
| |
| if (parent->isStructDeclaration()) |
| { |
| if (funcdecl->isCtorDeclaration()) |
| { |
| goto Ldone; |
| } |
| } |
| |
| if (ClassDeclaration *cd = parent->isClassDeclaration()) |
| { |
| if (funcdecl->isCtorDeclaration()) |
| { |
| goto Ldone; |
| } |
| |
| if (funcdecl->storage_class & STCabstract) |
| cd->isabstract = ABSyes; |
| |
| // if static function, do not put in vtbl[] |
| if (!funcdecl->isVirtual()) |
| { |
| //printf("\tnot virtual\n"); |
| goto Ldone; |
| } |
| // Suppress further errors if the return type is an error |
| if (funcdecl->type->nextOf() == Type::terror) |
| goto Ldone; |
| |
| bool may_override = false; |
| for (size_t i = 0; i < cd->baseclasses->length; i++) |
| { |
| BaseClass *b = (*cd->baseclasses)[i]; |
| ClassDeclaration *cbd = b->type->toBasetype()->isClassHandle(); |
| if (!cbd) |
| continue; |
| for (size_t j = 0; j < cbd->vtbl.length; j++) |
| { |
| FuncDeclaration *f2 = cbd->vtbl[j]->isFuncDeclaration(); |
| if (!f2 || f2->ident != funcdecl->ident) |
| continue; |
| if (cbd->parent && cbd->parent->isTemplateInstance()) |
| { |
| if (!f2->functionSemantic()) |
| goto Ldone; |
| } |
| may_override = true; |
| } |
| } |
| if (may_override && funcdecl->type->nextOf() == NULL) |
| { |
| /* If same name function exists in base class but 'this' is auto return, |
| * cannot find index of base class's vtbl[] to override. |
| */ |
| funcdecl->error("return type inference is not supported if may override base class function"); |
| } |
| |
| /* Find index of existing function in base class's vtbl[] to override |
| * (the index will be the same as in cd's current vtbl[]) |
| */ |
| int vi = cd->baseClass ? funcdecl->findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, (int)cd->baseClass->vtbl.length) |
| : -1; |
| |
| bool doesoverride = false; |
| switch (vi) |
| { |
| case -1: |
| Lintro: |
| /* Didn't find one, so |
| * This is an 'introducing' function which gets a new |
| * slot in the vtbl[]. |
| */ |
| |
| // Verify this doesn't override previous final function |
| if (cd->baseClass) |
| { |
| Dsymbol *s = cd->baseClass->search(funcdecl->loc, funcdecl->ident); |
| if (s) |
| { |
| FuncDeclaration *f2 = s->isFuncDeclaration(); |
| if (f2) |
| { |
| f2 = f2->overloadExactMatch(funcdecl->type); |
| if (f2 && f2->isFinalFunc() && f2->prot().kind != Prot::private_) |
| funcdecl->error("cannot override final function %s", f2->toPrettyChars()); |
| } |
| } |
| } |
| |
| /* These quirky conditions mimic what VC++ appears to do |
| */ |
| if (global.params.mscoff && cd->isCPPclass() && |
| cd->baseClass && cd->baseClass->vtbl.length) |
| { |
| /* if overriding an interface function, then this is not |
| * introducing and don't put it in the class vtbl[] |
| */ |
| funcdecl->interfaceVirtual = funcdecl->overrideInterface(); |
| if (funcdecl->interfaceVirtual) |
| { |
| //printf("\tinterface function %s\n", funcdecl->toChars()); |
| cd->vtblFinal.push(funcdecl); |
| goto Linterfaces; |
| } |
| } |
| |
| if (funcdecl->isFinalFunc()) |
| { |
| // Don't check here, as it may override an interface function |
| //if (funcdecl->isOverride()) |
| //funcdecl->error("is marked as override, but does not override any function"); |
| cd->vtblFinal.push(funcdecl); |
| } |
| else |
| { |
| //printf("\tintroducing function %s\n", funcdecl->toChars()); |
| funcdecl->introducing = 1; |
| if (cd->isCPPclass() && target.cpp.reverseOverloads) |
| { |
| // with dmc, overloaded functions are grouped and in reverse order |
| funcdecl->vtblIndex = (int)cd->vtbl.length; |
| for (int i = 0; i < (int)cd->vtbl.length; i++) |
| { |
| if (cd->vtbl[i]->ident == funcdecl->ident && cd->vtbl[i]->parent == parent) |
| { |
| funcdecl->vtblIndex = (int)i; |
| break; |
| } |
| } |
| // shift all existing functions back |
| for (int i = (int)cd->vtbl.length; i > funcdecl->vtblIndex; i--) |
| { |
| FuncDeclaration *fd = cd->vtbl[i-1]->isFuncDeclaration(); |
| assert(fd); |
| fd->vtblIndex++; |
| } |
| cd->vtbl.insert(funcdecl->vtblIndex, funcdecl); |
| } |
| else |
| { |
| // Append to end of vtbl[] |
| vi = (int)cd->vtbl.length; |
| cd->vtbl.push(funcdecl); |
| funcdecl->vtblIndex = vi; |
| } |
| } |
| break; |
| |
| case -2: |
| // can't determine because of forward references |
| funcdecl->errors = true; |
| return; |
| |
| default: |
| { |
| FuncDeclaration *fdv = cd->baseClass->vtbl[vi]->isFuncDeclaration(); |
| FuncDeclaration *fdc = cd->vtbl[vi]->isFuncDeclaration(); |
| // This function is covariant with fdv |
| |
| if (fdc == funcdecl) |
| { |
| doesoverride = true; |
| break; |
| } |
| |
| if (fdc->toParent() == parent) |
| { |
| //printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n", |
| // vi, funcdecl, funcdecl->toChars(), funcdecl->type->toChars(), funcdecl->loc.toChars(), |
| // fdc, fdc ->toChars(), fdc ->type->toChars(), fdc ->loc.toChars(), |
| // fdv, fdv ->toChars(), fdv ->type->toChars(), fdv ->loc.toChars()); |
| |
| // fdc overrides fdv exactly, then this introduces new function. |
| if (fdc->type->mod == fdv->type->mod && funcdecl->type->mod != fdv->type->mod) |
| goto Lintro; |
| } |
| |
| // This function overrides fdv |
| if (fdv->isFinalFunc()) |
| funcdecl->error("cannot override final function %s", fdv->toPrettyChars()); |
| |
| if (!funcdecl->isOverride()) |
| { |
| if (fdv->isFuture()) |
| { |
| ::deprecation(funcdecl->loc, "@__future base class method %s is being overridden by %s; rename the latter", |
| fdv->toPrettyChars(), funcdecl->toPrettyChars()); |
| // Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[] |
| goto Lintro; |
| } |
| else |
| { |
| int vi2 = funcdecl->findVtblIndex(&cd->baseClass->vtbl, (int)cd->baseClass->vtbl.length, false); |
| if (vi2 < 0) |
| // https://issues.dlang.org/show_bug.cgi?id=17349 |
| ::deprecation(funcdecl->loc, "cannot implicitly override base class method `%s` with `%s`; add `override` attribute", |
| fdv->toPrettyChars(), funcdecl->toPrettyChars()); |
| else |
| error(funcdecl->loc, "implicitly overriding base class method %s with %s deprecated; add `override` attribute", |
| fdv->toPrettyChars(), funcdecl->toPrettyChars()); |
| } |
| } |
| |
| doesoverride = true; |
| if (fdc->toParent() == parent) |
| { |
| // If both are mixins, or both are not, then error. |
| // If either is not, the one that is not overrides the other. |
| bool thismixin = funcdecl->parent->isClassDeclaration() != NULL; |
| bool fdcmixin = fdc->parent->isClassDeclaration() != NULL; |
| if (thismixin == fdcmixin) |
| { |
| funcdecl->error("multiple overrides of same function"); |
| } |
| else if (!thismixin) // fdc overrides fdv |
| { |
| // this doesn't override any function |
| break; |
| } |
| } |
| cd->vtbl[vi] = funcdecl; |
| funcdecl->vtblIndex = vi; |
| |
| /* Remember which functions this overrides |
| */ |
| funcdecl->foverrides.push(fdv); |
| |
| /* This works by whenever this function is called, |
| * it actually returns tintro, which gets dynamically |
| * cast to type. But we know that tintro is a base |
| * of type, so we could optimize it by not doing a |
| * dynamic cast, but just subtracting the isBaseOf() |
| * offset if the value is != null. |
| */ |
| |
| if (fdv->tintro) |
| funcdecl->tintro = fdv->tintro; |
| else if (!funcdecl->type->equals(fdv->type)) |
| { |
| /* Only need to have a tintro if the vptr |
| * offsets differ |
| */ |
| int offset; |
| if (fdv->type->nextOf()->isBaseOf(funcdecl->type->nextOf(), &offset)) |
| { |
| funcdecl->tintro = fdv->type; |
| } |
| } |
| break; |
| } |
| } |
| |
| /* Go through all the interface bases. |
| * If this function is covariant with any members of those interface |
| * functions, set the tintro. |
| */ |
| Linterfaces: |
| for (size_t i = 0; i < cd->interfaces.length; i++) |
| { |
| BaseClass *b = cd->interfaces.ptr[i]; |
| vi = funcdecl->findVtblIndex((Dsymbols *)&b->sym->vtbl, (int)b->sym->vtbl.length); |
| switch (vi) |
| { |
| case -1: |
| break; |
| |
| case -2: |
| // can't determine because of forward references |
| funcdecl->errors = true; |
| return; |
| |
| default: |
| { |
| FuncDeclaration *fdv = (FuncDeclaration *)b->sym->vtbl[vi]; |
| Type *ti = NULL; |
| |
| /* Remember which functions this overrides |
| */ |
| funcdecl->foverrides.push(fdv); |
| |
| /* Should we really require 'override' when implementing |
| * an interface function? |
| */ |
| //if (!funcdecl->isOverride()) |
| //warning(funcdecl->loc, "overrides base class function %s, but is not marked with `override`", fdv->toPrettyChars()); |
| |
| if (fdv->tintro) |
| ti = fdv->tintro; |
| else if (!funcdecl->type->equals(fdv->type)) |
| { |
| /* Only need to have a tintro if the vptr |
| * offsets differ |
| */ |
| int offset; |
| if (fdv->type->nextOf()->isBaseOf(funcdecl->type->nextOf(), &offset)) |
| { |
| ti = fdv->type; |
| } |
| } |
| if (ti) |
| { |
| if (funcdecl->tintro) |
| { |
| if (!funcdecl->tintro->nextOf()->equals(ti->nextOf()) && |
| !funcdecl->tintro->nextOf()->isBaseOf(ti->nextOf(), NULL) && |
| !ti->nextOf()->isBaseOf(funcdecl->tintro->nextOf(), NULL)) |
| { |
| funcdecl->error("incompatible covariant types %s and %s", funcdecl->tintro->toChars(), ti->toChars()); |
| } |
| } |
| funcdecl->tintro = ti; |
| } |
| goto L2; |
| } |
| } |
| } |
| |
| if (!doesoverride && funcdecl->isOverride() && (funcdecl->type->nextOf() || !may_override)) |
| { |
| BaseClass *bc = NULL; |
| Dsymbol *s = NULL; |
| for (size_t i = 0; i < cd->baseclasses->length; i++) |
| { |
| bc = (*cd->baseclasses)[i]; |
| s = bc->sym->search_correct(funcdecl->ident); |
| if (s) break; |
| } |
| |
| if (s) |
| funcdecl->error("does not override any function, did you mean to override `%s%s`?", |
| bc->sym->isCPPclass() ? "extern (C++) " : "", s->toPrettyChars()); |
| else |
| funcdecl->error("does not override any function"); |
| } |
| |
| L2: ; |
| |
| /* Go through all the interface bases. |
| * Disallow overriding any final functions in the interface(s). |
| */ |
| for (size_t i = 0; i < cd->interfaces.length; i++) |
| { |
| BaseClass *b = cd->interfaces.ptr[i]; |
| if (b->sym) |
| { |
| Dsymbol *s = search_function(b->sym, funcdecl->ident); |
| if (s) |
| { |
| FuncDeclaration *f2 = s->isFuncDeclaration(); |
| if (f2) |
| { |
| f2 = f2->overloadExactMatch(funcdecl->type); |
| if (f2 && f2->isFinalFunc() && f2->prot().kind != Prot::private_) |
| funcdecl->error("cannot override final function %s.%s", b->sym->toChars(), f2->toPrettyChars()); |
| } |
| } |
| } |
| } |
| |
| if (funcdecl->isOverride()) |
| { |
| if (funcdecl->storage_class & STCdisable) |
| funcdecl->deprecation("overridden functions cannot be annotated @disable"); |
| if (funcdecl->isDeprecated()) |
| funcdecl->deprecation("deprecated functions cannot be annotated @disable"); |
| } |
| } |
| else if (funcdecl->isOverride() && !parent->isTemplateInstance()) |
| funcdecl->error("override only applies to class member functions"); |
| |
| // Reflect this->type to f because it could be changed by findVtblIndex |
| f = funcdecl->type->toTypeFunction(); |
| |
| Ldone: |
| /* Contracts can only appear without a body when they are virtual interface functions |
| */ |
| if (!funcdecl->fbody && !allowsContractWithoutBody(funcdecl)) |
| funcdecl->error("in and out contracts can only appear without a body when they are virtual interface functions or abstract"); |
| |
| /* Do not allow template instances to add virtual functions |
| * to a class. |
| */ |
| if (funcdecl->isVirtual()) |
| { |
| 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 |
| ClassDeclaration *cd = ti->tempdecl->isClassMember(); |
| if (cd) |
| { |
| funcdecl->error("cannot use template to add virtual function to class `%s`", cd->toChars()); |
| } |
| } |
| } |
| |
| if (funcdecl->isMain()) |
| funcdecl->checkDmain(); // Check main() parameters and return type |
| |
| /* Purity and safety can be inferred for some functions by examining |
| * the function body. |
| */ |
| if (canInferAttributes(funcdecl, sc)) |
| initInferAttributes(funcdecl); |
| |
| Module::dprogress++; |
| funcdecl->semanticRun = PASSsemanticdone; |
| |
| /* Save scope for possible later use (if we need the |
| * function internals) |
| */ |
| funcdecl->_scope = sc->copy(); |
| funcdecl->_scope->setNoFree(); |
| |
| static bool printedMain = false; // semantic might run more than once |
| if (global.params.verbose && !printedMain) |
| { |
| const char *type = funcdecl->isMain() ? "main" : funcdecl->isWinMain() ? "winmain" : funcdecl->isDllMain() ? "dllmain" : (const char *)NULL; |
| Module *mod = sc->_module; |
| |
| if (type && mod) |
| { |
| printedMain = true; |
| const char *name = mod->srcfile->toChars(); |
| const char *path = FileName::searchPath(global.path, name, true); |
| message("entry %-10s\t%s", type, path ? path : name); |
| } |
| } |
| |
| if (funcdecl->fbody && funcdecl->isMain() && sc->_module->isRoot()) |
| Compiler::genCmain(sc); |
| |
| assert(funcdecl->type->ty != Terror || funcdecl->errors); |
| |
| // semantic for parameters' UDAs |
| const size_t nparams = f->parameterList.length(); |
| for (size_t i = 0; i < nparams; i++) |
| { |
| Parameter *param = f->parameterList[i]; |
| if (param && param->userAttribDecl) |
| dsymbolSemantic(param->userAttribDecl, sc); |
| } |
| } |
| |
| // Do the semantic analysis on the external interface to the function. |
| void visit(FuncDeclaration *funcdecl) |
| { |
| funcDeclarationSemantic(funcdecl); |
| } |
| |
| void visit(CtorDeclaration *ctd) |
| { |
| //printf("CtorDeclaration::semantic() %s\n", ctd->toChars()); |
| if (ctd->semanticRun >= PASSsemanticdone) |
| return; |
| if (ctd->_scope) |
| { |
| sc = ctd->_scope; |
| ctd->_scope = NULL; |
| } |
| |
| ctd->parent = sc->parent; |
| Dsymbol *p = ctd->toParent2(); |
| AggregateDeclaration *ad = p->isAggregateDeclaration(); |
| if (!ad) |
| { |
| error(ctd->loc, "constructor can only be a member of aggregate, not %s %s", |
| p->kind(), p->toChars()); |
| ctd->type = Type::terror; |
| ctd->errors = true; |
| return; |
| } |
| |
| sc = sc->push(); |
| sc->stc &= ~STCstatic; // not a static constructor |
| sc->flags |= SCOPEctor; |
| |
| funcDeclarationSemantic(ctd); |
| |
| sc->pop(); |
| |
| if (ctd->errors) |
| return; |
| |
| TypeFunction *tf = ctd->type->toTypeFunction(); |
| |
| /* See if it's the default constructor |
| * But, template constructor should not become a default constructor. |
| */ |
| if (ad && (!ctd->parent->isTemplateInstance() || ctd->parent->isTemplateMixin())) |
| { |
| const size_t dim = tf->parameterList.length(); |
| |
| if (StructDeclaration *sd = ad->isStructDeclaration()) |
| { |
| if (dim == 0 && tf->parameterList.varargs == VARARGnone) // empty default ctor w/o any varargs |
| { |
| if (ctd->fbody || !(ctd->storage_class & STCdisable) || dim) |
| { |
| ctd->error("default constructor for structs only allowed " |
| "with @disable, no body, and no parameters"); |
| ctd->storage_class |= STCdisable; |
| ctd->fbody = NULL; |
| } |
| sd->noDefaultCtor = true; |
| } |
| else if (dim == 0 && tf->parameterList.varargs) // allow varargs only ctor |
| { |
| } |
| else if (dim && tf->parameterList[0]->defaultArg) |
| { |
| // if the first parameter has a default argument, then the rest does as well |
| if (ctd->storage_class & STCdisable) |
| { |
| ctd->deprecation("@disable'd constructor cannot have default " |
| "arguments for all parameters."); |
| deprecationSupplemental(ctd->loc, "Use @disable this(); if you want to disable default initialization."); |
| } |
| else |
| ctd->deprecation("all parameters have default arguments, " |
| "but structs cannot have default constructors."); |
| } |
| |
| } |
| else if (dim == 0 && tf->parameterList.varargs == VARARGnone) |
| { |
| ad->defaultCtor = ctd; |
| } |
| } |
| } |
| |
| void visit(PostBlitDeclaration *pbd) |
| { |
| //printf("PostBlitDeclaration::semantic() %s\n", pbd->toChars()); |
| //printf("ident: %s, %s, %p, %p\n", pbd->ident->toChars(), Id::dtor->toChars(), pbd->ident, Id::dtor); |
| //printf("stc = x%llx\n", sc->stc); |
| if (pbd->semanticRun >= PASSsemanticdone) |
| return; |
| if (pbd->_scope) |
| { |
| sc = pbd->_scope; |
| pbd->_scope = NULL; |
| } |
| |
| pbd->parent = sc->parent; |
| Dsymbol *p = pbd->toParent2(); |
| StructDeclaration *ad = p->isStructDeclaration(); |
| if (!ad) |
| { |
| error(pbd->loc, "postblit can only be a member of struct/union, not %s %s", |
| p->kind(), p->toChars()); |
| pbd->type = Type::terror; |
| pbd->errors = true; |
| return; |
| } |
| if (pbd->ident == Id::postblit && pbd->semanticRun < PASSsemantic) |
| ad->postblits.push(pbd); |
| if (!pbd->type) |
| pbd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, pbd->storage_class); |
| |
| sc = sc->push(); |
| sc->stc &= ~STCstatic; // not static |
| sc->linkage = LINKd; |
| |
| funcDeclarationSemantic(pbd); |
| |
| sc->pop(); |
| } |
| |
| void visit(DtorDeclaration *dd) |
| { |
| //printf("DtorDeclaration::semantic() %s\n", dd->toChars()); |
| //printf("ident: %s, %s, %p, %p\n", dd->ident->toChars(), Id::dtor->toChars(), dd->ident, Id::dtor); |
| if (dd->semanticRun >= PASSsemanticdone) |
| return; |
| if (dd->_scope) |
| { |
| sc = dd->_scope; |
| dd->_scope = NULL; |
| } |
| |
| dd->parent = sc->parent; |
| Dsymbol *p = dd->toParent2(); |
| AggregateDeclaration *ad = p->isAggregateDeclaration(); |
| if (!ad) |
| { |
| error(dd->loc, "destructor can only be a member of aggregate, not %s %s", |
| p->kind(), p->toChars()); |
| dd->type = Type::terror; |
| dd->errors = true; |
| return; |
| } |
| if (dd->ident == Id::dtor && dd->semanticRun < PASSsemantic) |
| ad->dtors.push(dd); |
| if (!dd->type) |
| dd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, dd->storage_class); |
| |
| sc = sc->push(); |
| sc->stc &= ~STCstatic; // not a static destructor |
| if (sc->linkage != LINKcpp) |
| sc->linkage = LINKd; |
| |
| funcDeclarationSemantic(dd); |
| |
| sc->pop(); |
| } |
| |
| void visit(StaticCtorDeclaration *scd) |
| { |
| //printf("StaticCtorDeclaration::semantic()\n"); |
| if (scd->semanticRun >= PASSsemanticdone) |
| return; |
| if (scd->_scope) |
| { |
| sc = scd->_scope; |
| scd->_scope = NULL; |
| } |
| |
| scd->parent = sc->parent; |
| Dsymbol *p = scd->parent->pastMixin(); |
| if (!p->isScopeDsymbol()) |
| { |
| const char *s = (scd->isSharedStaticCtorDeclaration() ? "shared " : ""); |
| error(scd->loc, "%sstatic constructor can only be member of module/aggregate/template, not %s %s", |
| s, p->kind(), p->toChars()); |
| scd->type = Type::terror; |
| scd->errors = true; |
| return; |
| } |
| if (!scd->type) |
| scd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, scd->storage_class); |
| |
| /* If the static ctor appears within a template instantiation, |
| * it could get called multiple times by the module constructors |
| * for different modules. Thus, protect it with a gate. |
| */ |
| if (scd->isInstantiated() && scd->semanticRun < PASSsemantic) |
| { |
| /* Add this prefix to the function: |
| * static int gate; |
| * if (++gate != 1) return; |
| * Note that this is not thread safe; should not have threads |
| * during static construction. |
| */ |
| VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL); |
| v->storage_class = STCtemp | (scd->isSharedStaticCtorDeclaration() ? STCstatic : STCtls); |
| Statements *sa = new Statements(); |
| Statement *s = new ExpStatement(Loc(), v); |
| sa->push(s); |
| Expression *e = new IdentifierExp(Loc(), v->ident); |
| e = new AddAssignExp(Loc(), e, new IntegerExp(1)); |
| e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(1)); |
| s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL, Loc()); |
| sa->push(s); |
| if (scd->fbody) |
| sa->push(scd->fbody); |
| scd->fbody = new CompoundStatement(Loc(), sa); |
| } |
| |
| funcDeclarationSemantic(scd); |
| |
| // We're going to need ModuleInfo |
| Module *m = scd->getModule(); |
| if (!m) |
| m = sc->_module; |
| if (m) |
| { |
| m->needmoduleinfo = 1; |
| //printf("module1 %s needs moduleinfo\n", m->toChars()); |
| } |
| } |
| |
| void visit(StaticDtorDeclaration *sdd) |
| { |
| if (sdd->semanticRun >= PASSsemanticdone) |
| return; |
| if (sdd->_scope) |
| { |
| sc = sdd->_scope; |
| sdd->_scope = NULL; |
| } |
| |
| sdd->parent = sc->parent; |
| Dsymbol *p = sdd->parent->pastMixin(); |
| if (!p->isScopeDsymbol()) |
| { |
| const char *s = (sdd->isSharedStaticDtorDeclaration() ? "shared " : ""); |
| error(sdd->loc, "%sstatic destructor can only be member of module/aggregate/template, not %s %s", |
| s, p->kind(), p->toChars()); |
| sdd->type = Type::terror; |
| sdd->errors = true; |
| return; |
| } |
| if (!sdd->type) |
| sdd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, sdd->storage_class); |
| |
| /* If the static ctor appears within a template instantiation, |
| * it could get called multiple times by the module constructors |
| * for different modules. Thus, protect it with a gate. |
| */ |
| if (sdd->isInstantiated() && sdd->semanticRun < PASSsemantic) |
| { |
| /* Add this prefix to the function: |
| * static int gate; |
| * if (--gate != 0) return; |
| * Increment gate during constructor execution. |
| * Note that this is not thread safe; should not have threads |
| * during static destruction. |
| */ |
| VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL); |
| v->storage_class = STCtemp | (sdd->isSharedStaticDtorDeclaration() ? STCstatic : STCtls); |
| Statements *sa = new Statements(); |
| Statement *s = new ExpStatement(Loc(), v); |
| sa->push(s); |
| Expression *e = new IdentifierExp(Loc(), v->ident); |
| e = new AddAssignExp(Loc(), e, new IntegerExp(-1)); |
| e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0)); |
| s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL, Loc()); |
| sa->push(s); |
| if (sdd->fbody) |
| sa->push(sdd->fbody); |
| sdd->fbody = new CompoundStatement(Loc(), sa); |
| sdd->vgate = v; |
| } |
| |
| funcDeclarationSemantic(sdd); |
| |
| // We're going to need ModuleInfo |
| Module *m = sdd->getModule(); |
| if (!m) |
| m = sc->_module; |
| if (m) |
| { |
| m->needmoduleinfo = 1; |
| //printf("module2 %s needs moduleinfo\n", m->toChars()); |
| } |
| } |
| |
| void visit(InvariantDeclaration *invd) |
| { |
| if (invd->semanticRun >= PASSsemanticdone) |
| return; |
| if (invd->_scope) |
| { |
| sc = invd->_scope; |
| invd->_scope = NULL; |
| } |
| |
| invd->parent = sc->parent; |
| Dsymbol *p = invd->parent->pastMixin(); |
| AggregateDeclaration *ad = p->isAggregateDeclaration(); |
| if (!ad) |
| { |
| error(invd->loc, "invariant can only be a member of aggregate, not %s %s", |
| p->kind(), p->toChars()); |
| invd->type = Type::terror; |
| invd->errors = true; |
| return; |
| } |
| if (invd->ident != Id::classInvariant && |
| invd->semanticRun < PASSsemantic && |
| !ad->isUnionDeclaration() // users are on their own with union fields |
| ) |
| ad->invs.push(invd); |
| if (!invd->type) |
| invd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, invd->storage_class); |
| |
| sc = sc->push(); |
| sc->stc &= ~STCstatic; // not a static invariant |
| sc->stc |= STCconst; // invariant() is always const |
| sc->flags = (sc->flags & ~SCOPEcontract) | SCOPEinvariant; |
| sc->linkage = LINKd; |
| |
| funcDeclarationSemantic(invd); |
| |
| sc->pop(); |
| } |
| |
| void visit(UnitTestDeclaration *utd) |
| { |
| if (utd->semanticRun >= PASSsemanticdone) |
| return; |
| if (utd->_scope) |
| { |
| sc = utd->_scope; |
| utd->_scope = NULL; |
| } |
| |
| utd->protection = sc->protection; |
| |
| utd->parent = sc->parent; |
| Dsymbol *p = utd->parent->pastMixin(); |
| if (!p->isScopeDsymbol()) |
| { |
| error(utd->loc, "unittest can only be a member of module/aggregate/template, not %s %s", |
| p->kind(), p->toChars()); |
| utd->type = Type::terror; |
| utd->errors = true; |
| return; |
| } |
| |
| if (global.params.useUnitTests) |
| { |
| if (!utd->type) |
| utd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, utd->storage_class); |
| Scope *sc2 = sc->push(); |
| sc2->linkage = LINKd; |
| funcDeclarationSemantic(utd); |
| sc2->pop(); |
| } |
| } |
| |
| void visit(NewDeclaration *nd) |
| { |
| //printf("NewDeclaration::semantic()\n"); |
| if (nd->semanticRun >= PASSsemanticdone) |
| return; |
| if (nd->_scope) |
| { |
| sc = nd->_scope; |
| nd->_scope = NULL; |
| } |
| |
| nd->parent = sc->parent; |
| Dsymbol *p = nd->parent->pastMixin(); |
| if (!p->isAggregateDeclaration()) |
| { |
| error(nd->loc, "allocator can only be a member of aggregate, not %s %s", |
| p->kind(), p->toChars()); |
| nd->type = Type::terror; |
| nd->errors = true; |
| return; |
| } |
| Type *tret = Type::tvoid->pointerTo(); |
| if (!nd->type) |
| nd->type = new TypeFunction(ParameterList(nd->parameters, nd->varargs), tret, LINKd, nd->storage_class); |
| |
| nd->type = typeSemantic(nd->type, nd->loc, sc); |
| |
| // Check that there is at least one argument of type size_t |
| TypeFunction *tf = nd->type->toTypeFunction(); |
| if (tf->parameterList.length() < 1) |
| { |
| nd->error("at least one argument of type size_t expected"); |
| } |
| else |
| { |
| Parameter *fparam = tf->parameterList[0]; |
| if (!fparam->type->equals(Type::tsize_t)) |
| nd->error("first argument must be type size_t, not %s", fparam->type->toChars()); |
| } |
| |
| funcDeclarationSemantic(nd); |
| } |
| |
| void visit(DeleteDeclaration *deld) |
| { |
| //printf("DeleteDeclaration::semantic()\n"); |
| if (deld->semanticRun >= PASSsemanticdone) |
| return; |
| if (deld->_scope) |
| { |
| sc = deld->_scope; |
| deld->_scope = NULL; |
| } |
| |
| deld->parent = sc->parent; |
| Dsymbol *p = deld->parent->pastMixin(); |
| if (!p->isAggregateDeclaration()) |
| { |
| error(deld->loc, "deallocator can only be a member of aggregate, not %s %s", |
| p->kind(), p->toChars()); |
| deld->type = Type::terror; |
| deld->errors = true; |
| return; |
| } |
| if (!deld->type) |
| deld->type = new TypeFunction(ParameterList(deld->parameters), Type::tvoid, LINKd, deld->storage_class); |
| |
| deld->type = typeSemantic(deld->type, deld->loc, sc); |
| |
| // Check that there is only one argument of type void* |
| TypeFunction *tf = deld->type->toTypeFunction(); |
| if (tf->parameterList.length() != 1) |
| { |
| deld->error("one argument of type void* expected"); |
| } |
| else |
| { |
| Parameter *fparam = tf->parameterList[0]; |
| if (!fparam->type->equals(Type::tvoid->pointerTo())) |
| deld->error("one argument of type void* expected, not %s", fparam->type->toChars()); |
| } |
| |
| funcDeclarationSemantic(deld); |
| } |
| |
| void visit(StructDeclaration *sd) |
| { |
| //printf("StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", sd, sd->parent->toChars(), sd->toChars(), sizeok); |
| |
| //static int count; if (++count == 20) halt(); |
| |
| if (sd->semanticRun >= PASSsemanticdone) |
| return; |
| unsigned errors = global.errors; |
| |
| //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", sd, sd->parent->toChars(), sd->toChars(), sizeok); |
| Scope *scx = NULL; |
| if (sd->_scope) |
| { |
| sc = sd->_scope; |
| scx = sd->_scope; // save so we don't make redundant copies |
| sd->_scope = NULL; |
| } |
| |
| if (!sd->parent) |
| { |
| assert(sc->parent && sc->func); |
| sd->parent = sc->parent; |
| } |
| assert(sd->parent && !sd->isAnonymous()); |
| |
| if (sd->errors) |
| sd->type = Type::terror; |
| if (sd->semanticRun == PASSinit) |
| sd->type = sd->type->addSTC(sc->stc | sd->storage_class); |
| sd->type = typeSemantic(sd->type, sd->loc, sc); |
| |
| if (sd->type->ty == Tstruct && ((TypeStruct *)sd->type)->sym != sd) |
| { |
| TemplateInstance *ti = ((TypeStruct *)sd->type)->sym->isInstantiated(); |
| if (ti && isError(ti)) |
| ((TypeStruct *)sd->type)->sym = sd; |
| } |
| |
| // Ungag errors when not speculative |
| Ungag ungag = sd->ungagSpeculative(); |
| |
| if (sd->semanticRun == PASSinit) |
| { |
| sd->protection = sc->protection; |
| |
| sd->alignment = sc->alignment(); |
| |
| sd->storage_class |= sc->stc; |
| if (sd->storage_class & STCdeprecated) |
| sd->isdeprecated = true; |
| if (sd->storage_class & STCabstract) |
| sd->error("structs, unions cannot be abstract"); |
| sd->userAttribDecl = sc->userAttribDecl; |
| |
| if (sc->linkage == LINKcpp) |
| sd->classKind = ClassKind::cpp; |
| } |
| else if (sd->symtab && !scx) |
| { |
| return; |
| } |
| sd->semanticRun = PASSsemantic; |
| |
| if (!sd->members) // if opaque declaration |
| { |
| sd->semanticRun = PASSsemanticdone; |
| return; |
| } |
| if (!sd->symtab) |
| { |
| sd->symtab = new DsymbolTable(); |
| |
| for (size_t i = 0; i < sd->members->length; i++) |
| { |
| Dsymbol *s = (*sd->members)[i]; |
| //printf("adding member '%s' to '%s'\n", s->toChars(), sd->toChars()); |
| s->addMember(sc, sd); |
| } |
| } |
| |
| Scope *sc2 = sd->newScope(sc); |
| |
| /* Set scope so if there are forward references, we still might be able to |
| * resolve individual members like enums. |
| */ |
| for (size_t i = 0; i < sd->members->length; i++) |
| { |
| Dsymbol *s = (*sd->members)[i]; |
| //printf("struct: setScope %s %s\n", s->kind(), s->toChars()); |
| s->setScope(sc2); |
| } |
| |
| for (size_t i = 0; i < sd->members->length; i++) |
| { |
| Dsymbol *s = (*sd->members)[i]; |
| s->importAll(sc2); |
| } |
| |
| for (size_t i = 0; i < sd->members->length; i++) |
| { |
| Dsymbol *s = (*sd->members)[i]; |
| dsymbolSemantic(s, sc2); |
| } |
| |
| if (!sd->determineFields()) |
| { |
| assert(sd->type->ty == Terror); |
| sc2->pop(); |
| sd->semanticRun = PASSsemanticdone; |
| return; |
| } |
| |
| /* Following special member functions creation needs semantic analysis |
| * completion of sub-structs in each field types. For example, buildDtor |
| * needs to check existence of elaborate dtor in type of each fields. |
| * See the case in compilable/test14838.d |
| */ |
| for (size_t i = 0; i < sd->fields.length; i++) |
| { |
| VarDeclaration *v = sd->fields[i]; |
| Type *tb = v->type->baseElemOf(); |
| if (tb->ty != Tstruct) |
| continue; |
| StructDeclaration *sdec = ((TypeStruct *)tb)->sym; |
| if (sdec->semanticRun >= PASSsemanticdone) |
| continue; |
| |
| sc2->pop(); |
| |
| sd->_scope = scx ? scx : sc->copy(); |
| sd->_scope->setNoFree(); |
| Module::addDeferredSemantic(sd); |
| |
| //printf("\tdeferring %s\n", sd->toChars()); |
| return; |
| } |
| |
| /* Look for special member functions. |
| */ |
| sd->aggNew = (NewDeclaration *)sd->search(Loc(), Id::classNew); |
| sd->aggDelete = (DeleteDeclaration *)sd->search(Loc(), Id::classDelete); |
| |
| // Look for the constructor |
| sd->ctor = sd->searchCtor(); |
| |
| sd->dtor = buildDtor(sd, sc2); |
| sd->postblit = buildPostBlit(sd, sc2); |
| |
| buildOpAssign(sd, sc2); |
| buildOpEquals(sd, sc2); |
| |
| if (global.params.useTypeInfo && Type::dtypeinfo) // these functions are used for TypeInfo |
| { |
| sd->xeq = buildXopEquals(sd, sc2); |
| sd->xcmp = buildXopCmp(sd, sc2); |
| sd->xhash = buildXtoHash(sd, sc2); |
| } |
| |
| sd->inv = buildInv(sd, sc2); |
| |
| Module::dprogress++; |
| sd->semanticRun = PASSsemanticdone; |
| //printf("-StructDeclaration::semantic(this=%p, '%s')\n", sd, sd->toChars()); |
| |
| sc2->pop(); |
| |
| if (sd->ctor) |
| { |
| Dsymbol *scall = sd->search(Loc(), Id::call); |
| if (scall) |
| { |
| unsigned xerrors = global.startGagging(); |
| sc = sc->push(); |
| sc->tinst = NULL; |
| sc->minst = NULL; |
| FuncDeclaration *fcall = resolveFuncCall(sd->loc, sc, scall, NULL, NULL, NULL, 1); |
| sc = sc->pop(); |
| global.endGagging(xerrors); |
| |
| if (fcall && fcall->isStatic()) |
| { |
| sd->error(fcall->loc, "`static opCall` is hidden by constructors and can never be called"); |
| errorSupplemental(fcall->loc, "Please use a factory method instead, or replace all constructors with `static opCall`."); |
| } |
| } |
| } |
| |
| if (sd->type->ty == Tstruct && ((TypeStruct *)sd->type)->sym != sd) |
| { |
| // https://issues.dlang.org/show_bug.cgi?id=19024 |
| StructDeclaration *sym = ((TypeStruct *)sd->type)->sym; |
| sd->error("already exists at %s. Perhaps in another function with the same name?", sym->loc.toChars()); |
| } |
| |
| if (global.errors != errors) |
| { |
| // The type is no good. |
| sd->type = Type::terror; |
| sd->errors = true; |
| if (sd->deferred) |
| sd->deferred->errors = true; |
| } |
| |
| if (sd->deferred && !global.gag) |
| { |
| semantic2(sd->deferred, sc); |
| semantic3(sd->deferred, sc); |
| } |
| } |
| |
| void interfaceSemantic(ClassDeclaration *cd) |
| { |
| cd->vtblInterfaces = new BaseClasses(); |
| cd->vtblInterfaces->reserve(cd->interfaces.length); |
| |
| for (size_t i = 0; i < cd->interfaces.length; i++) |
| { |
| BaseClass *b = cd->interfaces.ptr[i]; |
| cd->vtblInterfaces->push(b); |
| b->copyBaseInterfaces(cd->vtblInterfaces); |
| } |
| } |
| |
| void visit(ClassDeclaration *cldec) |
| { |
| //printf("ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", cldec->toChars(), cldec->type, sizeok, cldec); |
| //printf("\tparent = %p, '%s'\n", sc->parent, sc->parent ? sc->parent->toChars() : ""); |
| //printf("sc->stc = %x\n", sc->stc); |
| |
| //{ static int n; if (++n == 20) *(char*)0=0; } |
| |
| if (cldec->semanticRun >= PASSsemanticdone) |
| return; |
| unsigned errors = global.errors; |
| |
| //printf("+ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", cldec->toChars(), cldec->type, sizeok, cldec); |
| |
| Scope *scx = NULL; |
| if (cldec->_scope) |
| { |
| sc = cldec->_scope; |
| scx = cldec->_scope; // save so we don't make redundant copies |
| cldec->_scope = NULL; |
| } |
| |
| if (!cldec->parent) |
| { |
| assert(sc->parent); |
| cldec->parent = sc->parent; |
| } |
| |
| if (cldec->errors) |
| cldec->type = Type::terror; |
| cldec->type = typeSemantic(cldec->type, cldec->loc, sc); |
| |
| if (cldec->type->ty == Tclass && ((TypeClass *)cldec->type)->sym != cldec) |
| { |
| TemplateInstance *ti = ((TypeClass *)cldec->type)->sym->isInstantiated(); |
| if (ti && isError(ti)) |
| ((TypeClass *)cldec->type)->sym = cldec; |
| } |
| |
| // Ungag errors when not speculative |
| Ungag ungag = cldec->ungagSpeculative(); |
| |
| if (cldec->semanticRun == PASSinit) |
| { |
| cldec->protection = sc->protection; |
| |
| cldec->storage_class |= sc->stc; |
| if (cldec->storage_class & STCdeprecated) |
| cldec->isdeprecated = true; |
| if (cldec->storage_class & STCauto) |
| cldec->error("storage class `auto` is invalid when declaring a class, did you mean to use `scope`?"); |
| if (cldec->storage_class & STCscope) |
| cldec->isscope = true; |
| if (cldec->storage_class & STCabstract) |
| cldec->isabstract = ABSyes; |
| |
| cldec->userAttribDecl = sc->userAttribDecl; |
| |
| if (sc->linkage == LINKcpp) |
| cldec->classKind = ClassKind::cpp; |
| if (sc->linkage == LINKobjc) |
| objc()->setObjc(cldec); |
| } |
| else if (cldec->symtab && !scx) |
| { |
| return; |
| } |
| cldec->semanticRun = PASSsemantic; |
| |
| if (cldec->baseok < BASEOKdone) |
| { |
| cldec->baseok = BASEOKin; |
| |
| // Expand any tuples in baseclasses[] |
| for (size_t i = 0; i < cldec->baseclasses->length; ) |
| { |
| BaseClass *b = (*cldec->baseclasses)[i]; |
| b->type = resolveBase(cldec, sc, scx, b->type); |
| |
| Type *tb = b->type->toBasetype(); |
| if (tb->ty == Ttuple) |
| { |
| TypeTuple *tup = (TypeTuple *)tb; |
| cldec->baseclasses->remove(i); |
| size_t dim = Parameter::dim(tup->arguments); |
| for (size_t j = 0; j < dim; j++) |
| { |
| Parameter *arg = Parameter::getNth(tup->arguments, j); |
| b = new BaseClass(arg->type); |
| cldec->baseclasses->insert(i + j, b); |
| } |
| } |
| else |
| i++; |
| } |
| |
| if (cldec->baseok >= BASEOKdone) |
| { |
| //printf("%s already semantic analyzed, semanticRun = %d\n", cldec->toChars(), cldec->semanticRun); |
| if (cldec->semanticRun >= PASSsemanticdone) |
| return; |
| goto Lancestorsdone; |
| } |
| |
| // See if there's a base class as first in baseclasses[] |
| if (cldec->baseclasses->length) |
| { |
| BaseClass *b = (*cldec->baseclasses)[0]; |
| Type *tb = b->type->toBasetype(); |
| TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; |
| if (!tc) |
| { |
| if (b->type != Type::terror) |
| cldec->error("base type must be class or interface, not %s", b->type->toChars()); |
| cldec->baseclasses->remove(0); |
| goto L7; |
| } |
| |
| if (tc->sym->isDeprecated()) |
| { |
| if (!cldec->isDeprecated()) |
| { |
| // Deriving from deprecated class makes this one deprecated too |
| cldec->isdeprecated = true; |
| |
| tc->checkDeprecated(cldec->loc, sc); |
| } |
| } |
| |
| if (tc->sym->isInterfaceDeclaration()) |
| goto L7; |
| |
| for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass) |
| { |
| if (cdb == cldec) |
| { |
| cldec->error("circular inheritance"); |
| cldec->baseclasses->remove(0); |
| goto L7; |
| } |
| } |
| |
| /* Bugzilla 11034: Essentially, class inheritance hierarchy |
| * and instance size of each classes are orthogonal information. |
| * Therefore, even if tc->sym->sizeof == SIZEOKnone, |
| * we need to set baseClass field for class covariance check. |
| */ |
| cldec->baseClass = tc->sym; |
| b->sym = cldec->baseClass; |
| |
| if (tc->sym->baseok < BASEOKdone) |
| resolveBase(cldec, sc, scx, tc->sym); // Try to resolve forward reference |
| if (tc->sym->baseok < BASEOKdone) |
| { |
| //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); |
| if (tc->sym->_scope) |
| Module::addDeferredSemantic(tc->sym); |
| cldec->baseok = BASEOKnone; |
| } |
| L7: ; |
| } |
| |
| // Treat the remaining entries in baseclasses as interfaces |
| // Check for errors, handle forward references |
| for (size_t i = (cldec->baseClass ? 1 : 0); i < cldec->baseclasses->length; ) |
| { |
| BaseClass *b = (*cldec->baseclasses)[i]; |
| Type *tb = b->type->toBasetype(); |
| TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; |
| if (!tc || !tc->sym->isInterfaceDeclaration()) |
| { |
| if (b->type != Type::terror) |
| cldec->error("base type must be interface, not %s", b->type->toChars()); |
| cldec->baseclasses->remove(i); |
| continue; |
| } |
| |
| // Check for duplicate interfaces |
| for (size_t j = (cldec->baseClass ? 1 : 0); j < i; j++) |
| { |
| BaseClass *b2 = (*cldec->baseclasses)[j]; |
| if (b2->sym == tc->sym) |
| { |
| cldec->error("inherits from duplicate interface %s", b2->sym->toChars()); |
| cldec->baseclasses->remove(i); |
| continue; |
| } |
| } |
| |
| if (tc->sym->isDeprecated()) |
| { |
| if (!cldec->isDeprecated()) |
| { |
| // Deriving from deprecated class makes this one deprecated too |
| cldec->isdeprecated = true; |
| |
| tc->checkDeprecated(cldec->loc, sc); |
| } |
| } |
| |
| b->sym = tc->sym; |
| |
| if (tc->sym->baseok < BASEOKdone) |
| resolveBase(cldec, sc, scx, tc->sym); // Try to resolve forward reference |
| if (tc->sym->baseok < BASEOKdone) |
| { |
| //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); |
| if (tc->sym->_scope) |
| Module::addDeferredSemantic(tc->sym); |
| cldec->baseok = BASEOKnone; |
| } |
| i++; |
| } |
| if (cldec->baseok == BASEOKnone) |
| { |
| // Forward referencee of one or more bases, try again later |
| cldec->_scope = scx ? scx : sc->copy(); |
| cldec->_scope->setNoFree(); |
| Module::addDeferredSemantic(cldec); |
| //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, cldec->toChars()); |
| return; |
| } |
| cldec->baseok = BASEOKdone; |
| |
| // If no base class, and this is not an Object, use Object as base class |
| if (!cldec->baseClass && cldec->ident != Id::Object && !cldec->isCPPclass()) |
| { |
| if (!ClassDeclaration::object || ClassDeclaration::object->errors) |
| badObjectDotD(cldec); |
| |
| Type *t = ClassDeclaration::object->type; |
| t = typeSemantic(t, cldec->loc, sc)->toBasetype(); |
| if (t->ty == Terror) |
| badObjectDotD(cldec); |
| assert(t->ty == Tclass); |
| TypeClass *tc = (TypeClass *)t; |
| |
| BaseClass *b = new BaseClass(tc); |
| cldec->baseclasses->shift(b); |
| |
| cldec->baseClass = tc->sym; |
| assert(!cldec->baseClass->isInterfaceDeclaration()); |
| b->sym = cldec->baseClass; |
| } |
| if (cldec->baseClass) |
| { |
| if (cldec->baseClass->storage_class & STCfinal) |
| cldec->error("cannot inherit from final class %s", cldec->baseClass->toChars()); |
| |
| // Inherit properties from base class |
| if (cldec->baseClass->isCOMclass()) |
| cldec->com = true; |
| if (cldec->baseClass->isCPPclass()) |
| cldec->classKind = ClassKind::cpp; |
| if (cldec->baseClass->isscope) |
| cldec->isscope = true; |
| cldec->enclosing = cldec->baseClass->enclosing; |
| cldec->storage_class |= cldec->baseClass->storage_class & STC_TYPECTOR; |
| } |
| |
| cldec->interfaces.length = cldec->baseclasses->length - (cldec->baseClass ? 1 : 0); |
| cldec->interfaces.ptr = cldec->baseclasses->tdata() + (cldec->baseClass ? 1 : 0); |
| |
| for (size_t i = 0; i < cldec->interfaces.length; i++) |
| { |
| BaseClass *b = cldec->interfaces.ptr[i]; |
| // If this is an interface, and it derives from a COM interface, |
| // then this is a COM interface too. |
| if (b->sym->isCOMinterface()) |
| cldec->com = true; |
| if (cldec->isCPPclass() && !b->sym->isCPPinterface()) |
| { |
| error(cldec->loc, "C++ class `%s` cannot implement D interface `%s`", |
| cldec->toPrettyChars(), b->sym->toPrettyChars()); |
| } |
| } |
| |
| interfaceSemantic(cldec); |
| } |
| Lancestorsdone: |
| //printf("\tClassDeclaration::semantic(%s) baseok = %d\n", cldec->toChars(), cldec->baseok); |
| |
| if (!cldec->members) // if opaque declaration |
| { |
| cldec->semanticRun = PASSsemanticdone; |
| return; |
| } |
| if (!cldec->symtab) |
| { |
| cldec->symtab = new DsymbolTable(); |
| |
| /* Bugzilla 12152: The semantic analysis of base classes should be finished |
| * before the members semantic analysis of this class, in order to determine |
| * vtbl in this class. However if a base class refers the member of this class, |
| * it can be resolved as a normal forward reference. |
| * Call addMember() and setScope() to make this class members visible from the base classes. |
| */ |
| for (size_t i = 0; i < cldec->members->length; i++) |
| { |
| Dsymbol *s = (*cldec->members)[i]; |
| s->addMember(sc, cldec); |
| } |
| |
| Scope *sc2 = cldec->newScope(sc); |
| |
| /* Set scope so if there are forward references, we still might be able to |
| * resolve individual members like enums. |
| */ |
| for (size_t i = 0; i < cldec->members->length; i++) |
| { |
| Dsymbol *s = (*cldec->members)[i]; |
| //printf("[%d] setScope %s %s, sc2 = %p\n", i, s->kind(), s->toChars(), sc2); |
| s->setScope(sc2); |
| } |
| |
| sc2->pop(); |
| } |
| |
| for (size_t i = 0; i < cldec->baseclasses->length; i++) |
| { |
| BaseClass *b = (*cldec->baseclasses)[i]; |
| Type *tb = b->type->toBasetype(); |
| assert(tb->ty == Tclass); |
| TypeClass *tc = (TypeClass *)tb; |
| |
| if (tc->sym->semanticRun < PASSsemanticdone) |
| { |
| // Forward referencee of one or more bases, try again later |
| cldec->_scope = scx ? scx : sc->copy(); |
| cldec->_scope->setNoFree(); |
| if (tc->sym->_scope) |
| Module::addDeferredSemantic(tc->sym); |
| Module::addDeferredSemantic(cldec); |
| //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, cldec->toChars()); |
| return; |
| } |
| } |
| |
| if (cldec->baseok == BASEOKdone) |
| { |
| cldec->baseok = BASEOKsemanticdone; |
| |
| // initialize vtbl |
| if (cldec->baseClass) |
| { |
| if (cldec->isCPPclass() && cldec->baseClass->vtbl.length == 0) |
| { |
| cldec->error("C++ base class %s needs at least one virtual function", cldec->baseClass->toChars()); |
| } |
| |
| // Copy vtbl[] from base class |
| cldec->vtbl.setDim(cldec->baseClass->vtbl.length); |
| memcpy(cldec->vtbl.tdata(), cldec->baseClass->vtbl.tdata(), sizeof(void *) * cldec->vtbl.length); |
| |
| cldec->vthis = cldec->baseClass->vthis; |
| } |
| else |
| { |
| // No base class, so this is the root of the class hierarchy |
| cldec->vtbl.setDim(0); |
| if (cldec->vtblOffset()) |
| cldec->vtbl.push(cldec); // leave room for classinfo as first member |
| } |
| |
| /* If this is a nested class, add the hidden 'this' |
| * member which is a pointer to the enclosing scope. |
| */ |
| if (cldec->vthis) // if inheriting from nested class |
| { |
| // Use the base class's 'this' member |
| if (cldec->storage_class & STCstatic) |
| cldec->error("static class cannot inherit from nested class %s", cldec->baseClass->toChars()); |
| if (cldec->toParent2() != cldec->baseClass->toParent2() && |
| (!cldec->toParent2() || |
| !cldec->baseClass->toParent2()->getType() || |
| !cldec->baseClass->toParent2()->getType()->isBaseOf(cldec->toParent2()->getType(), NULL))) |
| { |
| if (cldec->toParent2()) |
| { |
| cldec->error("is nested within %s, but super class %s is nested within %s", |
| cldec->toParent2()->toChars(), |
| cldec->baseClass->toChars(), |
| cldec->baseClass->toParent2()->toChars()); |
| } |
| else |
| { |
| cldec->error("is not nested, but super class %s is nested within %s", |
| cldec->baseClass->toChars(), |
| cldec->baseClass->toParent2()->toChars()); |
| } |
| cldec->enclosing = NULL; |
| } |
| } |
| else |
| cldec->makeNested(); |
| } |
| |
| Scope *sc2 = cldec->newScope(sc); |
| |
| for (size_t i = 0; i < cldec->members->length; i++) |
| { |
| Dsymbol *s = (*cldec->members)[i]; |
| s->importAll(sc2); |
| } |
| |
| // Note that members.length can grow due to tuple expansion during semantic() |
| for (size_t i = 0; i < cldec->members->length; i++) |
| { |
| Dsymbol *s = (*cldec->members)[i]; |
| dsymbolSemantic(s, sc2); |
| } |
| |
| if (!cldec->determineFields()) |
| { |
| assert(cldec->type == Type::terror); |
| sc2->pop(); |
| return; |
| } |
| |
| /* Following special member functions creation needs semantic analysis |
| * completion of sub-structs in each field types. |
| */ |
| for (size_t i = 0; i < cldec->fields.length; i++) |
| { |
| VarDeclaration *v = cldec->fields[i]; |
| Type *tb = v->type->baseElemOf(); |
| if (tb->ty != Tstruct) |
| continue; |
| StructDeclaration *sd = ((TypeStruct *)tb)->sym; |
| if (sd->semanticRun >= PASSsemanticdone) |
| continue; |
| |
| sc2->pop(); |
| |
| cldec->_scope = scx ? scx : sc->copy(); |
| cldec->_scope->setNoFree(); |
| Module::addDeferredSemantic(cldec); |
| //printf("\tdeferring %s\n", cldec->toChars()); |
| return; |
| } |
| |
| /* Look for special member functions. |
| * They must be in this class, not in a base class. |
| */ |
| |
| // Can be in base class |
| cldec->aggNew = (NewDeclaration *)cldec->search(Loc(), Id::classNew); |
| cldec->aggDelete = (DeleteDeclaration *)cldec->search(Loc(), Id::classDelete); |
| |
| // Look for the constructor |
| cldec->ctor = cldec->searchCtor(); |
| |
| if (!cldec->ctor && cldec->noDefaultCtor) |
| { |
| // A class object is always created by constructor, so this check is legitimate. |
| for (size_t i = 0; i < cldec->fields.length; i++) |
| { |
| VarDeclaration *v = cldec->fields[i]; |
| if (v->storage_class & STCnodefaultctor) |
| error(v->loc, "field %s must be initialized in constructor", v->toChars()); |
| } |
| } |
| |
| // If this class has no constructor, but base class has a default |
| // ctor, create a constructor: |
| // this() { } |
| if (!cldec->ctor && cldec->baseClass && cldec->baseClass->ctor) |
| { |
| FuncDeclaration *fd = resolveFuncCall(cldec->loc, sc2, cldec->baseClass->ctor, NULL, cldec->type, NULL, 1); |
| if (!fd) // try shared base ctor instead |
| fd = resolveFuncCall(cldec->loc, sc2, cldec->baseClass->ctor, NULL, cldec->type->sharedOf(), NULL, 1); |
| if (fd && !fd->errors) |
| { |
| //printf("Creating default this(){} for class %s\n", cldec->toChars()); |
| TypeFunction *btf = fd->type->toTypeFunction(); |
| TypeFunction *tf = new TypeFunction(ParameterList(), NULL, LINKd, fd->storage_class); |
| tf->mod = btf->mod; |
| tf->purity = btf->purity; |
| tf->isnothrow = btf->isnothrow; |
| tf->isnogc = btf->isnogc; |
| tf->trust = btf->trust; |
| |
| CtorDeclaration *ctor = new CtorDeclaration(cldec->loc, Loc(), 0, tf); |
| ctor->fbody = new CompoundStatement(Loc(), new Statements()); |
| |
| cldec->members->push(ctor); |
| ctor->addMember(sc, cldec); |
| dsymbolSemantic(ctor, sc2); |
| |
| cldec->ctor = ctor; |
| cldec->defaultCtor = ctor; |
| } |
| else |
| { |
| cldec->error("cannot implicitly generate a default ctor when base class %s is missing a default ctor", |
| cldec->baseClass->toPrettyChars()); |
| } |
| } |
| |
| cldec->dtor = buildDtor(cldec, sc2); |
| |
| if (FuncDeclaration *f = hasIdentityOpAssign(cldec, sc2)) |
| { |
| if (!(f->storage_class & STCdisable)) |
| cldec->error(f->loc, "identity assignment operator overload is illegal"); |
| } |
| |
| cldec->inv = buildInv(cldec, sc2); |
| |
| Module::dprogress++; |
| cldec->semanticRun = PASSsemanticdone; |
| //printf("-ClassDeclaration.semantic(%s), type = %p\n", cldec->toChars(), cldec->type); |
| //members.print(); |
| |
| sc2->pop(); |
| |
| if (cldec->type->ty == Tclass && ((TypeClass *)cldec->type)->sym != cldec) |
| { |
| // https://issues.dlang.org/show_bug.cgi?id=17492 |
| ClassDeclaration *cd = ((TypeClass *)cldec->type)->sym; |
| cldec->error("already exists at %s. Perhaps in another function with the same name?", cd->loc.toChars()); |
| } |
| |
| if (global.errors != errors) |
| { |
| // The type is no good. |
| cldec->type = Type::terror; |
| cldec->errors = true; |
| if (cldec->deferred) |
| cldec->deferred->errors = true; |
| } |
| |
| // Verify fields of a synchronized class are not public |
| if (cldec->storage_class & STCsynchronized) |
| { |
| for (size_t i = 0; i < cldec->fields.length; i++) |
| { |
| VarDeclaration *vd = cldec->fields[i]; |
| if (!vd->isThisDeclaration() && |
| !vd->prot().isMoreRestrictiveThan(Prot(Prot::public_))) |
| { |
| vd->error("Field members of a synchronized class cannot be %s", |
| protectionToChars(vd->prot().kind)); |
| } |
| } |
| } |
| |
| if (cldec->deferred && !global.gag) |
| { |
| semantic2(cldec->deferred, sc); |
| semantic3(cldec->deferred, sc); |
| } |
| //printf("-ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", cldec->toChars(), cldec->type, sizeok, cldec); |
| } |
| |
| void visit(InterfaceDeclaration *idec) |
| { |
| //printf("InterfaceDeclaration::semantic(%s), type = %p\n", idec->toChars(), idec->type); |
| if (idec->semanticRun >= PASSsemanticdone) |
| return; |
| unsigned errors = global.errors; |
| |
| //printf("+InterfaceDeclaration.semantic(%s), type = %p\n", idec->toChars(), idec->type); |
| |
| Scope *scx = NULL; |
| if (idec->_scope) |
| { |
| sc = idec->_scope; |
| scx = idec->_scope; // save so we don't make redundant copies |
| idec->_scope = NULL; |
| } |
| |
| if (!idec->parent) |
| { |
| assert(sc->parent && sc->func); |
| idec->parent = sc->parent; |
| } |
| assert(idec->parent && !idec->isAnonymous()); |
| |
| if (idec->errors) |
| idec->type = Type::terror; |
| idec->type = typeSemantic(idec->type, idec->loc, sc); |
| |
| if (idec->type->ty == Tclass && ((TypeClass *)idec->type)->sym != idec) |
| { |
| TemplateInstance *ti = ((TypeClass *)idec->type)->sym->isInstantiated(); |
| if (ti && isError(ti)) |
| ((TypeClass *)idec->type)->sym = idec; |
| } |
| |
| // Ungag errors when not speculative |
| Ungag ungag = idec->ungagSpeculative(); |
| |
| if (idec->semanticRun == PASSinit) |
| { |
| idec->protection = sc->protection; |
| |
| idec->storage_class |= sc->stc; |
| if (idec->storage_class & STCdeprecated) |
| idec->isdeprecated = true; |
| |
| idec->userAttribDecl = sc->userAttribDecl; |
| } |
| else if (idec->symtab) |
| { |
| if (idec->sizeok == SIZEOKdone || !scx) |
| { |
| idec->semanticRun = PASSsemanticdone; |
| return; |
| } |
| } |
| idec->semanticRun = PASSsemantic; |
| |
| if (idec->baseok < BASEOKdone) |
| { |
| idec->baseok = BASEOKin; |
| |
| // Expand any tuples in baseclasses[] |
| for (size_t i = 0; i < idec->baseclasses->length; ) |
| { |
| BaseClass *b = (*idec->baseclasses)[i]; |
| b->type = resolveBase(idec, sc, scx, b->type); |
| |
| Type *tb = b->type->toBasetype(); |
| if (tb->ty == Ttuple) |
| { |
| TypeTuple *tup = (TypeTuple *)tb; |
| idec->baseclasses->remove(i); |
| size_t dim = Parameter::dim(tup->arguments); |
| for (size_t j = 0; j < dim; j++) |
| { |
| Parameter *arg = Parameter::getNth(tup->arguments, j); |
| b = new BaseClass(arg->type); |
| idec->baseclasses->insert(i + j, b); |
| } |
| } |
| else |
| i++; |
| } |
| |
| if (idec->baseok >= BASEOKdone) |
| { |
| //printf("%s already semantic analyzed, semanticRun = %d\n", idec->toChars(), idec->semanticRun); |
| if (idec->semanticRun >= PASSsemanticdone) |
| return; |
| goto Lancestorsdone; |
| } |
| |
| if (!idec->baseclasses->length && sc->linkage == LINKcpp) |
| idec->classKind = ClassKind::cpp; |
| if (sc->linkage == LINKobjc) |
| objc()->setObjc(idec); |
| |
| // Check for errors, handle forward references |
| for (size_t i = 0; i < idec->baseclasses->length; ) |
| { |
| BaseClass *b = (*idec->baseclasses)[i]; |
| Type *tb = b->type->toBasetype(); |
| TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; |
| if (!tc || !tc->sym->isInterfaceDeclaration()) |
| { |
| if (b->type != Type::terror) |
| idec->error("base type must be interface, not %s", b->type->toChars()); |
| idec->baseclasses->remove(i); |
| continue; |
| } |
| |
| // Check for duplicate interfaces |
| for (size_t j = 0; j < i; j++) |
| { |
| BaseClass *b2 = (*idec->baseclasses)[j]; |
| if (b2->sym == tc->sym) |
| { |
| idec->error("inherits from duplicate interface %s", b2->sym->toChars()); |
| idec->baseclasses->remove(i); |
| continue; |
| } |
| } |
| |
| if (tc->sym == idec || idec->isBaseOf2(tc->sym)) |
| { |
| idec->error("circular inheritance of interface"); |
| idec->baseclasses->remove(i); |
| continue; |
| } |
| |
| if (tc->sym->isDeprecated()) |
| { |
| if (!idec->isDeprecated()) |
| { |
| // Deriving from deprecated class makes this one deprecated too |
| idec->isdeprecated = true; |
| |
| tc->checkDeprecated(idec->loc, sc); |
| } |
| } |
| |
| b->sym = tc->sym; |
| |
| if (tc->sym->baseok < BASEOKdone) |
| resolveBase(idec, sc, scx, tc->sym); // Try to resolve forward reference |
| if (tc->sym->baseok < BASEOKdone) |
| { |
| //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); |
| if (tc->sym->_scope) |
| Module::addDeferredSemantic(tc->sym); |
| idec->baseok = BASEOKnone; |
| } |
| i++; |
| } |
| if (idec->baseok == BASEOKnone) |
| { |
| // Forward referencee of one or more bases, try again later |
| idec->_scope = scx ? scx : sc->copy(); |
| idec->_scope->setNoFree(); |
| Module::addDeferredSemantic(idec); |
| return; |
| } |
| idec->baseok = BASEOKdone; |
| |
| idec->interfaces.length = idec->baseclasses->length; |
| idec->interfaces.ptr = idec->baseclasses->tdata(); |
| |
| for (size_t i = 0; i < idec->interfaces.length; i++) |
| { |
| BaseClass *b = idec->interfaces.ptr[i]; |
| // If this is an interface, and it derives from a COM interface, |
| // then this is a COM interface too. |
| if (b->sym->isCOMinterface()) |
| idec->com = true; |
| if (b->sym->isCPPinterface()) |
| idec->classKind = ClassKind::cpp; |
| } |
| |
| interfaceSemantic(idec); |
| } |
| Lancestorsdone: |
| |
| if (!idec->members) // if opaque declaration |
| { |
| idec->semanticRun = PASSsemanticdone; |
| return; |
| } |
| if (!idec->symtab) |
| idec->symtab = new DsymbolTable(); |
| |
| for (size_t i = 0; i < idec->baseclasses->length; i++) |
| { |
| BaseClass *b = (*idec->baseclasses)[i]; |
| Type *tb = b->type->toBasetype(); |
| assert(tb->ty == Tclass); |
| TypeClass *tc = (TypeClass *)tb; |
| |
| if (tc->sym->semanticRun < PASSsemanticdone) |
| { |
| // Forward referencee of one or more bases, try again later |
| idec->_scope = scx ? scx : sc->copy(); |
| idec->_scope->setNoFree(); |
| if (tc->sym->_scope) |
| Module::addDeferredSemantic(tc->sym); |
| Module::addDeferredSemantic(idec); |
| return; |
| } |
| } |
| |
| if (idec->baseok == BASEOKdone) |
| { |
| idec->baseok = BASEOKsemanticdone; |
| |
| // initialize vtbl |
| if (idec->vtblOffset()) |
| idec->vtbl.push(idec); // leave room at vtbl[0] for classinfo |
| |
| // Cat together the vtbl[]'s from base cldec->interfaces |
| for (size_t i = 0; i < idec->interfaces.length; i++) |
| { |
| BaseClass *b = idec->interfaces.ptr[i]; |
| |
| // Skip if b has already appeared |
| for (size_t k = 0; k < i; k++) |
| { |
| if (b == idec->interfaces.ptr[k]) |
| goto Lcontinue; |
| } |
| |
| // Copy vtbl[] from base class |
| if (b->sym->vtblOffset()) |
| { |
| size_t d = b->sym->vtbl.length; |
| if (d > 1) |
| { |
| idec->vtbl.reserve(d - 1); |
| for (size_t j = 1; j < d; j++) |
| idec->vtbl.push(b->sym->vtbl[j]); |
| } |
| } |
| else |
| { |
| idec->vtbl.append(&b->sym->vtbl); |
| } |
| |
| Lcontinue: |
| ; |
| } |
| } |
| |
| for (size_t i = 0; i < idec->members->length; i++) |
| { |
| Dsymbol *s = (*idec->members)[i]; |
| s->addMember(sc, idec); |
| } |
| |
| Scope *sc2 = idec->newScope(sc); |
| |
| /* Set scope so if there are forward references, we still might be able to |
| * resolve individual members like enums. |
| */ |
| for (size_t i = 0; i < idec->members->length; i++) |
| { |
| Dsymbol *s = (*idec->members)[i]; |
| //printf("setScope %s %s\n", s->kind(), s->toChars()); |
| s->setScope(sc2); |
| } |
| |
| for (size_t i = 0; i < idec->members->length; i++) |
| { |
| Dsymbol *s = (*idec->members)[i]; |
| s->importAll(sc2); |
| } |
| |
| for (size_t i = 0; i < idec->members->length; i++) |
| { |
| Dsymbol *s = (*idec->members)[i]; |
| dsymbolSemantic(s, sc2); |
| } |
| |
| Module::dprogress++; |
| idec->semanticRun = PASSsemanticdone; |
| //printf("-InterfaceDeclaration.semantic(%s), type = %p\n", idec->toChars(), idec->type); |
| //members->print(); |
| |
| sc2->pop(); |
| |
| if (global.errors != errors) |
| { |
| // The type is no good. |
| idec->type = Type::terror; |
| } |
| |
| assert(idec->type->ty != Tclass || ((TypeClass *)idec->type)->sym == idec); |
| } |
| }; |
| |
| /****************************************************** |
| * Do template instance semantic for isAliasSeq templates. |
| * This is a greatly simplified version of TemplateInstance::semantic(). |
| */ |
| static void aliasSeqInstanceSemantic(TemplateInstance *tempinst, Scope *sc, TemplateDeclaration *tempdecl) |
| { |
| //printf("[%s] aliasSeqInstanceSemantic('%s')\n", tempinst->loc.toChars(), tempinst->toChars()); |
| Scope *paramscope = sc->push(); |
| paramscope->stc = 0; |
| paramscope->protection = Prot(Prot::public_); |
| |
| TemplateTupleParameter *ttp = (*tempdecl->parameters)[0]->isTemplateTupleParameter(); |
| Tuple *va = isTuple(tempinst->tdtypes[0]); |
| Declaration *d = new TupleDeclaration(tempinst->loc, ttp->ident, &va->objects); |
| d->storage_class |= STCtemplateparameter; |
| dsymbolSemantic(d, sc); |
| |
| paramscope->pop(); |
| |
| tempinst->aliasdecl = d; |
| |
| tempinst->semanticRun = PASSsemanticdone; |
| } |
| |
| /****************************************************** |
| * Do template instance semantic for isAlias templates. |
| * This is a greatly simplified version of TemplateInstance::semantic(). |
| */ |
| static void aliasInstanceSemantic(TemplateInstance *tempinst, Scope *sc, TemplateDeclaration *tempdecl) |
| { |
| //printf("[%s] aliasInstanceSemantic('%s')\n", tempinst->loc.toChars(), tempinst->toChars()); |
| Scope *paramscope = sc->push(); |
| paramscope->stc = 0; |
| paramscope->protection = Prot(Prot::public_); |
| |
| TemplateTypeParameter *ttp = (*tempdecl->parameters)[0]->isTemplateTypeParameter(); |
| Type *ta = isType(tempinst->tdtypes[0]); |
| AliasDeclaration *ad = tempdecl->onemember->isAliasDeclaration(); |
| |
| // Note: qualifiers can be in both 'ad.type.mod' and 'ad.storage_class' |
| Declaration *d = new AliasDeclaration(tempinst->loc, ttp->ident, ta->addMod(ad->type->mod)); |
| d->storage_class |= STCtemplateparameter | ad->storage_class; |
| dsymbolSemantic(d, sc); |
| |
| paramscope->pop(); |
| |
| tempinst->aliasdecl = d; |
| |
| tempinst->semanticRun = PASSsemanticdone; |
| } |
| |
| void templateInstanceSemantic(TemplateInstance *tempinst, Scope *sc, Expressions *fargs) |
| { |
| //printf("[%s] TemplateInstance::semantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst->loc.toChars(), tempinst->toChars(), tempinst, global.gag, sc); |
| if (tempinst->inst) // if semantic() was already run |
| { |
| return; |
| } |
| if (tempinst->semanticRun != PASSinit) |
| { |
| Ungag ungag(global.gag); |
| if (!tempinst->gagged) |
| global.gag = 0; |
| tempinst->error(tempinst->loc, "recursive template expansion"); |
| if (tempinst->gagged) |
| tempinst->semanticRun = PASSinit; |
| else |
| tempinst->inst = tempinst; |
| tempinst->errors = true; |
| return; |
| } |
| |
| // Get the enclosing template instance from the scope tinst |
| tempinst->tinst = sc->tinst; |
| |
| // Get the instantiating module from the scope minst |
| tempinst->minst = sc->minst; |
| // Bugzilla 10920: If the enclosing function is non-root symbol, |
| // this instance should be speculative. |
| if (!tempinst->tinst && sc->func && sc->func->inNonRoot()) |
| { |
| tempinst->minst = NULL; |
| } |
| |
| tempinst->gagged = (global.gag > 0); |
| |
| tempinst->semanticRun = PASSsemantic; |
| |
| /* Find template declaration first, |
| * then run semantic on each argument (place results in tiargs[]), |
| * last find most specialized template from overload list/set. |
| */ |
| if (!tempinst->findTempDecl(sc, NULL) || |
| !tempinst->semanticTiargs(sc) || |
| !tempinst->findBestMatch(sc, fargs)) |
| { |
| Lerror: |
| if (tempinst->gagged) |
| { |
| // Bugzilla 13220: Rollback status for later semantic re-running. |
| tempinst->semanticRun = PASSinit; |
| } |
| else |
| tempinst->inst = tempinst; |
| tempinst->errors = true; |
| return; |
| } |
| TemplateDeclaration *tempdecl = tempinst->tempdecl->isTemplateDeclaration(); |
| assert(tempdecl); |
| |
| // If tempdecl is a mixin, disallow it |
| if (tempdecl->ismixin) |
| { |
| tempinst->error("mixin templates are not regular templates"); |
| goto Lerror; |
| } |
| |
| tempinst->hasNestedArgs(tempinst->tiargs, tempdecl->isstatic); |
| if (tempinst->errors) |
| goto Lerror; |
| |
| /* Greatly simplified semantic processing for AliasSeq templates |
| */ |
| if (tempdecl->isTrivialAliasSeq) |
| { |
| tempinst->inst = tempinst; |
| return aliasSeqInstanceSemantic(tempinst, sc, tempdecl); |
| } |
| /* Greatly simplified semantic processing for Alias templates |
| */ |
| else if (tempdecl->isTrivialAlias) |
| { |
| tempinst->inst = tempinst; |
| return aliasInstanceSemantic(tempinst, sc, tempdecl); |
| } |
| |
| /* See if there is an existing TemplateInstantiation that already |
| * implements the typeargs. If so, just refer to that one instead. |
| */ |
| tempinst->inst = tempdecl->findExistingInstance(tempinst, fargs); |
| TemplateInstance *errinst = NULL; |
| if (!tempinst->inst) |
| { |
| // So, we need to implement 'this' instance. |
| } |
| else if (tempinst->inst->gagged && !tempinst->gagged && tempinst->inst->errors) |
| { |
| // If the first instantiation had failed, re-run semantic, |
| // so that error messages are shown. |
| errinst = tempinst->inst; |
| } |
| else |
| { |
| // It's a match |
| tempinst->parent = tempinst->inst->parent; |
| tempinst->errors = tempinst->inst->errors; |
| |
| // If both this and the previous instantiation were gagged, |
| // use the number of errors that happened last time. |
| global.errors += tempinst->errors; |
| global.gaggedErrors += tempinst->errors; |
| |
| // If the first instantiation was gagged, but this is not: |
| if (tempinst->inst->gagged) |
| { |
| // It had succeeded, mark it is a non-gagged instantiation, |
| // and reuse it. |
| tempinst->inst->gagged = tempinst->gagged; |
| } |
| |
| tempinst->tnext = tempinst->inst->tnext; |
| tempinst->inst->tnext = tempinst; |
| |
| /* A module can have explicit template instance and its alias |
| * in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`). |
| * If the first instantiation 'inst' had happened in non-root module, |
| * compiler can assume that its instantiated code would be included |
| * in the separately compiled obj/lib file (e.g. phobos.lib). |
| * |
| * However, if 'this' second instantiation happened in root module, |
| * compiler might need to invoke its codegen (Bugzilla 2500 & 2644). |
| * But whole import graph is not determined until all semantic pass finished, |
| * so 'inst' should conservatively finish the semantic3 pass for the codegen. |
| */ |
| if (tempinst->minst && tempinst->minst->isRoot() && !(tempinst->inst->minst && tempinst->inst->minst->isRoot())) |
| { |
| /* Swap the position of 'inst' and 'this' in the instantiation graph. |
| * Then, the primary instance `inst` will be changed to a root instance, |
| * along with all members of `inst` having their scopes updated. |
| * |
| * Before: |
| * non-root -> A!() -> B!()[inst] -> C!() { members[non-root] } |
| * | |
| * root -> D!() -> B!()[this] |
| * |
| * After: |
| * non-root -> A!() -> B!()[this] |
| * | |
| * root -> D!() -> B!()[inst] -> C!() { members[root] } |
| */ |
| Module *mi = tempinst->minst; |
| TemplateInstance *ti = tempinst->tinst; |
| tempinst->minst = tempinst->inst->minst; |
| tempinst->tinst = tempinst->inst->tinst; |
| tempinst->inst->minst = mi; |
| tempinst->inst->tinst = ti; |
| |
| /* https://issues.dlang.org/show_bug.cgi?id=21299 |
| `minst` has been updated on the primary instance `inst` so it is |
| now coming from a root module, however all Dsymbol `inst.members` |
| of the instance still have their `_scope.minst` pointing at the |
| original non-root module. We must now propagate `minst` to all |
| members so that forward referenced dependencies that get |
| instantiated will also be appended to the root module, otherwise |
| there will be undefined references at link-time. */ |
| class InstMemberWalker : public Visitor |
| { |
| public: |
| TemplateInstance *inst; |
| |
| InstMemberWalker(TemplateInstance *inst) |
| : inst(inst) { } |
| |
| void visit(Dsymbol *d) |
| { |
| if (d->_scope) |
| d->_scope->minst = inst->minst; |
| } |
| |
| void visit(ScopeDsymbol *sds) |
| { |
| if (!sds->members) |
| return; |
| for (size_t i = 0; i < sds->members->length; i++) |
| { |
| Dsymbol *s = (*sds->members)[i]; |
| s->accept(this); |
| } |
| visit((Dsymbol *)sds); |
| } |
| |
| void visit(AttribDeclaration *ad) |
| { |
| Dsymbols *d = ad->include(NULL); |
| if (!d) |
| return; |
| for (size_t i = 0; i < d->length; i++) |
| { |
| Dsymbol *s = (*d)[i]; |
| s->accept(this); |
| } |
| visit((Dsymbol *)ad); |
| } |
| |
| void visit(ConditionalDeclaration *cd) |
| { |
| if (cd->condition->inc) |
| visit((AttribDeclaration *)cd); |
| else |
| visit((Dsymbol *)cd); |
| } |
| }; |
| InstMemberWalker v(tempinst->inst); |
| tempinst->inst->accept(&v); |
| |
| if (tempinst->minst) // if inst was not speculative |
| { |
| /* Add 'inst' once again to the root module members[], then the |
| * instance members will get codegen chances. |
| */ |
| tempinst->inst->appendToModuleMember(); |
| } |
| } |
| |
| return; |
| } |
| unsigned errorsave = global.errors; |
| |
| tempinst->inst = tempinst; |
| tempinst->parent = tempinst->enclosing ? tempinst->enclosing : tempdecl->parent; |
| //printf("parent = '%s'\n", tempinst->parent->kind()); |
| |
| TemplateInstance *tempdecl_instance_idx = tempdecl->addInstance(tempinst); |
| |
| //getIdent(); |
| |
| // Store the place we added it to in target_symbol_list(_idx) so we can |
| // remove it later if we encounter an error. |
| Dsymbols *target_symbol_list = tempinst->appendToModuleMember(); |
| size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list->length - 1 : 0; |
| |
| // Copy the syntax trees from the TemplateDeclaration |
| tempinst->members = Dsymbol::arraySyntaxCopy(tempdecl->members); |
| |
| // resolve TemplateThisParameter |
| for (size_t i = 0; i < tempdecl->parameters->length; i++) |
| { |
| if ((*tempdecl->parameters)[i]->isTemplateThisParameter() == NULL) |
| continue; |
| Type *t = isType((*tempinst->tiargs)[i]); |
| assert(t); |
| if (StorageClass stc = ModToStc(t->mod)) |
| { |
| //printf("t = %s, stc = x%llx\n", t->toChars(), stc); |
| Dsymbols *s = new Dsymbols(); |
| s->push(new StorageClassDeclaration(stc, tempinst->members)); |
| tempinst->members = s; |
| } |
| break; |
| } |
| |
| // Create our own scope for the template parameters |
| Scope *scope = tempdecl->_scope; |
| if (tempdecl->semanticRun == PASSinit) |
| { |
| tempinst->error("template instantiation %s forward references template declaration %s", tempinst->toChars(), tempdecl->toChars()); |
| return; |
| } |
| |
| tempinst->argsym = new ScopeDsymbol(); |
| tempinst->argsym->parent = scope->parent; |
| scope = scope->push(tempinst->argsym); |
| scope->tinst = tempinst; |
| scope->minst = tempinst->minst; |
| //scope->stc = 0; |
| |
| // Declare each template parameter as an alias for the argument type |
| Scope *paramscope = scope->push(); |
| paramscope->stc = 0; |
| paramscope->protection = Prot(Prot::public_); // Bugzilla 14169: template parameters should be public |
| tempinst->declareParameters(paramscope); |
| paramscope->pop(); |
| |
| // Add members of template instance to template instance symbol table |
| // tempinst->parent = scope->scopesym; |
| tempinst->symtab = new DsymbolTable(); |
| for (size_t i = 0; i < tempinst->members->length; i++) |
| { |
| Dsymbol *s = (*tempinst->members)[i]; |
| s->addMember(scope, tempinst); |
| } |
| |
| /* See if there is only one member of template instance, and that |
| * member has the same name as the template instance. |
| * If so, this template instance becomes an alias for that member. |
| */ |
| //printf("members->length = %d\n", tempinst->members->length); |
| if (tempinst->members->length) |
| { |
| Dsymbol *s; |
| if (Dsymbol::oneMembers(tempinst->members, &s, tempdecl->ident) && s) |
| { |
| //printf("tempdecl->ident = %s, s = '%s'\n", tempdecl->ident->toChars(), s->kind(), s->toPrettyChars()); |
| //printf("setting aliasdecl\n"); |
| tempinst->aliasdecl = s; |
| } |
| } |
| |
| /* If function template declaration |
| */ |
| if (fargs && tempinst->aliasdecl) |
| { |
| FuncDeclaration *fd = tempinst->aliasdecl->isFuncDeclaration(); |
| if (fd) |
| { |
| /* Transmit fargs to type so that TypeFunction::semantic() can |
| * resolve any "auto ref" storage classes. |
| */ |
| TypeFunction *tf = (TypeFunction *)fd->type; |
| if (tf && tf->ty == Tfunction) |
| tf->fargs = fargs; |
| } |
| } |
| |
| // Do semantic() analysis on template instance members |
| Scope *sc2; |
| sc2 = scope->push(tempinst); |
| //printf("enclosing = %d, sc->parent = %s\n", tempinst->enclosing, sc->parent->toChars()); |
| sc2->parent = tempinst; |
| sc2->tinst = tempinst; |
| sc2->minst = tempinst->minst; |
| |
| tempinst->tryExpandMembers(sc2); |
| |
| tempinst->semanticRun = PASSsemanticdone; |
| |
| /* ConditionalDeclaration may introduce eponymous declaration, |
| * so we should find it once again after semantic. |
| */ |
| if (tempinst->members->length) |
| { |
| Dsymbol *s; |
| if (Dsymbol::oneMembers(tempinst->members, &s, tempdecl->ident) && s) |
| { |
| if (!tempinst->aliasdecl || tempinst->aliasdecl != s) |
| { |
| //printf("tempdecl->ident = %s, s = '%s'\n", tempdecl->ident->toChars(), s->kind(), s->toPrettyChars()); |
| //printf("setting aliasdecl 2\n"); |
| tempinst->aliasdecl = s; |
| } |
| } |
| } |
| |
| if (global.errors != errorsave) |
| goto Laftersemantic; |
| |
| /* If any of the instantiation members didn't get semantic() run |
| * on them due to forward references, we cannot run semantic2() |
| * or semantic3() yet. |
| */ |
| { |
| bool found_deferred_ad = false; |
| for (size_t i = 0; i < Module::deferred.length; i++) |
| { |
| Dsymbol *sd = Module::deferred[i]; |
| AggregateDeclaration *ad = sd->isAggregateDeclaration(); |
| if (ad && ad->parent && ad->parent->isTemplateInstance()) |
| { |
| //printf("deferred template aggregate: %s %s\n", |
| // sd->parent->toChars(), sd->toChars()); |
| found_deferred_ad = true; |
| if (ad->parent == tempinst) |
| { |
| ad->deferred = tempinst; |
| break; |
| } |
| } |
| } |
| if (found_deferred_ad || Module::deferred.length) |
| goto Laftersemantic; |
| } |
| |
| /* The problem is when to parse the initializer for a variable. |
| * Perhaps VarDeclaration::semantic() should do it like it does |
| * for initializers inside a function. |
| */ |
| //if (sc->parent->isFuncDeclaration()) |
| { |
| /* BUG 782: this has problems if the classes this depends on |
| * are forward referenced. Find a way to defer semantic() |
| * on this template. |
| */ |
| semantic2(tempinst, sc2); |
| } |
| if (global.errors != errorsave) |
| goto Laftersemantic; |
| |
| if ((sc->func || (sc->flags & SCOPEfullinst)) && !tempinst->tinst) |
| { |
| /* If a template is instantiated inside function, the whole instantiation |
| * should be done at that position. But, immediate running semantic3 of |
| * dependent templates may cause unresolved forward reference (Bugzilla 9050). |
| * To avoid the issue, don't run semantic3 until semantic and semantic2 done. |
| */ |
| TemplateInstances deferred; |
| tempinst->deferred = &deferred; |
| |
| //printf("Run semantic3 on %s\n", tempinst->toChars()); |
| tempinst->trySemantic3(sc2); |
| |
| for (size_t i = 0; i < deferred.length; i++) |
| { |
| //printf("+ run deferred semantic3 on %s\n", deferred[i]->toChars()); |
| semantic3(deferred[i], NULL); |
| } |
| |
| tempinst->deferred = NULL; |
| } |
| else if (tempinst->tinst) |
| { |
| bool doSemantic3 = false; |
| if (sc->func && tempinst->aliasdecl && tempinst->aliasdecl->toAlias()->isFuncDeclaration()) |
| { |
| /* Template function instantiation should run semantic3 immediately |
| * for attribute inference. |
| */ |
| tempinst->trySemantic3(sc2); |
| } |
| else if (sc->func) |
| { |
| /* A lambda function in template arguments might capture the |
| * instantiated scope context. For the correct context inference, |
| * all instantiated functions should run the semantic3 immediately. |
| * See also compilable/test14973.d |
| */ |
| for (size_t i = 0; i < tempinst->tdtypes.length; i++) |
| { |
| RootObject *oarg = tempinst->tdtypes[i]; |
| Dsymbol *s = getDsymbol(oarg); |
| if (!s) |
| continue; |
| |
| if (TemplateDeclaration *td = s->isTemplateDeclaration()) |
| { |
| if (!td->literal) |
| continue; |
| assert(td->members && td->members->length == 1); |
| s = (*td->members)[0]; |
| } |
| if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration()) |
| { |
| if (fld->tok == TOKreserved) |
| { |
| doSemantic3 = true; |
| break; |
| } |
| } |
| } |
| //printf("[%s] %s doSemantic3 = %d\n", tempinst->loc.toChars(), tempinst->toChars(), doSemantic3); |
| } |
| if (doSemantic3) |
| tempinst->trySemantic3(sc2); |
| |
| TemplateInstance *ti = tempinst->tinst; |
| int nest = 0; |
| while (ti && !ti->deferred && ti->tinst) |
| { |
| ti = ti->tinst; |
| if (++nest > global.recursionLimit) |
| { |
| global.gag = 0; // ensure error message gets printed |
| tempinst->error("recursive expansion"); |
| fatal(); |
| } |
| } |
| if (ti && ti->deferred) |
| { |
| //printf("deferred semantic3 of %p %s, ti = %s, ti->deferred = %p\n", tempinst, tempinst->toChars(), ti->toChars()); |
| for (size_t i = 0; ; i++) |
| { |
| if (i == ti->deferred->length) |
| { |
| ti->deferred->push(tempinst); |
| break; |
| } |
| if ((*ti->deferred)[i] == tempinst) |
| break; |
| } |
| } |
| } |
| |
| if (tempinst->aliasdecl) |
| { |
| /* Bugzilla 13816: AliasDeclaration tries to resolve forward reference |
| * twice (See inuse check in AliasDeclaration::toAlias()). It's |
| * necessary to resolve mutual references of instantiated symbols, but |
| * it will left a true recursive alias in tuple declaration - an |
| * AliasDeclaration A refers TupleDeclaration B, and B contains A |
| * in its elements. To correctly make it an error, we strictly need to |
| * resolve the alias of eponymous member. |
| */ |
| tempinst->aliasdecl = tempinst->aliasdecl->toAlias2(); |
| } |
| |
| Laftersemantic: |
| sc2->pop(); |
| |
| scope->pop(); |
| |
| // Give additional context info if error occurred during instantiation |
| if (global.errors != errorsave) |
| { |
| if (!tempinst->errors) |
| { |
| if (!tempdecl->literal) |
| tempinst->error(tempinst->loc, "error instantiating"); |
| if (tempinst->tinst) |
| tempinst->tinst->printInstantiationTrace(); |
| } |
| tempinst->errors = true; |
| if (tempinst->gagged) |
| { |
| // Errors are gagged, so remove the template instance from the |
| // instance/symbol lists we added it to and reset our state to |
| // finish clean and so we can try to instantiate it again later |
| // (see bugzilla 4302 and 6602). |
| tempdecl->removeInstance(tempdecl_instance_idx); |
| if (target_symbol_list) |
| { |
| // Because we added 'this' in the last position above, we |
| // should be able to remove it without messing other indices up. |
| assert((*target_symbol_list)[target_symbol_list_idx] == tempinst); |
| target_symbol_list->remove(target_symbol_list_idx); |
| tempinst->memberOf = NULL; // no longer a member |
| } |
| tempinst->semanticRun = PASSinit; |
| tempinst->inst = NULL; |
| tempinst->symtab = NULL; |
| } |
| } |
| else if (errinst) |
| { |
| /* Bugzilla 14541: If the previous gagged instance had failed by |
| * circular references, currrent "error reproduction instantiation" |
| * might succeed, because of the difference of instantiated context. |
| * On such case, the cached error instance needs to be overridden by the |
| * succeeded instance. |
| */ |
| //printf("replaceInstance()\n"); |
| TemplateInstances *tinstances = (TemplateInstances *)dmd_aaGetRvalue((AA *)tempdecl->instances, (void *)tempinst->hash); |
| assert(tinstances); |
| for (size_t i = 0; i < tinstances->length; i++) |
| { |
| TemplateInstance *ti = (*tinstances)[i]; |
| if (ti == errinst) |
| { |
| (*tinstances)[i] = tempinst; // override |
| break; |
| } |
| } |
| } |
| } |
| |
| // function used to perform semantic on AliasDeclaration |
| void aliasSemantic(AliasDeclaration *ds, Scope *sc) |
| { |
| //printf("AliasDeclaration::semantic() %s\n", ds->toChars()); |
| |
| // as AliasDeclaration::semantic, in case we're called first. |
| // see https://issues.dlang.org/show_bug.cgi?id=21001 |
| ds->storage_class |= sc->stc & STCdeprecated; |
| ds->protection = sc->protection; |
| ds->userAttribDecl = sc->userAttribDecl; |
| |
| // TypeTraits needs to know if it's located in an AliasDeclaration |
| const unsigned oldflags = sc->flags; |
| sc->flags |= SCOPEalias; |
| |
| if (ds->aliassym) |
| { |
| FuncDeclaration *fd = ds->aliassym->isFuncLiteralDeclaration(); |
| TemplateDeclaration *td = ds->aliassym->isTemplateDeclaration(); |
| if (fd || (td && td->literal)) |
| { |
| if (fd && fd->semanticRun >= PASSsemanticdone) |
| { |
| sc->flags = oldflags; |
| return; |
| } |
| |
| Expression *e = new FuncExp(ds->loc, ds->aliassym); |
| e = expressionSemantic(e, sc); |
| if (e->op == TOKfunction) |
| { |
| FuncExp *fe = (FuncExp *)e; |
| ds->aliassym = fe->td ? (Dsymbol *)fe->td : fe->fd; |
| } |
| else |
| { |
| ds->aliassym = NULL; |
| ds->type = Type::terror; |
| } |
| sc->flags = oldflags; |
| return; |
| } |
| |
| if (ds->aliassym->isTemplateInstance()) |
| dsymbolSemantic(ds->aliassym, sc); |
| sc->flags = oldflags; |
| return; |
| } |
| ds->inuse = 1; |
| |
| // Given: |
| // alias foo.bar.abc def; |
| // it is not knowable from the syntax whether this is an alias |
| // for a type or an alias for a symbol. It is up to the semantic() |
| // pass to distinguish. |
| // If it is a type, then type is set and getType() will return that |
| // type. If it is a symbol, then aliassym is set and type is NULL - |
| // toAlias() will return aliasssym. |
| |
| unsigned int errors = global.errors; |
| Type *oldtype = ds->type; |
| |
| // Ungag errors when not instantiated DeclDefs scope alias |
| Ungag ungag(global.gag); |
| //printf("%s parent = %s, gag = %d, instantiated = %d\n", ds->toChars(), ds->parent, global.gag, ds->isInstantiated()); |
| if (ds->parent && global.gag && !ds->isInstantiated() && !ds->toParent2()->isFuncDeclaration()) |
| { |
| //printf("%s type = %s\n", ds->toPrettyChars(), ds->type->toChars()); |
| global.gag = 0; |
| } |
| |
| /* This section is needed because Type::resolve() will: |
| * const x = 3; |
| * alias y = x; |
| * try to convert identifier x to 3. |
| */ |
| Dsymbol *s = ds->type->toDsymbol(sc); |
| if (errors != global.errors) |
| { |
| s = NULL; |
| ds->type = Type::terror; |
| } |
| if (s && s == ds) |
| { |
| ds->error("cannot resolve"); |
| s = NULL; |
| ds->type = Type::terror; |
| } |
| if (!s || !s->isEnumMember()) |
| { |
| Type *t; |
| Expression *e; |
| Scope *sc2 = sc; |
| if (ds->storage_class & (STCref | STCnothrow | STCnogc | STCpure | STCdisable)) |
| { |
| // For 'ref' to be attached to function types, and picked |
| // up by Type::resolve(), it has to go into sc. |
| sc2 = sc->push(); |
| sc2->stc |= ds->storage_class & (STCref | STCnothrow | STCnogc | STCpure | STCshared | STCdisable); |
| } |
| ds->type = ds->type->addSTC(ds->storage_class); |
| ds->type->resolve(ds->loc, sc2, &e, &t, &s); |
| if (sc2 != sc) |
| sc2->pop(); |
| |
| if (e) // Try to convert Expression to Dsymbol |
| { |
| s = getDsymbol(e); |
| if (!s) |
| { |
| if (e->op != TOKerror) |
| ds->error("cannot alias an expression %s", e->toChars()); |
| t = Type::terror; |
| } |
| } |
| ds->type = t; |
| } |
| if (s == ds) |
| { |
| assert(global.errors); |
| ds->type = Type::terror; |
| s = NULL; |
| } |
| if (!s) // it's a type alias |
| { |
| //printf("alias %s resolved to type %s\n", ds->toChars(), ds->type->toChars()); |
| ds->type = typeSemantic(ds->type, ds->loc, sc); |
| ds->aliassym = NULL; |
| } |
| else // it's a symbolic alias |
| { |
| //printf("alias %s resolved to %s %s\n", ds->toChars(), s->kind(), s->toChars()); |
| ds->type = NULL; |
| ds->aliassym = s; |
| } |
| if (global.gag && errors != global.errors) |
| { |
| ds->type = oldtype; |
| ds->aliassym = NULL; |
| } |
| ds->inuse = 0; |
| ds->semanticRun = PASSsemanticdone; |
| |
| if (Dsymbol *sx = ds->overnext) |
| { |
| ds->overnext = NULL; |
| |
| if (!ds->overloadInsert(sx)) |
| ScopeDsymbol::multiplyDefined(Loc(), sx, ds); |
| } |
| sc->flags = oldflags; |
| } |
| |
| |
| /************************************* |
| * Does semantic analysis on the public face of declarations. |
| */ |
| void dsymbolSemantic(Dsymbol *dsym, Scope *sc) |
| { |
| DsymbolSemanticVisitor v(sc); |
| dsym->accept(&v); |
| } |