blob: 6bd9a6d2e27c6e266aad902757d823ce474a53c6 [file] [log] [blame]
/* 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);
}