diff options
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); +} |