/* 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 if (f->next->ty == Tnoreturn) { } else { const bool inlineAsm = (funcdecl->hasReturnExp & 8) != 0; if ((blockexit & BEfallthru) && f->next->ty != Tvoid && !inlineAsm) { 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 (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 (target.libraryObjectMonitors(funcdecl, sbody)) { 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); }