aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd/semantic3.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/semantic3.c')
-rw-r--r--gcc/d/dmd/semantic3.c1421
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);
+}