diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2021-02-09 15:40:06 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2021-02-13 12:50:45 +0100 |
commit | a3b38b7781622babb5ca68c621367770a65012fa (patch) | |
tree | 32ba65cb98047efa6ed8f86b327903ce647c008f /gcc/d/dmd/semantic3.c | |
parent | 0f3a743b688f4845e1798eed9b2e2284e891da11 (diff) | |
download | gcc-a3b38b7781622babb5ca68c621367770a65012fa.zip gcc-a3b38b7781622babb5ca68c621367770a65012fa.tar.gz gcc-a3b38b7781622babb5ca68c621367770a65012fa.tar.bz2 |
d: Merge upstream dmd 7132b3537
Splits out all semantic passes for Dsymbol, Type, and TemplateParameter
nodes into Visitors in separate files, and the copyright years of all
sources have been updated.
Reviewed-on: https://github.com/dlang/dmd/pull/12190
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd 7132b3537.
* Make-lang.in (D_FRONTEND_OBJS): Add d/dsymbolsem.o, d/semantic2.o,
d/semantic3.o, and d/templateparamsem.o.
* d-compiler.cc (Compiler::genCmain): Update calls to semantic
entrypoint functions.
* d-lang.cc (d_parse_file): Likewise.
* typeinfo.cc (make_frontend_typeinfo): Likewise.
Diffstat (limited to 'gcc/d/dmd/semantic3.c')
-rw-r--r-- | gcc/d/dmd/semantic3.c | 1421 |
1 files changed, 1421 insertions, 0 deletions
diff --git a/gcc/d/dmd/semantic3.c b/gcc/d/dmd/semantic3.c new file mode 100644 index 0000000..304eaee --- /dev/null +++ b/gcc/d/dmd/semantic3.c @@ -0,0 +1,1421 @@ + +/* 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 "dsymbol.h" +#include "aggregate.h" +#include "attrib.h" +#include "declaration.h" +#include "errors.h" +#include "id.h" +#include "init.h" +#include "module.h" +#include "nspace.h" +#include "scope.h" +#include "statement.h" +#include "statement_rewrite_walker.h" +#include "target.h" +#include "template.h" +#include "visitor.h" + +bool allowsContractWithoutBody(FuncDeclaration *funcdecl); +int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow); +bool checkReturnEscape(Scope *sc, Expression *e, bool gag); +bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag); +TypeIdentifier *getThrowable(); +char *MODtoChars(MOD mod); +Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads); +void allocFieldinit(Scope *sc, size_t dim); +void freeFieldinit(Scope *sc); + +/* Determine if function should add `return 0;` + */ +static bool addReturn0(FuncDeclaration *funcdecl) +{ + TypeFunction *f = (TypeFunction *)funcdecl->type; + + return f->next->ty == Tvoid && + (funcdecl->isMain() || (global.params.betterC && funcdecl->isCMain())); +} + +/******************************************************** + * Generate Expression to call the invariant. + * Input: + * ad aggregate with the invariant + * vthis variable with 'this' + * Returns: + * void expression that calls the invariant + */ +static Expression *addInvariant(AggregateDeclaration *ad, VarDeclaration *vthis) +{ + Expression *e = NULL; + + // Call invariant directly only if it exists + FuncDeclaration *inv = ad->inv; + ClassDeclaration *cd = ad->isClassDeclaration(); + + while (!inv && cd) + { + cd = cd->baseClass; + if (!cd) + break; + inv = cd->inv; + } + if (inv) + { + #if 1 + // Workaround for bugzilla 13394: For the correct mangling, + // run attribute inference on inv if needed. + inv->functionSemantic(); + #endif + + //e = new DsymbolExp(Loc(), inv); + //e = new CallExp(Loc(), e); + //dsymbolSemantic(e, sc2); + + /* https://issues.dlang.org/show_bug.cgi?id=13113 + * Currently virtual invariant calls completely + * bypass attribute enforcement. + * Change the behavior of pre-invariant call by following it. + */ + e = new ThisExp(Loc()); + e->type = vthis->type; + e = new DotVarExp(Loc(), e, inv, false); + e->type = inv->type; + e = new CallExp(Loc(), e); + e->type = Type::tvoid; + } + return e; +} + +/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO. + */ +class NrvoWalker : public StatementRewriteWalker +{ +public: + FuncDeclaration *fd; + Scope *sc; + + void visit(ReturnStatement *s) + { + // See if all returns are instead to be replaced with a goto returnLabel; + if (fd->returnLabel) + { + /* Rewrite: + * return exp; + * as: + * vresult = exp; goto Lresult; + */ + GotoStatement *gs = new GotoStatement(s->loc, Id::returnLabel); + gs->label = fd->returnLabel; + + Statement *s1 = gs; + if (s->exp) + s1 = new CompoundStatement(s->loc, new ExpStatement(s->loc, s->exp), gs); + + replaceCurrent(s1); + } + } + void visit(TryFinallyStatement *s) + { + DtorExpStatement *des; + if (fd->nrvo_can && + s->finalbody && (des = s->finalbody->isDtorExpStatement()) != NULL && + fd->nrvo_var == des->var) + { + if (!(global.params.useExceptions && ClassDeclaration::throwable)) + { + /* Don't need to call destructor at all, since it is nrvo + */ + replaceCurrent(s->_body); + s->_body->accept(this); + return; + } + + /* Normally local variable dtors are called regardless exceptions. + * But for nrvo_var, its dtor should be called only when exception is thrown. + * + * Rewrite: + * try { s->body; } finally { nrvo_var->edtor; } + * // equivalent with: + * // s->body; scope(exit) nrvo_var->edtor; + * as: + * try { s->body; } catch(Throwable __o) { nrvo_var->edtor; throw __o; } + * // equivalent with: + * // s->body; scope(failure) nrvo_var->edtor; + */ + Statement *sexception = new DtorExpStatement(Loc(), fd->nrvo_var->edtor, fd->nrvo_var); + Identifier *id = Identifier::generateId("__o"); + + Statement *handler = new PeelStatement(sexception); + if (blockExit(sexception, fd, false) & BEfallthru) + { + ThrowStatement *ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id)); + ts->internalThrow = true; + handler = new CompoundStatement(Loc(), handler, ts); + } + + Catches *catches = new Catches(); + Catch *ctch = new Catch(Loc(), getThrowable(), id, handler); + ctch->internalCatch = true; + catchSemantic(ctch, sc); // Run semantic to resolve identifier '__o' + catches->push(ctch); + + Statement *s2 = new TryCatchStatement(Loc(), s->_body, catches); + replaceCurrent(s2); + s2->accept(this); + } + else + StatementRewriteWalker::visit(s); + } +}; + +class Semantic3Visitor : public Visitor +{ +public: + Scope *sc; + + Semantic3Visitor(Scope *sc) + { + this->sc = sc; + } + + void visit(Dsymbol *) + { + // Most Dsymbols have no further semantic analysis needed + } + + void visit(TemplateInstance *tempinst) + { + //if (tempinst->toChars()[0] == 'D') *(char*)0=0; + if (tempinst->semanticRun >= PASSsemantic3) + return; + tempinst->semanticRun = PASSsemantic3; + if (!tempinst->errors && tempinst->members) + { + TemplateDeclaration *tempdecl = tempinst->tempdecl->isTemplateDeclaration(); + assert(tempdecl); + + sc = tempdecl->_scope; + sc = sc->push(tempinst->argsym); + sc = sc->push(tempinst); + sc->tinst = tempinst; + sc->minst = tempinst->minst; + + int needGagging = (tempinst->gagged && !global.gag); + unsigned int olderrors = global.errors; + int oldGaggedErrors = -1; // dead-store to prevent spurious warning + /* If this is a gagged instantiation, gag errors. + * Future optimisation: If the results are actually needed, errors + * would already be gagged, so we don't really need to run semantic + * on the members. + */ + if (needGagging) + oldGaggedErrors = global.startGagging(); + + for (size_t i = 0; i < tempinst->members->length; i++) + { + Dsymbol *s = (*tempinst->members)[i]; + semantic3(s, sc); + if (tempinst->gagged && global.errors != olderrors) + break; + } + + if (global.errors != olderrors) + { + if (!tempinst->errors) + { + if (!tempdecl->literal) + tempinst->error(tempinst->loc, "error instantiating"); + if (tempinst->tinst) + tempinst->tinst->printInstantiationTrace(); + } + tempinst->errors = true; + } + if (needGagging) + global.endGagging(oldGaggedErrors); + + sc = sc->pop(); + sc->pop(); + } + } + + void visit(TemplateMixin *tmix) + { + if (tmix->semanticRun >= PASSsemantic3) + return; + tmix->semanticRun = PASSsemantic3; + if (tmix->members) + { + sc = sc->push(tmix->argsym); + sc = sc->push(tmix); + for (size_t i = 0; i < tmix->members->length; i++) + { + Dsymbol *s = (*tmix->members)[i]; + semantic3(s, sc); + } + sc = sc->pop(); + sc->pop(); + } + } + + void visit(Module *mod) + { + //printf("Module::semantic3('%s'): parent = %p\n", mod->toChars(), mod->parent); + if (mod->semanticRun != PASSsemantic2done) + return; + mod->semanticRun = PASSsemantic3; + + // 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 = Scope::createGlobal(mod); // create root scope + //printf("Module = %p\n", sc.scopesym); + + // Pass 3 semantic routines: do initializers and function bodies + for (size_t i = 0; i < mod->members->length; i++) + { + Dsymbol *s = (*mod->members)[i]; + //printf("Module %s: %s.semantic3()\n", mod->toChars(), s->toChars()); + semantic3(s, sc); + + mod->runDeferredSemantic2(); + } + + if (mod->userAttribDecl) + { + semantic3(mod->userAttribDecl, sc); + } + + sc = sc->pop(); + sc->pop(); + mod->semanticRun = PASSsemantic3done; + } + + void visit(FuncDeclaration *funcdecl) + { + VarDeclaration *_arguments = NULL; + + if (!funcdecl->parent) + { + if (global.errors) + return; + //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", funcdecl->kind(), funcdecl->toChars(), sc); + assert(0); + } + if (funcdecl->errors || isError(funcdecl->parent)) + { + funcdecl->errors = true; + return; + } + //printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", funcdecl->parent->toChars(), funcdecl->toChars(), funcdecl, sc, funcdecl->loc.toChars()); + //fflush(stdout); + //printf("storage class = x%x %x\n", sc->stc, funcdecl->storage_class); + //{ static int x; if (++x == 2) *(char*)0=0; } + //printf("\tlinkage = %d\n", sc->linkage); + + if (funcdecl->ident == Id::assign && !funcdecl->inuse) + { + if (funcdecl->storage_class & STCinference) + { + /* Bugzilla 15044: For generated opAssign function, any errors + * from its body need to be gagged. + */ + unsigned oldErrors = global.startGagging(); + funcdecl->inuse++; + semantic3(funcdecl, sc); + funcdecl->inuse--; + if (global.endGagging(oldErrors)) // if errors happened + { + // Disable generated opAssign, because some members forbid identity assignment. + funcdecl->storage_class |= STCdisable; + funcdecl->fbody = NULL; // remove fbody which contains the error + funcdecl->semantic3Errors = false; + } + return; + } + } + + //printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract)); + if (funcdecl->semanticRun >= PASSsemantic3) + return; + funcdecl->semanticRun = PASSsemantic3; + funcdecl->semantic3Errors = false; + + if (!funcdecl->type || funcdecl->type->ty != Tfunction) + return; + TypeFunction *f = (TypeFunction *)funcdecl->type; + if (!funcdecl->inferRetType && f->next->ty == Terror) + return; + + if (!funcdecl->fbody && funcdecl->inferRetType && !f->next) + { + funcdecl->error("has no function body with return type inference"); + return; + } + + unsigned oldErrors = global.errors; + + if (funcdecl->frequires) + { + for (size_t i = 0; i < funcdecl->foverrides.length; i++) + { + FuncDeclaration *fdv = funcdecl->foverrides[i]; + + if (fdv->fbody && !fdv->frequires) + { + funcdecl->error("cannot have an in contract when overriden function %s does not have an in contract", fdv->toPrettyChars()); + break; + } + } + } + + // Remember whether we need to generate an 'out' contract. + const bool needEnsure = FuncDeclaration::needsFensure(funcdecl); + + if (funcdecl->fbody || funcdecl->frequires || needEnsure) + { + /* Symbol table into which we place parameters and nested functions, + * solely to diagnose name collisions. + */ + funcdecl->localsymtab = new DsymbolTable(); + + // Establish function scope + ScopeDsymbol *ss = new ScopeDsymbol(); + // find enclosing scope symbol, might skip symbol-less CTFE and/or FuncExp scopes + for (Scope *scx = sc; ; scx = scx->enclosing) + { + if (scx->scopesym) + { + ss->parent = scx->scopesym; + break; + } + } + ss->loc = funcdecl->loc; + ss->endlinnum = funcdecl->endloc.linnum; + Scope *sc2 = sc->push(ss); + sc2->func = funcdecl; + sc2->parent = funcdecl; + sc2->callSuper = 0; + sc2->sbreak = NULL; + sc2->scontinue = NULL; + sc2->sw = NULL; + sc2->fes = funcdecl->fes; + sc2->linkage = LINKd; + sc2->stc &= ~(STCauto | STCscope | STCstatic | STCextern | STCabstract | + STCdeprecated | STCoverride | + STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref | STCreturn | + STCproperty | STCnothrow | STCpure | STCsafe | STCtrusted | STCsystem); + sc2->protection = Prot(Prot::public_); + sc2->explicitProtection = 0; + sc2->aligndecl = NULL; + if (funcdecl->ident != Id::require && funcdecl->ident != Id::ensure) + sc2->flags = sc->flags & ~SCOPEcontract; + sc2->flags &= ~SCOPEcompile; + sc2->tf = NULL; + sc2->os = NULL; + sc2->noctor = 0; + sc2->userAttribDecl = NULL; + if (sc2->intypeof == 1) sc2->intypeof = 2; + sc2->fieldinit = NULL; + sc2->fieldinit_dim = 0; + + /* Note: When a lambda is defined immediately under aggregate member + * scope, it should be contextless due to prevent interior pointers. + * e.g. + * // dg points 'this' - it's interior pointer + * class C { int x; void delegate() dg = (){ this.x = 1; }; } + * + * However, lambdas could be used inside typeof, in order to check + * some expressions varidity at compile time. For such case the lambda + * body can access aggregate instance members. + * e.g. + * class C { int x; static assert(is(typeof({ this.x = 1; }))); } + * + * To properly accept it, mark these lambdas as member functions. + */ + if (FuncLiteralDeclaration *fld = funcdecl->isFuncLiteralDeclaration()) + { + if (AggregateDeclaration *ad = funcdecl->isMember2()) + { + if (!sc->intypeof) + { + if (fld->tok == TOKdelegate) + funcdecl->error("cannot be %s members", ad->kind()); + else + fld->tok = TOKfunction; + } + else + { + if (fld->tok != TOKfunction) + fld->tok = TOKdelegate; + } + } + } + + // Declare 'this' + AggregateDeclaration *ad = funcdecl->isThis(); + funcdecl->vthis = funcdecl->declareThis(sc2, ad); + //printf("[%s] ad = %p vthis = %p\n", funcdecl->loc.toChars(), ad, funcdecl->vthis); + //if (funcdecl->vthis) printf("\tvthis->type = %s\n", funcdecl->vthis->type->toChars()); + + // Declare hidden variable _arguments[] and _argptr + if (f->parameterList.varargs == VARARGvariadic) + { + if (f->linkage == LINKd) + { + // Variadic arguments depend on Typeinfo being defined + if (!global.params.useTypeInfo || !Type::dtypeinfo || !Type::typeinfotypelist) + { + if (!global.params.useTypeInfo) + funcdecl->error("D-style variadic functions cannot be used with -betterC"); + else if (!Type::typeinfotypelist) + funcdecl->error("`object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions"); + else + funcdecl->error("`object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions"); + fatal(); + } + + // Declare _arguments[] + funcdecl->v_arguments = new VarDeclaration(Loc(), Type::typeinfotypelist->type, Id::_arguments_typeinfo, NULL); + funcdecl->v_arguments->storage_class |= STCtemp | STCparameter; + dsymbolSemantic(funcdecl->v_arguments, sc2); + sc2->insert(funcdecl->v_arguments); + funcdecl->v_arguments->parent = funcdecl; + + //Type *t = Type::typeinfo->type->constOf()->arrayOf(); + Type *t = Type::dtypeinfo->type->arrayOf(); + _arguments = new VarDeclaration(Loc(), t, Id::_arguments, NULL); + _arguments->storage_class |= STCtemp; + dsymbolSemantic(_arguments, sc2); + sc2->insert(_arguments); + _arguments->parent = funcdecl; + } + if (f->linkage == LINKd || f->parameterList.length()) + { + // Declare _argptr + Type *t = target.va_listType(funcdecl->loc, sc); + funcdecl->v_argptr = new VarDeclaration(Loc(), t, Id::_argptr, NULL); + funcdecl->v_argptr->storage_class |= STCtemp; + dsymbolSemantic(funcdecl->v_argptr, sc2); + sc2->insert(funcdecl->v_argptr); + funcdecl->v_argptr->parent = funcdecl; + } + } + + /* Declare all the function parameters as variables + * and install them in parameters[] + */ + size_t nparams = f->parameterList.length(); + if (nparams) + { + /* parameters[] has all the tuples removed, as the back end + * doesn't know about tuples + */ + funcdecl->parameters = new VarDeclarations(); + funcdecl->parameters->reserve(nparams); + for (size_t i = 0; i < nparams; i++) + { + Parameter *fparam = f->parameterList[i]; + Identifier *id = fparam->ident; + StorageClass stc = 0; + if (!id) + { + /* Generate identifier for un-named parameter, + * because we need it later on. + */ + fparam->ident = id = Identifier::generateId("_param_", i); + stc |= STCtemp; + } + Type *vtype = fparam->type; + VarDeclaration *v = new VarDeclaration(funcdecl->loc, vtype, id, NULL); + //printf("declaring parameter %s of type %s\n", v->toChars(), v->type->toChars()); + stc |= STCparameter; + if (f->parameterList.varargs == VARARGtypesafe && i + 1 == nparams) + stc |= STCvariadic; + if (funcdecl->flags & FUNCFLAGinferScope && !(fparam->storageClass & STCscope)) + stc |= STCmaybescope; + stc |= fparam->storageClass & (STCin | STCout | STCref | STCreturn | STCscope | STClazy | STCfinal | STC_TYPECTOR | STCnodtor); + v->storage_class = stc; + dsymbolSemantic(v, sc2); + if (!sc2->insert(v)) + funcdecl->error("parameter %s.%s is already defined", funcdecl->toChars(), v->toChars()); + else + funcdecl->parameters->push(v); + funcdecl->localsymtab->insert(v); + v->parent = funcdecl; + if (fparam->userAttribDecl) + v->userAttribDecl = fparam->userAttribDecl; + } + } + + // Declare the tuple symbols and put them in the symbol table, + // but not in parameters[]. + if (f->parameterList.parameters) + { + for (size_t i = 0; i < f->parameterList.parameters->length; i++) + { + Parameter *fparam = (*f->parameterList.parameters)[i]; + + if (!fparam->ident) + continue; // never used, so ignore + if (fparam->type->ty == Ttuple) + { + TypeTuple *t = (TypeTuple *)fparam->type; + size_t dim = Parameter::dim(t->arguments); + Objects *exps = new Objects(); + exps->setDim(dim); + for (size_t j = 0; j < dim; j++) + { + Parameter *narg = Parameter::getNth(t->arguments, j); + assert(narg->ident); + VarDeclaration *v = sc2->search(Loc(), narg->ident, NULL)->isVarDeclaration(); + assert(v); + Expression *e = new VarExp(v->loc, v); + (*exps)[j] = e; + } + assert(fparam->ident); + TupleDeclaration *v = new TupleDeclaration(funcdecl->loc, fparam->ident, exps); + //printf("declaring tuple %s\n", v->toChars()); + v->isexp = true; + if (!sc2->insert(v)) + funcdecl->error("parameter %s.%s is already defined", funcdecl->toChars(), v->toChars()); + funcdecl->localsymtab->insert(v); + v->parent = funcdecl; + } + } + } + + // Precondition invariant + Statement *fpreinv = NULL; + if (funcdecl->addPreInvariant()) + { + Expression *e = addInvariant(ad, funcdecl->vthis); + if (e) + fpreinv = new ExpStatement(Loc(), e); + } + + // Postcondition invariant + Statement *fpostinv = NULL; + if (funcdecl->addPostInvariant()) + { + Expression *e = addInvariant(ad, funcdecl->vthis); + if (e) + fpostinv = new ExpStatement(Loc(), e); + } + + // Pre/Postcondition contract + if (!funcdecl->fbody) + funcdecl->buildEnsureRequire(); + + Scope *scout = NULL; + if (needEnsure || funcdecl->addPostInvariant()) + { + if ((needEnsure && global.params.useOut == CHECKENABLEon) || fpostinv) + { + funcdecl->returnLabel = new LabelDsymbol(Id::returnLabel); + } + + // scope of out contract (need for vresult->semantic) + ScopeDsymbol *sym = new ScopeDsymbol(); + sym->parent = sc2->scopesym; + sym->loc = funcdecl->loc; + sym->endlinnum = funcdecl->endloc.linnum; + scout = sc2->push(sym); + } + + if (funcdecl->fbody) + { + ScopeDsymbol *sym = new ScopeDsymbol(); + sym->parent = sc2->scopesym; + sym->loc = funcdecl->loc; + sym->endlinnum = funcdecl->endloc.linnum; + sc2 = sc2->push(sym); + + AggregateDeclaration *ad2 = funcdecl->isMember2(); + + /* If this is a class constructor + */ + if (ad2 && funcdecl->isCtorDeclaration()) + { + allocFieldinit(sc2, ad2->fields.length); + for (size_t i = 0; i < ad2->fields.length; i++) + { + VarDeclaration *v = ad2->fields[i]; + v->ctorinit = 0; + } + } + + bool inferRef = (f->isref && (funcdecl->storage_class & STCauto)); + + funcdecl->fbody = statementSemantic(funcdecl->fbody, sc2); + if (!funcdecl->fbody) + funcdecl->fbody = new CompoundStatement(Loc(), new Statements()); + + if (funcdecl->naked) + { + fpreinv = NULL; // can't accommodate with no stack frame + fpostinv = NULL; + } + + assert(funcdecl->type == f || + (funcdecl->type->ty == Tfunction && + f->purity == PUREimpure && + ((TypeFunction *)funcdecl->type)->purity >= PUREfwdref)); + f = (TypeFunction *)funcdecl->type; + + if (funcdecl->inferRetType) + { + // If no return type inferred yet, then infer a void + if (!f->next) + f->next = Type::tvoid; + if (f->checkRetType(funcdecl->loc)) + funcdecl->fbody = new ErrorStatement(); + } + if (global.params.vcomplex && f->next != NULL) + f->next->checkComplexTransition(funcdecl->loc); + + if (funcdecl->returns && !funcdecl->fbody->isErrorStatement()) + { + for (size_t i = 0; i < funcdecl->returns->length; ) + { + Expression *exp = (*funcdecl->returns)[i]->exp; + if (exp->op == TOKvar && ((VarExp *)exp)->var == funcdecl->vresult) + { + if (addReturn0(funcdecl)) + exp->type = Type::tint32; + else + exp->type = f->next; + // Remove `return vresult;` from returns + funcdecl->returns->remove(i); + continue; + } + if (inferRef && f->isref && !exp->type->constConv(f->next)) // Bugzilla 13336 + f->isref = false; + i++; + } + } + if (f->isref) // Function returns a reference + { + if (funcdecl->storage_class & STCauto) + funcdecl->storage_class &= ~STCauto; + } + if (!target.isReturnOnStack(f, funcdecl->needThis()) || !funcdecl->checkNRVO()) + funcdecl->nrvo_can = 0; + + if (funcdecl->fbody->isErrorStatement()) + ; + else if (funcdecl->isStaticCtorDeclaration()) + { + /* It's a static constructor. Ensure that all + * ctor consts were initialized. + */ + ScopeDsymbol *pd = funcdecl->toParent()->isScopeDsymbol(); + for (size_t i = 0; i < pd->members->length; i++) + { + Dsymbol *s = (*pd->members)[i]; + s->checkCtorConstInit(); + } + } + else if (ad2 && funcdecl->isCtorDeclaration()) + { + ClassDeclaration *cd = ad2->isClassDeclaration(); + + // Verify that all the ctorinit fields got initialized + if (!(sc2->callSuper & CSXthis_ctor)) + { + for (size_t i = 0; i < ad2->fields.length; i++) + { + VarDeclaration *v = ad2->fields[i]; + if (v->isThisDeclaration()) + continue; + if (v->ctorinit == 0) + { + /* Current bugs in the flow analysis: + * 1. union members should not produce error messages even if + * not assigned to + * 2. structs should recognize delegating opAssign calls as well + * as delegating calls to other constructors + */ + if (v->isCtorinit() && !v->type->isMutable() && cd) + funcdecl->error("missing initializer for %s field %s", MODtoChars(v->type->mod), v->toChars()); + else if (v->storage_class & STCnodefaultctor) + error(funcdecl->loc, "field %s must be initialized in constructor", v->toChars()); + else if (v->type->needsNested()) + error(funcdecl->loc, "field %s must be initialized in constructor, because it is nested struct", v->toChars()); + } + else + { + bool mustInit = (v->storage_class & STCnodefaultctor || + v->type->needsNested()); + if (mustInit && !(sc2->fieldinit[i] & CSXthis_ctor)) + { + funcdecl->error("field %s must be initialized but skipped", v->toChars()); + } + } + } + } + freeFieldinit(sc2); + + if (cd && + !(sc2->callSuper & CSXany_ctor) && + cd->baseClass && cd->baseClass->ctor) + { + sc2->callSuper = 0; + + // Insert implicit super() at start of fbody + FuncDeclaration *fd = resolveFuncCall(Loc(), sc2, cd->baseClass->ctor, NULL, funcdecl->vthis->type, NULL, 1); + if (!fd) + { + funcdecl->error("no match for implicit super() call in constructor"); + } + else if (fd->storage_class & STCdisable) + { + funcdecl->error("cannot call super() implicitly because it is annotated with @disable"); + } + else + { + Expression *e1 = new SuperExp(Loc()); + Expression *e = new CallExp(Loc(), e1); + e = expressionSemantic(e, sc2); + + Statement *s = new ExpStatement(Loc(), e); + funcdecl->fbody = new CompoundStatement(Loc(), s, funcdecl->fbody); + } + } + //printf("callSuper = x%x\n", sc2->callSuper); + } + + /* https://issues.dlang.org/show_bug.cgi?id=17502 + * Wait until after the return type has been inferred before + * generating the contracts for this function, and merging contracts + * from overrides. + * + * https://issues.dlang.org/show_bug.cgi?id=17893 + * However should take care to generate this before inferered + * function attributes are applied, such as 'nothrow'. + * + * This was originally at the end of the first semantic pass, but + * required a fix-up to be done here for the '__result' variable + * type of __ensure() inside auto functions, but this didn't work + * if the out parameter was implicit. + */ + funcdecl->buildEnsureRequire(); + + int blockexit = BEnone; + if (!funcdecl->fbody->isErrorStatement()) + { + // Check for errors related to 'nothrow'. + unsigned int nothrowErrors = global.errors; + blockexit = blockExit(funcdecl->fbody, funcdecl, f->isnothrow); + if (f->isnothrow && (global.errors != nothrowErrors)) + error(funcdecl->loc, "nothrow %s `%s` may throw", funcdecl->kind(), funcdecl->toPrettyChars()); + if (funcdecl->flags & FUNCFLAGnothrowInprocess) + { + if (funcdecl->type == f) f = (TypeFunction *)f->copy(); + f->isnothrow = !(blockexit & BEthrow); + } + } + + if (funcdecl->fbody->isErrorStatement()) + ; + else if (ad2 && funcdecl->isCtorDeclaration()) + { + /* Append: + * return this; + * to function body + */ + if (blockexit & BEfallthru) + { + Statement *s = new ReturnStatement(funcdecl->loc, NULL); + s = statementSemantic(s, sc2); + funcdecl->fbody = new CompoundStatement(funcdecl->loc, funcdecl->fbody, s); + funcdecl->hasReturnExp |= (funcdecl->hasReturnExp & 1 ? 16 : 1); + } + } + else if (funcdecl->fes) + { + // For foreach(){} body, append a return 0; + if (blockexit & BEfallthru) + { + Expression *e = new IntegerExp(0); + Statement *s = new ReturnStatement(Loc(), e); + funcdecl->fbody = new CompoundStatement(Loc(), funcdecl->fbody, s); + funcdecl->hasReturnExp |= (funcdecl->hasReturnExp & 1 ? 16 : 1); + } + assert(!funcdecl->returnLabel); + } + else + { + const bool inlineAsm = (funcdecl->hasReturnExp & 8) != 0; + if ((blockexit & BEfallthru) && f->next->ty != Tvoid && !inlineAsm) + { + Expression *e; + if (!funcdecl->hasReturnExp) + funcdecl->error("has no return statement, but is expected to return a value of type %s", f->next->toChars()); + else + funcdecl->error("no return exp; or assert(0); at end of function"); + if (global.params.useAssert == CHECKENABLEon && + !global.params.useInline) + { + /* Add an assert(0, msg); where the missing return + * should be. + */ + e = new AssertExp(funcdecl->endloc, + new IntegerExp(0), + new StringExp(funcdecl->loc, const_cast<char *>("missing return expression"))); + } + else + e = new HaltExp(funcdecl->endloc); + e = new CommaExp(Loc(), e, f->next->defaultInit()); + e = expressionSemantic(e, sc2); + Statement *s = new ExpStatement(Loc(), e); + funcdecl->fbody = new CompoundStatement(Loc(), funcdecl->fbody, s); + } + } + + if (funcdecl->returns) + { + bool implicit0 = addReturn0(funcdecl); + Type *tret = implicit0 ? Type::tint32 : f->next; + assert(tret->ty != Tvoid); + if (funcdecl->vresult || funcdecl->returnLabel) + funcdecl->buildResultVar(scout ? scout : sc2, tret); + + /* Cannot move this loop into NrvoWalker, because + * returns[i] may be in the nested delegate for foreach-body. + */ + for (size_t i = 0; i < funcdecl->returns->length; i++) + { + ReturnStatement *rs = (*funcdecl->returns)[i]; + Expression *exp = rs->exp; + if (exp->op == TOKerror) + continue; + if (tret->ty == Terror) + { + // Bugzilla 13702 + exp = checkGC(sc2, exp); + continue; + } + + if (!exp->implicitConvTo(tret) && + funcdecl->parametersIntersect(exp->type)) + { + if (exp->type->immutableOf()->implicitConvTo(tret)) + exp = exp->castTo(sc2, exp->type->immutableOf()); + else if (exp->type->wildOf()->implicitConvTo(tret)) + exp = exp->castTo(sc2, exp->type->wildOf()); + } + exp = exp->implicitCastTo(sc2, tret); + + if (f->isref) + { + // Function returns a reference + exp = exp->toLvalue(sc2, exp); + checkReturnEscapeRef(sc2, exp, false); + } + else + { + exp = exp->optimize(WANTvalue); + + /* Bugzilla 10789: + * If NRVO is not possible, all returned lvalues should call their postblits. + */ + if (!funcdecl->nrvo_can) + exp = doCopyOrMove(sc2, exp); + + if (tret->hasPointers()) + checkReturnEscape(sc2, exp, false); + } + + exp = checkGC(sc2, exp); + + if (funcdecl->vresult) + { + // Create: return vresult = exp; + exp = new BlitExp(rs->loc, funcdecl->vresult, exp); + exp->type = funcdecl->vresult->type; + + if (rs->caseDim) + exp = Expression::combine(exp, new IntegerExp(rs->caseDim)); + } + else if (funcdecl->tintro && !tret->equals(funcdecl->tintro->nextOf())) + { + exp = exp->implicitCastTo(sc2, funcdecl->tintro->nextOf()); + } + rs->exp = exp; + } + } + if (funcdecl->nrvo_var || funcdecl->returnLabel) + { + NrvoWalker nw; + nw.fd = funcdecl; + nw.sc = sc2; + nw.visitStmt(funcdecl->fbody); + } + + sc2 = sc2->pop(); + } + + funcdecl->frequire = funcdecl->mergeFrequire(funcdecl->frequire); + funcdecl->fensure = funcdecl->mergeFensure(funcdecl->fensure, Id::result); + + Statement *freq = funcdecl->frequire; + Statement *fens = funcdecl->fensure; + + /* Do the semantic analysis on the [in] preconditions and + * [out] postconditions. + */ + if (freq) + { + /* frequire is composed of the [in] contracts + */ + ScopeDsymbol *sym = new ScopeDsymbol(); + sym->parent = sc2->scopesym; + sym->loc = funcdecl->loc; + sym->endlinnum = funcdecl->endloc.linnum; + sc2 = sc2->push(sym); + sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPErequire; + + // BUG: need to error if accessing out parameters + // BUG: need to disallow returns and throws + // BUG: verify that all in and ref parameters are read + freq = statementSemantic(freq, sc2); + blockExit(freq, funcdecl, false); + + sc2 = sc2->pop(); + + if (global.params.useIn == CHECKENABLEoff) + freq = NULL; + } + + if (fens) + { + /* fensure is composed of the [out] contracts + */ + if (f->next->ty == Tvoid && funcdecl->fensures) + { + for (size_t i = 0; i < funcdecl->fensures->length; i++) + { + Ensure e = (*funcdecl->fensures)[i]; + if (e.id) + { + funcdecl->error(e.ensure->loc, "`void` functions have no result"); + //fens = NULL; + } + } + } + + sc2 = scout; //push + sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPEensure; + + // BUG: need to disallow returns and throws + if (funcdecl->fensure && f->next->ty != Tvoid) + funcdecl->buildResultVar(scout, f->next); + + fens = statementSemantic(fens, sc2); + blockExit(fens, funcdecl, false); + + sc2 = sc2->pop(); + + if (global.params.useOut == CHECKENABLEoff) + fens = NULL; + } + + if (funcdecl->fbody && funcdecl->fbody->isErrorStatement()) + ; + else + { + Statements *a = new Statements(); + + // Merge in initialization of 'out' parameters + if (funcdecl->parameters) + { + for (size_t i = 0; i < funcdecl->parameters->length; i++) + { + VarDeclaration *v = (*funcdecl->parameters)[i]; + if (v->storage_class & STCout) + { + assert(v->_init); + ExpInitializer *ie = v->_init->isExpInitializer(); + assert(ie); + if (ie->exp->op == TOKconstruct) + ie->exp->op = TOKassign; // construction occured in parameter processing + a->push(new ExpStatement(Loc(), ie->exp)); + } + } + } + + if (funcdecl->v_argptr) + { + // Handled in FuncDeclaration::toObjFile + funcdecl->v_argptr->_init = new VoidInitializer(funcdecl->loc); + } + + if (_arguments) + { + /* Advance to elements[] member of TypeInfo_Tuple with: + * _arguments = v_arguments.elements; + */ + Expression *e = new VarExp(Loc(), funcdecl->v_arguments); + e = new DotIdExp(Loc(), e, Id::elements); + e = new ConstructExp(Loc(), _arguments, e); + e = expressionSemantic(e, sc2); + + _arguments->_init = new ExpInitializer(Loc(), e); + DeclarationExp *de = new DeclarationExp(Loc(), _arguments); + a->push(new ExpStatement(Loc(), de)); + } + + // Merge contracts together with body into one compound statement + + if (freq || fpreinv) + { + if (!freq) + freq = fpreinv; + else if (fpreinv) + freq = new CompoundStatement(Loc(), freq, fpreinv); + + a->push(freq); + } + + if (funcdecl->fbody) + a->push(funcdecl->fbody); + + if (fens || fpostinv) + { + if (!fens) + fens = fpostinv; + else if (fpostinv) + fens = new CompoundStatement(Loc(), fpostinv, fens); + + LabelStatement *ls = new LabelStatement(Loc(), Id::returnLabel, fens); + funcdecl->returnLabel->statement = ls; + a->push(funcdecl->returnLabel->statement); + + if (f->next->ty != Tvoid && funcdecl->vresult) + { + // Create: return vresult; + Expression *e = new VarExp(Loc(), funcdecl->vresult); + if (funcdecl->tintro) + { + e = e->implicitCastTo(sc, funcdecl->tintro->nextOf()); + e = expressionSemantic(e, sc); + } + ReturnStatement *s = new ReturnStatement(Loc(), e); + a->push(s); + } + } + if (addReturn0(funcdecl)) + { + // Add a return 0; statement + Statement *s = new ReturnStatement(Loc(), new IntegerExp(0)); + a->push(s); + } + + Statement *sbody = new CompoundStatement(Loc(), a); + /* Append destructor calls for parameters as finally blocks. + */ + if (funcdecl->parameters) + { + for (size_t i = 0; i < funcdecl->parameters->length; i++) + { + VarDeclaration *v = (*funcdecl->parameters)[i]; + + if (v->storage_class & (STCref | STCout | STClazy)) + continue; + + if (v->needsScopeDtor()) + { + // same with ExpStatement.scopeCode() + Statement *s = new DtorExpStatement(Loc(), v->edtor, v); + v->storage_class |= STCnodtor; + + s = statementSemantic(s, sc2); + + bool isnothrow = f->isnothrow & !(funcdecl->flags & FUNCFLAGnothrowInprocess); + int blockexit = blockExit(s, funcdecl, isnothrow); + if (f->isnothrow && isnothrow && blockexit & BEthrow) + error(funcdecl->loc, "nothrow %s `%s` may throw", funcdecl->kind(), funcdecl->toPrettyChars()); + if (funcdecl->flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow) + f->isnothrow = false; + if (blockExit(sbody, funcdecl, f->isnothrow) == BEfallthru) + sbody = new CompoundStatement(Loc(), sbody, s); + else + sbody = new TryFinallyStatement(Loc(), sbody, s); + } + } + } + // from this point on all possible 'throwers' are checked + funcdecl->flags &= ~FUNCFLAGnothrowInprocess; + + if (funcdecl->isSynchronized()) + { + /* Wrap the entire function body in a synchronized statement + */ + ClassDeclaration *cd = funcdecl->isThis() ? funcdecl->isThis()->isClassDeclaration() : funcdecl->parent->isClassDeclaration(); + + if (cd) + { + if (!global.params.is64bit && + global.params.isWindows && + !funcdecl->isStatic() && !sbody->usesEH() && !global.params.trace) + { + /* The back end uses the "jmonitor" hack for syncing; + * no need to do the sync at this level. + */ + } + else + { + Expression *vsync; + if (funcdecl->isStatic()) + { + // The monitor is in the ClassInfo + vsync = new DotIdExp(funcdecl->loc, resolve(funcdecl->loc, sc2, cd, false), Id::classinfo); + } + else + { + // 'this' is the monitor + vsync = new VarExp(funcdecl->loc, funcdecl->vthis); + } + sbody = new PeelStatement(sbody); // don't redo semantic() + sbody = new SynchronizedStatement(funcdecl->loc, vsync, sbody); + sbody = statementSemantic(sbody, sc2); + } + } + else + { + funcdecl->error("synchronized function %s must be a member of a class", funcdecl->toChars()); + } + } + + // If declaration has no body, don't set sbody to prevent incorrect codegen. + if (funcdecl->fbody || allowsContractWithoutBody(funcdecl)) + funcdecl->fbody = sbody; + } + + // Fix up forward-referenced gotos + if (funcdecl->gotos) + { + for (size_t i = 0; i < funcdecl->gotos->length; ++i) + { + (*funcdecl->gotos)[i]->checkLabel(); + } + } + + if (funcdecl->naked && (funcdecl->fensures || funcdecl->frequires)) + funcdecl->error("naked assembly functions with contracts are not supported"); + + sc2->callSuper = 0; + sc2->pop(); + } + + if (funcdecl->checkClosure()) + { + // We should be setting errors here instead of relying on the global error count. + //errors = true; + } + + /* If function survived being marked as impure, then it is pure + */ + if (funcdecl->flags & FUNCFLAGpurityInprocess) + { + funcdecl->flags &= ~FUNCFLAGpurityInprocess; + if (funcdecl->type == f) + f = (TypeFunction *)f->copy(); + f->purity = PUREfwdref; + } + + if (funcdecl->flags & FUNCFLAGsafetyInprocess) + { + funcdecl->flags &= ~FUNCFLAGsafetyInprocess; + if (funcdecl->type == f) + f = (TypeFunction *)f->copy(); + f->trust = TRUSTsafe; + } + + if (funcdecl->flags & FUNCFLAGnogcInprocess) + { + funcdecl->flags &= ~FUNCFLAGnogcInprocess; + if (funcdecl->type == f) + f = (TypeFunction *)f->copy(); + f->isnogc = true; + } + + if (funcdecl->flags & FUNCFLAGreturnInprocess) + { + funcdecl->flags &= ~FUNCFLAGreturnInprocess; + if (funcdecl->storage_class & STCreturn) + { + if (funcdecl->type == f) + f = (TypeFunction *)f->copy(); + f->isreturn = true; + } + } + + funcdecl->flags &= ~FUNCFLAGinferScope; + + // Infer STCscope + if (funcdecl->parameters) + { + size_t nfparams = f->parameterList.length(); + assert(nfparams == funcdecl->parameters->length); + for (size_t u = 0; u < funcdecl->parameters->length; u++) + { + VarDeclaration *v = (*funcdecl->parameters)[u]; + if (v->storage_class & STCmaybescope) + { + //printf("Inferring scope for %s\n", v->toChars()); + Parameter *p = f->parameterList[u]; + v->storage_class &= ~STCmaybescope; + v->storage_class |= STCscope | STCscopeinferred; + p->storageClass |= STCscope | STCscopeinferred; + assert(!(p->storageClass & STCmaybescope)); + } + } + } + + if (funcdecl->vthis && funcdecl->vthis->storage_class & STCmaybescope) + { + funcdecl->vthis->storage_class &= ~STCmaybescope; + funcdecl->vthis->storage_class |= STCscope | STCscopeinferred; + f->isscope = true; + f->isscopeinferred = true; + } + + // reset deco to apply inference result to mangled name + if (f != funcdecl->type) + f->deco = NULL; + + // Do semantic type AFTER pure/nothrow inference. + if (!f->deco && funcdecl->ident != Id::xopEquals && funcdecl->ident != Id::xopCmp) + { + sc = sc->push(); + if (funcdecl->isCtorDeclaration()) // Bugzilla #15665 + sc->flags |= SCOPEctor; + sc->stc = 0; + sc->linkage = funcdecl->linkage; // Bugzilla 8496 + funcdecl->type = typeSemantic(f, funcdecl->loc, sc); + sc = sc->pop(); + } + + /* If this function had instantiated with gagging, error reproduction will be + * done by TemplateInstance::semantic. + * Otherwise, error gagging should be temporarily ungagged by functionSemantic3. + */ + funcdecl->semanticRun = PASSsemantic3done; + funcdecl->semantic3Errors = (global.errors != oldErrors) || (funcdecl->fbody && funcdecl->fbody->isErrorStatement()); + if (funcdecl->type->ty == Terror) + funcdecl->errors = true; + //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", funcdecl->parent->toChars(), funcdecl->toChars(), sc, funcdecl->loc.toChars()); + //fflush(stdout); + } + + void visit(Nspace *ns) + { + if (ns->semanticRun >= PASSsemantic3) + return; + ns->semanticRun = PASSsemantic3; + if (ns->members) + { + sc = sc->push(ns); + sc->linkage = LINKcpp; + for (size_t i = 0; i < ns->members->length; i++) + { + Dsymbol *s = (*ns->members)[i]; + semantic3(s, sc); + } + sc->pop(); + } + } + + void visit(AttribDeclaration *ad) + { + Dsymbols *d = ad->include(sc); + + if (d) + { + Scope *sc2 = ad->newScope(sc); + + for (size_t i = 0; i < d->length; i++) + { + Dsymbol *s = (*d)[i]; + semantic3(s, sc2); + } + + if (sc2 != sc) + sc2->pop(); + } + } + + void visit(AggregateDeclaration *ad) + { + //printf("AggregateDeclaration::semantic3(%s) type = %s, errors = %d\n", ad->toChars(), ad->type->toChars(), ad->errors); + if (!ad->members) + return; + + StructDeclaration *sd = ad->isStructDeclaration(); + if (!sc) // from runDeferredSemantic3 for TypeInfo generation + { + assert(sd); + sd->semanticTypeInfoMembers(); + return; + } + + Scope *sc2 = ad->newScope(sc); + + for (size_t i = 0; i < ad->members->length; i++) + { + Dsymbol *s = (*ad->members)[i]; + semantic3(s, sc2); + } + + sc2->pop(); + + // don't do it for unused deprecated types + // or error types + if (!ad->getRTInfo && Type::rtinfo && + (!ad->isDeprecated() || global.params.useDeprecated != DIAGNOSTICerror) && + (ad->type && ad->type->ty != Terror)) + { + // Evaluate: RTinfo!type + Objects *tiargs = new Objects(); + tiargs->push(ad->type); + TemplateInstance *ti = new TemplateInstance(ad->loc, Type::rtinfo, tiargs); + + Scope *sc3 = ti->tempdecl->_scope->startCTFE(); + sc3->tinst = sc->tinst; + sc3->minst = sc->minst; + if (ad->isDeprecated()) + sc3->stc |= STCdeprecated; + + dsymbolSemantic(ti, sc3); + semantic2(ti, sc3); + semantic3(ti, sc3); + Expression *e = resolve(Loc(), sc3, ti->toAlias(), false); + + sc3->endCTFE(); + + e = e->ctfeInterpret(); + ad->getRTInfo = e; + } + + if (sd) + sd->semanticTypeInfoMembers(); + ad->semanticRun = PASSsemantic3done; + } +}; + +/************************************* + * Does semantic analysis on function bodies. + */ +void semantic3(Dsymbol *dsym, Scope *sc) +{ + Semantic3Visitor v(sc); + dsym->accept(&v); +} |