blob: 04c70cf3b7b3b99b514c88b3d1e00ac963a3280d [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2019 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
* https://github.com/D-Programming-Language/dmd/blob/master/src/func.c
*/
#include "root/dsystem.h"
#include "mars.h"
#include "init.h"
#include "declaration.h"
#include "attrib.h"
#include "expression.h"
#include "scope.h"
#include "mtype.h"
#include "aggregate.h"
#include "identifier.h"
#include "id.h"
#include "module.h"
#include "statement.h"
#include "template.h"
#include "hdrgen.h"
#include "target.h"
#include "parse.h"
#include "root/rmem.h"
#include "visitor.h"
#include "objc.h"
Expression *addInvariant(Loc loc, Scope *sc, AggregateDeclaration *ad, VarDeclaration *vthis, bool direct);
bool checkReturnEscape(Scope *sc, Expression *e, bool gag);
bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag);
bool checkNestedRef(Dsymbol *s, Dsymbol *p);
Statement *semantic(Statement *s, Scope *sc);
void semantic(Catch *c, Scope *sc);
Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
Expression *semantic(Expression *e, Scope *sc);
int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow);
TypeIdentifier *getThrowable();
RET retStyle(TypeFunction *tf);
void MODtoBuffer(OutBuffer *buf, MOD mod);
char *MODtoChars(MOD mod);
bool MODimplicitConv(MOD modfrom, MOD modto);
MATCH MODmethodConv(MOD modfrom, MOD modto);
void allocFieldinit(Scope *sc, size_t dim);
void freeFieldinit(Scope *sc);
Objc *objc();
/* A visitor to walk entire statements and provides ability to replace any sub-statements.
*/
class StatementRewriteWalker : public Visitor
{
/* Point the currently visited statement.
* By using replaceCurrent() method, you can replace AST during walking.
*/
Statement **ps;
public:
void visitStmt(Statement *&s) { ps = &s; s->accept(this); }
void replaceCurrent(Statement *s) { *ps = s; }
void visit(ErrorStatement *) { }
void visit(PeelStatement *s)
{
if (s->s)
visitStmt(s->s);
}
void visit(ExpStatement *) { }
void visit(DtorExpStatement *) { }
void visit(CompileStatement *) { }
void visit(CompoundStatement *s)
{
if (s->statements && s->statements->dim)
{
for (size_t i = 0; i < s->statements->dim; i++)
{
if ((*s->statements)[i])
visitStmt((*s->statements)[i]);
}
}
}
void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); }
void visit(UnrolledLoopStatement *s)
{
if (s->statements && s->statements->dim)
{
for (size_t i = 0; i < s->statements->dim; i++)
{
if ((*s->statements)[i])
visitStmt((*s->statements)[i]);
}
}
}
void visit(ScopeStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(WhileStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(DoStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(ForStatement *s)
{
if (s->_init)
visitStmt(s->_init);
if (s->_body)
visitStmt(s->_body);
}
void visit(ForeachStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(ForeachRangeStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(IfStatement *s)
{
if (s->ifbody)
visitStmt(s->ifbody);
if (s->elsebody)
visitStmt(s->elsebody);
}
void visit(ConditionalStatement *) { }
void visit(PragmaStatement *) { }
void visit(StaticAssertStatement *) { }
void visit(SwitchStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(CaseStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(CaseRangeStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(DefaultStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(GotoDefaultStatement *) { }
void visit(GotoCaseStatement *) { }
void visit(SwitchErrorStatement *) { }
void visit(ReturnStatement *) { }
void visit(BreakStatement *) { }
void visit(ContinueStatement *) { }
void visit(SynchronizedStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(WithStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(TryCatchStatement *s)
{
if (s->_body)
visitStmt(s->_body);
if (s->catches && s->catches->dim)
{
for (size_t i = 0; i < s->catches->dim; i++)
{
Catch *c = (*s->catches)[i];
if (c && c->handler)
visitStmt(c->handler);
}
}
}
void visit(TryFinallyStatement *s)
{
if (s->_body)
visitStmt(s->_body);
if (s->finalbody)
visitStmt(s->finalbody);
}
void visit(OnScopeStatement *) { }
void visit(ThrowStatement *) { }
void visit(DebugStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(GotoStatement *) { }
void visit(LabelStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(AsmStatement *) { }
void visit(ImportStatement *) { }
};
/* 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;
::semantic(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);
}
};
/********************************* FuncDeclaration ****************************/
FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type)
: Declaration(id)
{
//printf("FuncDeclaration(id = '%s', type = %p)\n", id->toChars(), type);
//printf("storage_class = x%x\n", storage_class);
this->storage_class = storage_class;
this->type = type;
if (type)
{
// Normalize storage_class, because function-type related attributes
// are already set in the 'type' in parsing phase.
this->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR);
}
this->loc = loc;
this->endloc = endloc;
fthrows = NULL;
frequire = NULL;
fdrequire = NULL;
fdensure = NULL;
mangleString = NULL;
outId = NULL;
vresult = NULL;
returnLabel = NULL;
fensure = NULL;
fbody = NULL;
localsymtab = NULL;
vthis = NULL;
v_arguments = NULL;
v_argptr = NULL;
parameters = NULL;
labtab = NULL;
overnext = NULL;
overnext0 = NULL;
vtblIndex = -1;
hasReturnExp = 0;
naked = false;
generated = false;
inlineStatusExp = ILSuninitialized;
inlineStatusStmt = ILSuninitialized;
inlining = PINLINEdefault;
inlineNest = 0;
ctfeCode = NULL;
isArrayOp = 0;
semantic3Errors = false;
fes = NULL;
interfaceVirtual = NULL;
introducing = 0;
tintro = NULL;
/* The type given for "infer the return type" is a TypeFunction with
* NULL for the return type.
*/
inferRetType = (type && type->nextOf() == NULL);
storage_class2 = 0;
hasReturnExp = 0;
nrvo_can = 1;
nrvo_var = NULL;
shidden = NULL;
builtin = BUILTINunknown;
tookAddressOf = 0;
requiresClosure = false;
inlinedNestedCallees = NULL;
flags = 0;
returns = NULL;
gotos = NULL;
selector = NULL;
}
FuncDeclaration *FuncDeclaration::create(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type)
{
return new FuncDeclaration(loc, endloc, id, storage_class, type);
}
Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)
{
//printf("FuncDeclaration::syntaxCopy('%s')\n", toChars());
FuncDeclaration *f =
s ? (FuncDeclaration *)s
: new FuncDeclaration(loc, endloc, ident, storage_class, type->syntaxCopy());
f->outId = outId;
f->frequire = frequire ? frequire->syntaxCopy() : NULL;
f->fensure = fensure ? fensure->syntaxCopy() : NULL;
f->fbody = fbody ? fbody->syntaxCopy() : NULL;
assert(!fthrows); // deprecated
return f;
}
/**********************************
* Decide if attributes for this function can be inferred from examining
* the function body.
* Returns:
* true if can
*/
static bool canInferAttributes(FuncDeclaration *fd, Scope *sc)
{
if (!fd->fbody)
return false;
if (fd->isVirtualMethod())
return false; // since they may be overridden
if (sc->func &&
/********** this is for backwards compatibility for the moment ********/
(!fd->isMember() || (sc->func->isSafeBypassingInference() && !fd->isInstantiated())))
return true;
if (fd->isFuncLiteralDeclaration() || // externs are not possible with literals
(fd->storage_class & STCinference) || // do attribute inference
(fd->inferRetType && !fd->isCtorDeclaration()))
return true;
if (fd->isInstantiated())
{
TemplateInstance *ti = fd->parent->isTemplateInstance();
if (ti == NULL || ti->isTemplateMixin() || ti->tempdecl->ident == fd->ident)
return true;
}
return false;
}
/*****************************************
* Initialize for inferring the attributes of this function.
*/
static void initInferAttributes(FuncDeclaration *fd)
{
//printf("initInferAttributes() for %s\n", toPrettyChars());
TypeFunction *tf = fd->type->toTypeFunction();
if (tf->purity == PUREimpure) // purity not specified
fd->flags |= FUNCFLAGpurityInprocess;
if (tf->trust == TRUSTdefault)
fd->flags |= FUNCFLAGsafetyInprocess;
if (!tf->isnothrow)
fd->flags |= FUNCFLAGnothrowInprocess;
if (!tf->isnogc)
fd->flags |= FUNCFLAGnogcInprocess;
if (!fd->isVirtual() || fd->introducing)
fd->flags |= FUNCFLAGreturnInprocess;
// Initialize for inferring STCscope
if (global.params.vsafe)
fd->flags |= FUNCFLAGinferScope;
}
// Do the semantic analysis on the external interface to the function.
void FuncDeclaration::semantic(Scope *sc)
{
TypeFunction *f;
AggregateDeclaration *ad;
InterfaceDeclaration *id;
if (semanticRun != PASSinit && isFuncLiteralDeclaration())
{
/* Member functions that have return types that are
* forward references can have semantic() run more than
* once on them.
* See test\interface2.d, test20
*/
return;
}
if (semanticRun >= PASSsemanticdone)
return;
assert(semanticRun <= PASSsemantic);
semanticRun = PASSsemantic;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *parent = toParent();
foverrides.setDim(0); // reset in case semantic() is being retried for this function
storage_class |= sc->stc & ~STCref;
ad = isThis();
// Don't nest structs b/c of generated methods which should not access the outer scopes.
// https://issues.dlang.org/show_bug.cgi?id=16627
if (ad && !generated)
{
storage_class |= ad->storage_class & (STC_TYPECTOR | STCsynchronized);
ad->makeNested();
}
if (sc->func)
storage_class |= sc->func->storage_class & STCdisable;
// Remove prefix storage classes silently.
if ((storage_class & STC_TYPECTOR) && !(ad || isNested()))
storage_class &= ~STC_TYPECTOR;
//printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", storage_class, sc->stc, Declaration::isFinal());
FuncLiteralDeclaration *fld = isFuncLiteralDeclaration();
if (fld && fld->treq)
{
Type *treq = fld->treq;
assert(treq->nextOf()->ty == Tfunction);
if (treq->ty == Tdelegate)
fld->tok = TOKdelegate;
else if (treq->ty == Tpointer && treq->nextOf()->ty == Tfunction)
fld->tok = TOKfunction;
else
assert(0);
linkage = treq->nextOf()->toTypeFunction()->linkage;
}
else
linkage = sc->linkage;
inlining = sc->inlining;
protection = sc->protection;
userAttribDecl = sc->userAttribDecl;
if (!originalType)
originalType = type->syntaxCopy();
if (type->ty != Tfunction)
{
if (type->ty != Terror)
{
error("%s must be a function instead of %s", toChars(), type->toChars());
type = Type::terror;
}
errors = true;
return;
}
if (!type->deco)
{
sc = sc->push();
sc->stc |= storage_class & (STCdisable | STCdeprecated); // forward to function type
TypeFunction *tf = type->toTypeFunction();
if (sc->func)
{
/* If the nesting parent is pure without inference,
* then this function defaults to pure too.
*
* auto foo() pure {
* auto bar() {} // become a weak purity funciton
* class C { // nested class
* auto baz() {} // become a weak purity funciton
* }
*
* static auto boo() {} // typed as impure
* // Even though, boo cannot call any impure functions.
* // See also Expression::checkPurity().
* }
*/
if (tf->purity == PUREimpure && (isNested() || isThis()))
{
FuncDeclaration *fd = NULL;
for (Dsymbol *p = toParent2(); p; p = p->toParent2())
{
if (AggregateDeclaration *adx = p->isAggregateDeclaration())
{
if (adx->isNested())
continue;
break;
}
if ((fd = p->isFuncDeclaration()) != NULL)
break;
}
/* If the parent's purity is inferred, then this function's purity needs
* to be inferred first.
*/
if (fd && fd->isPureBypassingInference() >= PUREweak &&
!isInstantiated())
{
tf->purity = PUREfwdref; // default to pure
}
}
}
if (tf->isref) sc->stc |= STCref;
if (tf->isscope) sc->stc |= STCscope;
if (tf->isnothrow) sc->stc |= STCnothrow;
if (tf->isnogc) sc->stc |= STCnogc;
if (tf->isproperty) sc->stc |= STCproperty;
if (tf->purity == PUREfwdref) sc->stc |= STCpure;
if (tf->trust != TRUSTdefault)
sc->stc &= ~(STCsafe | STCsystem | STCtrusted);
if (tf->trust == TRUSTsafe) sc->stc |= STCsafe;
if (tf->trust == TRUSTsystem) sc->stc |= STCsystem;
if (tf->trust == TRUSTtrusted) sc->stc |= STCtrusted;
if (isCtorDeclaration())
{
sc->flags |= SCOPEctor;
Type *tret = ad->handleType();
assert(tret);
tret = tret->addStorageClass(storage_class | sc->stc);
tret = tret->addMod(type->mod);
tf->next = tret;
if (ad->isStructDeclaration())
sc->stc |= STCref;
}
// 'return' on a non-static class member function implies 'scope' as well
if (ad && ad->isClassDeclaration() && (tf->isreturn || sc->stc & STCreturn) && !(sc->stc & STCstatic))
sc->stc |= STCscope;
// If 'this' has no pointers, remove 'scope' as it has no meaning
if (sc->stc & STCscope && ad && ad->isStructDeclaration() && !ad->type->hasPointers())
{
sc->stc &= ~STCscope;
tf->isscope = false;
}
sc->linkage = linkage;
if (!tf->isNaked() && !(isThis() || isNested()))
{
OutBuffer buf;
MODtoBuffer(&buf, tf->mod);
error("without 'this' cannot be %s", buf.peekString());
tf->mod = 0; // remove qualifiers
}
/* Apply const, immutable, wild and shared storage class
* to the function type. Do this before type semantic.
*/
StorageClass stc = storage_class;
if (type->isImmutable())
stc |= STCimmutable;
if (type->isConst())
stc |= STCconst;
if (type->isShared() || storage_class & STCsynchronized)
stc |= STCshared;
if (type->isWild())
stc |= STCwild;
switch (stc & STC_TYPECTOR)
{
case STCimmutable:
case STCimmutable | STCconst:
case STCimmutable | STCwild:
case STCimmutable | STCwild | STCconst:
case STCimmutable | STCshared:
case STCimmutable | STCshared | STCconst:
case STCimmutable | STCshared | STCwild:
case STCimmutable | STCshared | STCwild | STCconst:
// Don't use immutableOf(), as that will do a merge()
type = type->makeImmutable();
break;
case STCconst:
type = type->makeConst();
break;
case STCwild:
type = type->makeWild();
break;
case STCwild | STCconst:
type = type->makeWildConst();
break;
case STCshared:
type = type->makeShared();
break;
case STCshared | STCconst:
type = type->makeSharedConst();
break;
case STCshared | STCwild:
type = type->makeSharedWild();
break;
case STCshared | STCwild | STCconst:
type = type->makeSharedWildConst();
break;
case 0:
break;
default:
assert(0);
}
type = type->semantic(loc, sc);
sc = sc->pop();
}
if (type->ty != Tfunction)
{
if (type->ty != Terror)
{
error("%s must be a function instead of %s", toChars(), type->toChars());
type = Type::terror;
}
errors = true;
return;
}
else
{
// Merge back function attributes into 'originalType'.
// It's used for mangling, ddoc, and json output.
TypeFunction *tfo = originalType->toTypeFunction();
TypeFunction *tfx = type->toTypeFunction();
tfo->mod = tfx->mod;
tfo->isscope = tfx->isscope;
tfo->isscopeinferred = tfx->isscopeinferred;
tfo->isref = tfx->isref;
tfo->isnothrow = tfx->isnothrow;
tfo->isnogc = tfx->isnogc;
tfo->isproperty = tfx->isproperty;
tfo->purity = tfx->purity;
tfo->trust = tfx->trust;
storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR);
}
f = (TypeFunction *)type;
if ((storage_class & STCauto) && !f->isref && !inferRetType)
error("storage class 'auto' has no effect if return type is not inferred");
/* Functions can only be 'scope' if they have a 'this'
*/
if (f->isscope && !isNested() && !ad)
{
error("functions cannot be scope");
}
if (f->isreturn && !needThis() && !isNested())
{
/* Non-static nested functions have a hidden 'this' pointer to which
* the 'return' applies
*/
error("static member has no 'this' to which 'return' can apply");
}
if (isAbstract() && !isVirtual())
{
const char *sfunc;
if (isStatic())
sfunc = "static";
else if (protection.kind == PROTprivate || protection.kind == PROTpackage)
sfunc = protectionToChars(protection.kind);
else
sfunc = "non-virtual";
error("%s functions cannot be abstract", sfunc);
}
if (isOverride() && !isVirtual())
{
PROTKIND kind = prot().kind;
if ((kind == PROTprivate || kind == PROTpackage) && isMember())
error("%s method is not virtual and cannot override", protectionToChars(kind));
else
error("cannot override a non-virtual function");
}
if (isAbstract() && isFinalFunc())
error("cannot be both final and abstract");
id = parent->isInterfaceDeclaration();
if (id)
{
storage_class |= STCabstract;
if (isCtorDeclaration() ||
isPostBlitDeclaration() ||
isDtorDeclaration() ||
isInvariantDeclaration() ||
isNewDeclaration() || isDelete())
error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface %s", id->toChars());
if (fbody && isVirtual())
error("function body only allowed in final functions in interface %s", id->toChars());
}
if (UnionDeclaration *ud = parent->isUnionDeclaration())
{
if (isPostBlitDeclaration() ||
isDtorDeclaration() ||
isInvariantDeclaration())
error("destructors, postblits and invariants are not allowed in union %s", ud->toChars());
}
/* Contracts can only appear without a body when they are virtual interface functions
*/
if (!fbody && (fensure || frequire) && !(id && isVirtual()))
error("in and out contracts require function body");
if (parent->isStructDeclaration())
{
if (isCtorDeclaration())
{
goto Ldone;
}
}
if (ClassDeclaration *cd = parent->isClassDeclaration())
{
if (isCtorDeclaration())
{
goto Ldone;
}
if (storage_class & STCabstract)
cd->isabstract = ABSyes;
// if static function, do not put in vtbl[]
if (!isVirtual())
{
//printf("\tnot virtual\n");
goto Ldone;
}
// Suppress further errors if the return type is an error
if (type->nextOf() == Type::terror)
goto Ldone;
bool may_override = false;
for (size_t i = 0; i < cd->baseclasses->dim; i++)
{
BaseClass *b = (*cd->baseclasses)[i];
ClassDeclaration *cbd = b->type->toBasetype()->isClassHandle();
if (!cbd)
continue;
for (size_t j = 0; j < cbd->vtbl.dim; j++)
{
FuncDeclaration *f2 = cbd->vtbl[j]->isFuncDeclaration();
if (!f2 || f2->ident != ident)
continue;
if (cbd->parent && cbd->parent->isTemplateInstance())
{
if (!f2->functionSemantic())
goto Ldone;
}
may_override = true;
}
}
if (may_override && type->nextOf() == NULL)
{
/* If same name function exists in base class but 'this' is auto return,
* cannot find index of base class's vtbl[] to override.
*/
error("return type inference is not supported if may override base class function");
}
/* Find index of existing function in base class's vtbl[] to override
* (the index will be the same as in cd's current vtbl[])
*/
int vi = cd->baseClass ? findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, (int)cd->baseClass->vtbl.dim)
: -1;
bool doesoverride = false;
switch (vi)
{
case -1:
Lintro:
/* Didn't find one, so
* This is an 'introducing' function which gets a new
* slot in the vtbl[].
*/
// Verify this doesn't override previous final function
if (cd->baseClass)
{
Dsymbol *s = cd->baseClass->search(loc, ident);
if (s)
{
FuncDeclaration *f2 = s->isFuncDeclaration();
if (f2)
{
f2 = f2->overloadExactMatch(type);
if (f2 && f2->isFinalFunc() && f2->prot().kind != PROTprivate)
error("cannot override final function %s", f2->toPrettyChars());
}
}
}
/* These quirky conditions mimic what VC++ appears to do
*/
if (global.params.mscoff && cd->cpp &&
cd->baseClass && cd->baseClass->vtbl.dim)
{
/* if overriding an interface function, then this is not
* introducing and don't put it in the class vtbl[]
*/
interfaceVirtual = overrideInterface();
if (interfaceVirtual)
{
//printf("\tinterface function %s\n", toChars());
cd->vtblFinal.push(this);
goto Linterfaces;
}
}
if (isFinalFunc())
{
// Don't check here, as it may override an interface function
//if (isOverride())
//error("is marked as override, but does not override any function");
cd->vtblFinal.push(this);
}
else
{
//printf("\tintroducing function %s\n", toChars());
introducing = 1;
if (cd->cpp && Target::reverseCppOverloads)
{
// with dmc, overloaded functions are grouped and in reverse order
vtblIndex = (int)cd->vtbl.dim;
for (int i = 0; i < (int)cd->vtbl.dim; i++)
{
if (cd->vtbl[i]->ident == ident && cd->vtbl[i]->parent == parent)
{
vtblIndex = (int)i;
break;
}
}
// shift all existing functions back
for (int i = (int)cd->vtbl.dim; i > vtblIndex; i--)
{
FuncDeclaration *fd = cd->vtbl[i-1]->isFuncDeclaration();
assert(fd);
fd->vtblIndex++;
}
cd->vtbl.insert(vtblIndex, this);
}
else
{
// Append to end of vtbl[]
vi = (int)cd->vtbl.dim;
cd->vtbl.push(this);
vtblIndex = vi;
}
}
break;
case -2:
// can't determine because of forward references
return;
default:
{
FuncDeclaration *fdv = cd->baseClass->vtbl[vi]->isFuncDeclaration();
FuncDeclaration *fdc = cd->vtbl[vi]->isFuncDeclaration();
// This function is covariant with fdv
if (fdc == this)
{
doesoverride = true;
break;
}
if (fdc->toParent() == parent)
{
//printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n",
// vi, this, this->toChars(), this->type->toChars(), this->loc.toChars(),
// fdc, fdc ->toChars(), fdc ->type->toChars(), fdc ->loc.toChars(),
// fdv, fdv ->toChars(), fdv ->type->toChars(), fdv ->loc.toChars());
// fdc overrides fdv exactly, then this introduces new function.
if (fdc->type->mod == fdv->type->mod && this->type->mod != fdv->type->mod)
goto Lintro;
}
// This function overrides fdv
if (fdv->isFinalFunc())
error("cannot override final function %s", fdv->toPrettyChars());
if (!isOverride())
{
if (fdv->isFuture())
{
::deprecation(loc, "@future base class method %s is being overridden by %s; rename the latter",
fdv->toPrettyChars(), toPrettyChars());
// Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[]
goto Lintro;
}
else
{
int vi2 = findVtblIndex(&cd->baseClass->vtbl, (int)cd->baseClass->vtbl.dim, false);
if (vi2 < 0)
// https://issues.dlang.org/show_bug.cgi?id=17349
::deprecation(loc, "cannot implicitly override base class method `%s` with `%s`; add `override` attribute",
fdv->toPrettyChars(), toPrettyChars());
else
::error(loc, "implicitly overriding base class method %s with %s deprecated; add 'override' attribute",
fdv->toPrettyChars(), toPrettyChars());
}
}
doesoverride = true;
if (fdc->toParent() == parent)
{
// If both are mixins, or both are not, then error.
// If either is not, the one that is not overrides the other.
bool thismixin = this->parent->isClassDeclaration() != NULL;
bool fdcmixin = fdc->parent->isClassDeclaration() != NULL;
if (thismixin == fdcmixin)
{
error("multiple overrides of same function");
}
else if (!thismixin) // fdc overrides fdv
{
// this doesn't override any function
break;
}
}
cd->vtbl[vi] = this;
vtblIndex = vi;
/* Remember which functions this overrides
*/
foverrides.push(fdv);
/* This works by whenever this function is called,
* it actually returns tintro, which gets dynamically
* cast to type. But we know that tintro is a base
* of type, so we could optimize it by not doing a
* dynamic cast, but just subtracting the isBaseOf()
* offset if the value is != null.
*/
if (fdv->tintro)
tintro = fdv->tintro;
else if (!type->equals(fdv->type))
{
/* Only need to have a tintro if the vptr
* offsets differ
*/
int offset;
if (fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset))
{
tintro = fdv->type;
}
}
break;
}
}
/* Go through all the interface bases.
* If this function is covariant with any members of those interface
* functions, set the tintro.
*/
Linterfaces:
for (size_t i = 0; i < cd->interfaces.length; i++)
{
BaseClass *b = cd->interfaces.ptr[i];
vi = findVtblIndex((Dsymbols *)&b->sym->vtbl, (int)b->sym->vtbl.dim);
switch (vi)
{
case -1:
break;
case -2:
// can't determine because of forward references
return;
default:
{
FuncDeclaration *fdv = (FuncDeclaration *)b->sym->vtbl[vi];
Type *ti = NULL;
/* Remember which functions this overrides
*/
foverrides.push(fdv);
/* Should we really require 'override' when implementing
* an interface function?
*/
//if (!isOverride())
//warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars());
if (fdv->tintro)
ti = fdv->tintro;
else if (!type->equals(fdv->type))
{
/* Only need to have a tintro if the vptr
* offsets differ
*/
int offset;
if (fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset))
{
ti = fdv->type;
}
}
if (ti)
{
if (tintro)
{
if (!tintro->nextOf()->equals(ti->nextOf()) &&
!tintro->nextOf()->isBaseOf(ti->nextOf(), NULL) &&
!ti->nextOf()->isBaseOf(tintro->nextOf(), NULL))
{
error("incompatible covariant types %s and %s", tintro->toChars(), ti->toChars());
}
}
tintro = ti;
}
goto L2;
}
}
}
if (!doesoverride && isOverride() && (type->nextOf() || !may_override))
{
BaseClass *bc = NULL;
Dsymbol *s = NULL;
for (size_t i = 0; i < cd->baseclasses->dim; i++)
{
bc = (*cd->baseclasses)[i];
s = bc->sym->search_correct(ident);
if (s) break;
}
if (s)
error("does not override any function, did you mean to override '%s%s'?",
bc->sym->isCPPclass() ? "extern (C++) " : "", s->toPrettyChars());
else
error("does not override any function");
}
L2: ;
/* Go through all the interface bases.
* Disallow overriding any final functions in the interface(s).
*/
for (size_t i = 0; i < cd->interfaces.length; i++)
{
BaseClass *b = cd->interfaces.ptr[i];
if (b->sym)
{
Dsymbol *s = search_function(b->sym, ident);
if (s)
{
FuncDeclaration *f2 = s->isFuncDeclaration();
if (f2)
{
f2 = f2->overloadExactMatch(type);
if (f2 && f2->isFinalFunc() && f2->prot().kind != PROTprivate)
error("cannot override final function %s.%s", b->sym->toChars(), f2->toPrettyChars());
}
}
}
}
if (isOverride())
{
if (storage_class & STCdisable)
deprecation("overridden functions cannot be annotated @disable");
if (isDeprecated())
deprecation("deprecated functions cannot be annotated @disable");
}
}
else if (isOverride() && !parent->isTemplateInstance())
error("override only applies to class member functions");
// Reflect this->type to f because it could be changed by findVtblIndex
f = type->toTypeFunction();
/* Do not allow template instances to add virtual functions
* to a class.
*/
if (isVirtual())
{
TemplateInstance *ti = parent->isTemplateInstance();
if (ti)
{
// Take care of nested templates
while (1)
{
TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance();
if (!ti2)
break;
ti = ti2;
}
// If it's a member template
ClassDeclaration *cd = ti->tempdecl->isClassMember();
if (cd)
{
error("cannot use template to add virtual function to class '%s'", cd->toChars());
}
}
}
if (isMain())
checkDmain(); // Check main() parameters and return type
Ldone:
/* Purity and safety can be inferred for some functions by examining
* the function body.
*/
if (canInferAttributes(this, sc))
initInferAttributes(this);
Module::dprogress++;
semanticRun = PASSsemanticdone;
/* Save scope for possible later use (if we need the
* function internals)
*/
_scope = sc->copy();
_scope->setNoFree();
static bool printedMain = false; // semantic might run more than once
if (global.params.verbose && !printedMain)
{
const char *type = isMain() ? "main" : isWinMain() ? "winmain" : isDllMain() ? "dllmain" : (const char *)NULL;
Module *mod = sc->_module;
if (type && mod)
{
printedMain = true;
const char *name = FileName::searchPath(global.path, mod->srcfile->toChars(), true);
message("entry %-10s\t%s", type, name);
}
}
if (fbody && isMain() && sc->_module->isRoot())
Compiler::genCmain(sc);
assert(type->ty != Terror || errors);
}
void FuncDeclaration::semantic2(Scope *sc)
{
if (semanticRun >= PASSsemantic2done)
return;
assert(semanticRun <= PASSsemantic2);
semanticRun = PASSsemantic2;
objc()->setSelector(this, sc);
objc()->validateSelector(this);
if (parent->isClassDeclaration())
{
objc()->checkLinkage(this);
}
}
/****************************************************
* Determine whether an 'out' contract is declared inside
* the given function or any of its overrides.
* Params:
* fd = the function to search
* Returns:
* true found an 'out' contract
* false didn't find one
*/
static bool needsFensure(FuncDeclaration *fd)
{
if (fd->fensure)
return true;
for (size_t i = 0; i < fd->foverrides.dim; i++)
{
FuncDeclaration *fdv = fd->foverrides[i];
if (fdv->fensure)
return true;
if (needsFensure(fdv))
return true;
}
return false;
}
/****************************************************
* Rewrite contracts as nested functions, then call them. Doing it as nested
* functions means that overriding functions can call them.
* Params:
* fd = the function to rewrite contracts for
*/
static void buildEnsureRequire(FuncDeclaration *fdx)
{
if (!fdx->isVirtual())
return;
TypeFunction *f = (TypeFunction *)fdx->type;
if (fdx->frequire)
{
/* in { ... }
* becomes:
* void __require() { ... }
* __require();
*/
Loc loc = fdx->frequire->loc;
TypeFunction *tf = new TypeFunction(NULL, Type::tvoid, 0, LINKd);
tf->isnothrow = f->isnothrow;
tf->isnogc = f->isnogc;
tf->purity = f->purity;
tf->trust = f->trust;
FuncDeclaration *fd = new FuncDeclaration(loc, loc,
Id::require, STCundefined, tf);
fd->fbody = fdx->frequire;
Statement *s1 = new ExpStatement(loc, fd);
Expression *e = new CallExp(loc, new VarExp(loc, fd, false), (Expressions *)NULL);
Statement *s2 = new ExpStatement(loc, e);
fdx->frequire = new CompoundStatement(loc, s1, s2);
fdx->fdrequire = fd;
}
if (!fdx->outId && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid)
fdx->outId = Id::result; // provide a default
if (fdx->fensure)
{
/* out (result) { ... }
* becomes:
* void __ensure(ref tret result) { ... }
* __ensure(result);
*/
Loc loc = fdx->fensure->loc;
Parameters *fparams = new Parameters();
Parameter *p = NULL;
if (fdx->outId)
{
p = new Parameter(STCref | STCconst, f->nextOf(), fdx->outId, NULL);
fparams->push(p);
}
TypeFunction *tf = new TypeFunction(fparams, Type::tvoid, 0, LINKd);
tf->isnothrow = f->isnothrow;
tf->isnogc = f->isnogc;
tf->purity = f->purity;
tf->trust = f->trust;
FuncDeclaration *fd = new FuncDeclaration(loc, loc,
Id::ensure, STCundefined, tf);
fd->fbody = fdx->fensure;
Statement *s1 = new ExpStatement(loc, fd);
Expression *eresult = NULL;
if (fdx->outId)
eresult = new IdentifierExp(loc, fdx->outId);
Expression *e = new CallExp(loc, new VarExp(loc, fd, false), eresult);
Statement *s2 = new ExpStatement(loc, e);
fdx->fensure = new CompoundStatement(loc, s1, s2);
fdx->fdensure = fd;
}
}
/* 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()));
}
// Do the semantic analysis on the internals of the function.
void FuncDeclaration::semantic3(Scope *sc)
{
VarDeclaration *_arguments = NULL;
if (!parent)
{
if (global.errors)
return;
//printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc);
assert(0);
}
if (errors || isError(parent))
{
errors = true;
return;
}
//printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", parent->toChars(), toChars(), this, sc, loc.toChars());
//fflush(stdout);
//printf("storage class = x%x %x\n", sc->stc, storage_class);
//{ static int x; if (++x == 2) *(char*)0=0; }
//printf("\tlinkage = %d\n", sc->linkage);
if (ident == Id::assign && !inuse)
{
if (storage_class & STCinference)
{
/* Bugzilla 15044: For generated opAssign function, any errors
* from its body need to be gagged.
*/
unsigned oldErrors = global.startGagging();
++inuse;
semantic3(sc);
--inuse;
if (global.endGagging(oldErrors)) // if errors happened
{
// Disable generated opAssign, because some members forbid identity assignment.
storage_class |= STCdisable;
fbody = NULL; // remove fbody which contains the error
semantic3Errors = false;
}
return;
}
}
//printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract));
if (semanticRun >= PASSsemantic3)
return;
semanticRun = PASSsemantic3;
semantic3Errors = false;
if (!type || type->ty != Tfunction)
return;
TypeFunction *f = (TypeFunction *)type;
if (!inferRetType && f->next->ty == Terror)
return;
if (!fbody && inferRetType && !f->next)
{
error("has no function body with return type inference");
return;
}
unsigned oldErrors = global.errors;
if (frequire)
{
for (size_t i = 0; i < foverrides.dim; i++)
{
FuncDeclaration *fdv = foverrides[i];
if (fdv->fbody && !fdv->frequire)
{
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.
bool needEnsure = needsFensure(this);
if (fbody || frequire || needEnsure)
{
/* Symbol table into which we place parameters and nested functions,
* solely to diagnose name collisions.
*/
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 = loc;
ss->endlinnum = endloc.linnum;
Scope *sc2 = sc->push(ss);
sc2->func = this;
sc2->parent = this;
sc2->callSuper = 0;
sc2->sbreak = NULL;
sc2->scontinue = NULL;
sc2->sw = NULL;
sc2->fes = 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(PROTpublic);
sc2->explicitProtection = 0;
sc2->aligndecl = NULL;
if (this->ident != Id::require && this->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 -
* isThis() returns true and isNested() returns false.
*/
if (FuncLiteralDeclaration *fld = isFuncLiteralDeclaration())
{
if (AggregateDeclaration *ad = isMember2())
{
if (!sc->intypeof)
{
if (fld->tok == TOKdelegate)
error("cannot be %s members", ad->kind());
else
fld->tok = TOKfunction;
}
else
{
if (fld->tok != TOKfunction)
fld->tok = TOKdelegate;
}
assert(!isNested());
}
}
// Declare 'this'
AggregateDeclaration *ad = isThis();
vthis = declareThis(sc2, ad);
//printf("[%s] ad = %p vthis = %p\n", loc.toChars(), ad, vthis);
//if (vthis) printf("\tvthis->type = %s\n", vthis->type->toChars());
// Declare hidden variable _arguments[] and _argptr
if (f->varargs == 1)
{
if (f->linkage == LINKd)
{
// Variadic arguments depend on Typeinfo being defined
if (!global.params.useTypeInfo || !Type::dtypeinfo || !Type::typeinfotypelist)
{
if (!global.params.useTypeInfo)
error("D-style variadic functions cannot be used with -betterC");
else if (!Type::typeinfotypelist)
error("`object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions");
else
error("`object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions");
fatal();
}
// Declare _arguments[]
v_arguments = new VarDeclaration(Loc(), Type::typeinfotypelist->type, Id::_arguments_typeinfo, NULL);
v_arguments->storage_class |= STCtemp | STCparameter;
v_arguments->semantic(sc2);
sc2->insert(v_arguments);
v_arguments->parent = this;
//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;
_arguments->semantic(sc2);
sc2->insert(_arguments);
_arguments->parent = this;
}
if (f->linkage == LINKd || (f->parameters && Parameter::dim(f->parameters)))
{
// Declare _argptr
Type *t = Type::tvalist;
v_argptr = new VarDeclaration(Loc(), t, Id::_argptr, NULL);
v_argptr->storage_class |= STCtemp;
v_argptr->semantic(sc2);
sc2->insert(v_argptr);
v_argptr->parent = this;
}
}
/* Declare all the function parameters as variables
* and install them in parameters[]
*/
size_t nparams = Parameter::dim(f->parameters);
if (nparams)
{
/* parameters[] has all the tuples removed, as the back end
* doesn't know about tuples
*/
parameters = new VarDeclarations();
parameters->reserve(nparams);
for (size_t i = 0; i < nparams; i++)
{
Parameter *fparam = Parameter::getNth(f->parameters, 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(loc, vtype, id, NULL);
//printf("declaring parameter %s of type %s\n", v->toChars(), v->type->toChars());
stc |= STCparameter;
if (f->varargs == 2 && i + 1 == nparams)
stc |= STCvariadic;
if (flags & FUNCFLAGinferScope && !(fparam->storageClass & STCscope))
stc |= STCmaybescope;
stc |= fparam->storageClass & (STCin | STCout | STCref | STCreturn | STCscope | STClazy | STCfinal | STC_TYPECTOR | STCnodtor);
v->storage_class = stc;
v->semantic(sc2);
if (!sc2->insert(v))
error("parameter %s.%s is already defined", toChars(), v->toChars());
else
parameters->push(v);
localsymtab->insert(v);
v->parent = this;
}
}
// Declare the tuple symbols and put them in the symbol table,
// but not in parameters[].
if (f->parameters)
{
for (size_t i = 0; i < f->parameters->dim; i++)
{
Parameter *fparam = (*f->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(loc, fparam->ident, exps);
//printf("declaring tuple %s\n", v->toChars());
v->isexp = true;
if (!sc2->insert(v))
error("parameter %s.%s is already defined", toChars(), v->toChars());
localsymtab->insert(v);
v->parent = this;
}
}
}
// Precondition invariant
Statement *fpreinv = NULL;
if (addPreInvariant())
{
Expression *e = addInvariant(loc, sc, ad, vthis, isDtorDeclaration() != NULL);
if (e)
fpreinv = new ExpStatement(Loc(), e);
}
// Postcondition invariant
Statement *fpostinv = NULL;
if (addPostInvariant())
{
Expression *e = addInvariant(loc, sc, ad, vthis, isCtorDeclaration() != NULL);
if (e)
fpostinv = new ExpStatement(Loc(), e);
}
// Pre/Postcondition contract
if (!fbody)
buildEnsureRequire(this);
Scope *scout = NULL;
if (needEnsure || addPostInvariant())
{
if ((needEnsure && global.params.useOut) || fpostinv)
{
returnLabel = new LabelDsymbol(Id::returnLabel);
}
// scope of out contract (need for vresult->semantic)
ScopeDsymbol *sym = new ScopeDsymbol();
sym->parent = sc2->scopesym;
sym->loc = loc;
sym->endlinnum = endloc.linnum;
scout = sc2->push(sym);
}
if (fbody)
{
ScopeDsymbol *sym = new ScopeDsymbol();
sym->parent = sc2->scopesym;
sym->loc = loc;
sym->endlinnum = endloc.linnum;
sc2 = sc2->push(sym);
AggregateDeclaration *ad2 = isMember2();
/* If this is a class constructor
*/
if (ad2 && isCtorDeclaration())
{
allocFieldinit(sc2, ad2->fields.dim);
for (size_t i = 0; i < ad2->fields.dim; i++)
{
VarDeclaration *v = ad2->fields[i];
v->ctorinit = 0;
}
}
if (!inferRetType && retStyle(f) != RETstack)
nrvo_can = 0;
bool inferRef = (f->isref && (storage_class & STCauto));
fbody = ::semantic(fbody, sc2);
if (!fbody)
fbody = new CompoundStatement(Loc(), new Statements());
if (naked)
{
fpreinv = NULL; // can't accommodate with no stack frame
fpostinv = NULL;
}
assert(type == f ||
(type->ty == Tfunction &&
f->purity == PUREimpure &&
((TypeFunction *)type)->purity >= PUREfwdref));
f = (TypeFunction *)type;
if (inferRetType)
{
// If no return type inferred yet, then infer a void
if (!f->next)
f->next = Type::tvoid;
if (f->checkRetType(loc))
fbody = new ErrorStatement();
}
if (global.params.vcomplex && f->next != NULL)
f->next->checkComplexTransition(loc);
if (returns && !fbody->isErrorStatement())
{
for (size_t i = 0; i < returns->dim; )
{
Expression *exp = (*returns)[i]->exp;
if (exp->op == TOKvar && ((VarExp *)exp)->var == vresult)
{
if (addReturn0(this))
exp->type = Type::tint32;
else
exp->type = f->next;
// Remove `return vresult;` from returns
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 (storage_class & STCauto)
storage_class &= ~STCauto;
}
if (retStyle(f) != RETstack)
nrvo_can = 0;
if (fbody->isErrorStatement())
;
else if (isStaticCtorDeclaration())
{
/* It's a static constructor. Ensure that all
* ctor consts were initialized.
*/
ScopeDsymbol *pd = toParent()->isScopeDsymbol();
for (size_t i = 0; i < pd->members->dim; i++)
{
Dsymbol *s = (*pd->members)[i];
s->checkCtorConstInit();
}
}
else if (ad2 && 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.dim; 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)
error("missing initializer for %s field %s", MODtoChars(v->type->mod), v->toChars());
else if (v->storage_class & STCnodefaultctor)
::error(loc, "field %s must be initialized in constructor", v->toChars());
else if (v->type->needsNested())
::error(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))
{
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, vthis->type, NULL, 1);
if (!fd)
{
error("no match for implicit super() call in constructor");
}
else if (fd->storage_class & STCdisable)
{
error("cannot call super() implicitly because it is annotated with @disable");
}
else
{
Expression *e1 = new SuperExp(Loc());
Expression *e = new CallExp(Loc(), e1);
e = ::semantic(e, sc2);
Statement *s = new ExpStatement(Loc(), e);
fbody = new CompoundStatement(Loc(), s, 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.
*/
buildEnsureRequire(this);
int blockexit = BEnone;
if (!fbody->isErrorStatement())
{
// Check for errors related to 'nothrow'.
unsigned int nothrowErrors = global.errors;
blockexit = blockExit(fbody, this, f->isnothrow);
if (f->isnothrow && (global.errors != nothrowErrors))
::error(loc, "nothrow %s '%s' may throw", kind(), toPrettyChars());
if (flags & FUNCFLAGnothrowInprocess)
{
if (type == f) f = (TypeFunction *)f->copy();
f->isnothrow = !(blockexit & BEthrow);
}
}
if (fbody->isErrorStatement())
;
else if (ad2 && isCtorDeclaration())
{
/* Append:
* return this;
* to function body
*/
if (blockexit & BEfallthru)
{
Statement *s = new ReturnStatement(loc, NULL);
s = ::semantic(s, sc2);
fbody = new CompoundStatement(loc, fbody, s);
hasReturnExp |= (hasReturnExp & 1 ? 16 : 1);
}
}
else if (fes)
{
// For foreach(){} body, append a return 0;
if (blockexit & BEfallthru)
{
Expression *e = new IntegerExp(0);
Statement *s = new ReturnStatement(Loc(), e);
fbody = new CompoundStatement(Loc(), fbody, s);
hasReturnExp |= (hasReturnExp & 1 ? 16 : 1);
}
assert(!returnLabel);
}
else
{
const bool inlineAsm = (hasReturnExp & 8) != 0;
if ((blockexit & BEfallthru) && f->next->ty != Tvoid && !inlineAsm)
{
Expression *e;
if (!hasReturnExp)
error("has no return statement, but is expected to return a value of type %s", f->next->toChars());
else
error("no return exp; or assert(0); at end of function");
if (global.params.useAssert &&
!global.params.useInline)
{
/* Add an assert(0, msg); where the missing return
* should be.
*/
e = new AssertExp(
endloc,
new IntegerExp(0),
new StringExp(loc, const_cast<char *>("missing return expression"))
);
}
else
e = new HaltExp(endloc);
e = new CommaExp(Loc(), e, f->next->defaultInit());
e = ::semantic(e, sc2);
Statement *s = new ExpStatement(Loc(), e);
fbody = new CompoundStatement(Loc(), fbody, s);
}
}
if (returns)
{
bool implicit0 = addReturn0(this);
Type *tret = implicit0 ? Type::tint32 : f->next;
assert(tret->ty != Tvoid);
if (vresult || returnLabel)
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 < returns->dim; i++)
{
ReturnStatement *rs = (*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) &&
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 (!nrvo_can)
exp = doCopyOrMove(sc2, exp);
if (tret->hasPointers())
checkReturnEscape(sc2, exp, false);
}
exp = checkGC(sc2, exp);
if (vresult)
{
// Create: return vresult = exp;
exp = new BlitExp(rs->loc, vresult, exp);
exp->type = vresult->type;
if (rs->caseDim)
exp = Expression::combine(exp, new IntegerExp(rs->caseDim));
}
else if (tintro && !tret->equals(tintro->nextOf()))
{
exp = exp->implicitCastTo(sc2, tintro->nextOf());
}
rs->exp = exp;
}
}
if (nrvo_var || returnLabel)
{
NrvoWalker nw;
nw.fd = this;
nw.sc = sc2;
nw.visitStmt(fbody);
}
sc2 = sc2->pop();
}
frequire = mergeFrequire(frequire);
fensure = mergeFensure(fensure, outId);
Statement *freq = frequire;
Statement *fens = 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 = loc;
sym->endlinnum = endloc.linnum;
sc2 = sc2->push(sym);
sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPErequire;
// BUG: need to error if accessing out parameters
// BUG: need to treat parameters as const
// BUG: need to disallow returns and throws
// BUG: verify that all in and ref parameters are read
freq = ::semantic(freq, sc2);
blockExit(freq, this, false);
sc2 = sc2->pop();
if (!global.params.useIn)
freq = NULL;
}
if (fens)
{
/* fensure is composed of the [out] contracts
*/
if (f->next->ty == Tvoid && outId)
error("void functions have no result");
sc2 = scout; //push
sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPEensure;
// BUG: need to treat parameters as const
// BUG: need to disallow returns and throws
if (fensure && f->next->ty != Tvoid)
buildResultVar(scout, f->next);
fens = ::semantic(fens, sc2);
blockExit(fens, this, false);
sc2 = sc2->pop();
if (!global.params.useOut)
fens = NULL;
}
if (fbody && fbody->isErrorStatement())
;
else
{
Statements *a = new Statements();
// Merge in initialization of 'out' parameters
if (parameters)
{
for (size_t i = 0; i < parameters->dim; i++)
{
VarDeclaration *v = (*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 (v_argptr)
{
// Handled in FuncDeclaration::toObjFile
v_argptr->_init = new VoidInitializer(loc);
}
if (_arguments)
{
/* Advance to elements[] member of TypeInfo_Tuple with:
* _arguments = v_arguments.elements;
*/
Expression *e = new VarExp(Loc(), v_arguments);
e = new DotIdExp(Loc(), e, Id::elements);
e = new ConstructExp(Loc(), _arguments, e);
e = ::semantic(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 (fbody)
a->push(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);
returnLabel->statement = ls;
a->push(returnLabel->statement);
if (f->next->ty != Tvoid && vresult)
{
// Create: return vresult;
Expression *e = new VarExp(Loc(), vresult);
if (tintro)
{
e = e->implicitCastTo(sc, tintro->nextOf());
e = ::semantic(e, sc);
}
ReturnStatement *s = new ReturnStatement(Loc(), e);
a->push(s);
}
}
if (addReturn0(this))
{
// 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 (parameters)
{
for (size_t i = 0; i < parameters->dim; i++)
{
VarDeclaration *v = (*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 = ::semantic(s, sc2);
bool isnothrow = f->isnothrow & !(flags & FUNCFLAGnothrowInprocess);
int blockexit = blockExit(s, this, isnothrow);
if (f->isnothrow && isnothrow && blockexit & BEthrow)
::error(loc, "nothrow %s '%s' may throw", kind(), toPrettyChars());
if (flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow)
f->isnothrow = false;
if (blockExit(sbody, this, 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
flags &= ~FUNCFLAGnothrowInprocess;
if (isSynchronized())
{
/* Wrap the entire function body in a synchronized statement
*/
ClassDeclaration *cd = isThis() ? isThis()->isClassDeclaration() : parent->isClassDeclaration();
if (cd)
{
if (!global.params.is64bit &&
global.params.isWindows &&
!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 (isStatic())
{
// The monitor is in the ClassInfo
vsync = new DotIdExp(loc, resolve(loc, sc2, cd, false), Id::classinfo);
}
else
{
// 'this' is the monitor
vsync = new VarExp(loc, vthis);
}
sbody = new PeelStatement(sbody); // don't redo semantic()
sbody = new SynchronizedStatement(loc, vsync, sbody);
sbody = ::semantic(sbody, sc2);
}
}
else
{
error("synchronized function %s must be a member of a class", toChars());
}
}
// If declaration has no body, don't set sbody to prevent incorrect codegen.
InterfaceDeclaration *id = parent->isInterfaceDeclaration();
if (fbody || (id && (fdensure || fdrequire) && isVirtual()))
fbody = sbody;
}
// Fix up forward-referenced gotos
if (gotos)
{
for (size_t i = 0; i < gotos->dim; ++i)
{
(*gotos)[i]->checkLabel();
}
}
if (naked && (fensure || frequire))
error("naked assembly functions with contracts are not supported");
sc2->callSuper = 0;
sc2->pop();
}
if (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 (flags & FUNCFLAGpurityInprocess)
{
flags &= ~FUNCFLAGpurityInprocess;
if (type == f)
f = (TypeFunction *)f->copy();
f->purity = PUREfwdref;
}
if (flags & FUNCFLAGsafetyInprocess)
{
flags &= ~FUNCFLAGsafetyInprocess;
if (type == f)
f = (TypeFunction *)f->copy();
f->trust = TRUSTsafe;
}
if (flags & FUNCFLAGnogcInprocess)
{
flags &= ~FUNCFLAGnogcInprocess;
if (type == f)
f = (TypeFunction *)f->copy();
f->isnogc = true;
}
if (flags & FUNCFLAGreturnInprocess)
{
flags &= ~FUNCFLAGreturnInprocess;
if (storage_class & STCreturn)
{
if (type == f)
f = (TypeFunction *)f->copy();
f->isreturn = true;
}
}
flags &= ~FUNCFLAGinferScope;
// Infer STCscope
if (parameters)
{
size_t nfparams = Parameter::dim(f->parameters);
assert(nfparams == parameters->dim);
for (size_t u = 0; u < parameters->dim; u++)
{
VarDeclaration *v = (*parameters)[u];
if (v->storage_class & STCmaybescope)
{
//printf("Inferring scope for %s\n", v->toChars());
Parameter *p = Parameter::getNth(f->parameters, u);
v->storage_class &= ~STCmaybescope;
v->storage_class |= STCscope | STCscopeinferred;
p->storageClass |= STCscope | STCscopeinferred;
assert(!(p->storageClass & STCmaybescope));
}
}
}
if (vthis && vthis->storage_class & STCmaybescope)
{
vthis->storage_class &= ~STCmaybescope;
vthis->storage_class |= STCscope | STCscopeinferred;
f->isscope = true;
f->isscopeinferred = true;
}
// reset deco to apply inference result to mangled name
if (f != type)
f->deco = NULL;
// Do semantic type AFTER pure/nothrow inference.
if (!f->deco && ident != Id::xopEquals && ident != Id::xopCmp)
{
sc = sc->push();
if (isCtorDeclaration()) // Bugzilla #15665
sc->flags |= SCOPEctor;
sc->stc = 0;
sc->linkage = linkage; // Bugzilla 8496
type = f->semantic(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.
*/
semanticRun = PASSsemantic3done;
semantic3Errors = (global.errors != oldErrors) || (fbody && fbody->isErrorStatement());
if (type->ty == Terror)
errors = true;
//printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars());
//fflush(stdout);
}
/****************************************************
* Resolve forward reference of function signature -
* parameter types, return type, and attributes.
* Returns false if any errors exist in the signature.
*/
bool FuncDeclaration::functionSemantic()
{
if (!_scope)
return !errors;
if (!originalType) // semantic not yet run
{
TemplateInstance *spec = isSpeculative();
unsigned olderrs = global.errors;
unsigned oldgag = global.gag;
if (global.gag && !spec)
global.gag = 0;
semantic(_scope);
global.gag = oldgag;
if (spec && global.errors != olderrs)
spec->errors = (global.errors - olderrs != 0);
if (olderrs != global.errors) // if errors compiling this function
return false;
}
// if inferring return type, sematic3 needs to be run
// - When the function body contains any errors, we cannot assume
// the inferred return type is valid.
// So, the body errors should become the function signature error.
if (inferRetType && type && !type->nextOf())
return functionSemantic3();
TemplateInstance *ti;
if (isInstantiated() && !isVirtualMethod() &&
((ti = parent->isTemplateInstance()) == NULL || ti->isTemplateMixin() || ti->tempdecl->ident == ident))
{
AggregateDeclaration *ad = isMember2();
if (ad && ad->sizeok != SIZEOKdone)
{
/* Currently dmd cannot resolve forward references per methods,
* then setting SIZOKfwd is too conservative and would break existing code.
* So, just stop method attributes inference until ad->semantic() done.
*/
//ad->sizeok = SIZEOKfwd;
}
else
return functionSemantic3() || !errors;
}
if (storage_class & STCinference)
return functionSemantic3() || !errors;
return !errors;
}
/****************************************************
* Resolve forward reference of function body.
* Returns false if any errors exist in the body.
*/
bool FuncDeclaration::functionSemantic3()
{
if (semanticRun < PASSsemantic3 && _scope)
{
/* Forward reference - we need to run semantic3 on this function.
* If errors are gagged, and it's not part of a template instance,
* we need to temporarily ungag errors.
*/
TemplateInstance *spec = isSpeculative();
unsigned olderrs = global.errors;
unsigned oldgag = global.gag;
if (global.gag && !spec)
global.gag = 0;
semantic3(_scope);
global.gag = oldgag;
// If it is a speculatively-instantiated template, and errors occur,
// we need to mark the template as having errors.
if (spec && global.errors != olderrs)
spec->errors = (global.errors - olderrs != 0);
if (olderrs != global.errors) // if errors compiling this function
return false;
}
return !errors && !semantic3Errors;
}
/****************************************************
* Check that this function type is properly resolved.
* If not, report "forward reference error" and return true.
*/
bool FuncDeclaration::checkForwardRef(Loc loc)
{
if (!functionSemantic())
return true;
/* No deco means the functionSemantic() call could not resolve
* forward referenes in the type of this function.
*/
if (!type->deco)
{
bool inSemantic3 = (inferRetType && semanticRun >= PASSsemantic3);
::error(loc, "forward reference to %s'%s'",
(inSemantic3 ? "inferred return type of function " : ""),
toChars());
return true;
}
return false;
}
VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad)
{
if (ad)
{
VarDeclaration *v;
{
//printf("declareThis() %s\n", toChars());
Type *thandle = ad->handleType();
assert(thandle);
thandle = thandle->addMod(type->mod);
thandle = thandle->addStorageClass(storage_class);
v = new ThisDeclaration(loc, thandle);
v->storage_class |= STCparameter;
if (thandle->ty == Tstruct)
{
v->storage_class |= STCref;
// if member function is marked 'inout', then 'this' is 'return ref'
if (type->ty == Tfunction && ((TypeFunction *)type)->iswild & 2)
v->storage_class |= STCreturn;
}
if (type->ty == Tfunction)
{
TypeFunction *tf = (TypeFunction *)type;
if (tf->isreturn)
v->storage_class |= STCreturn;
if (tf->isscope)
v->storage_class |= STCscope;
}
if (flags & FUNCFLAGinferScope && !(v->storage_class & STCscope))
v->storage_class |= STCmaybescope;
v->semantic(sc);
if (!sc->insert(v))
assert(0);
v->parent = this;
return v;
}
}
else if (isNested())
{
/* The 'this' for a nested function is the link to the
* enclosing function's stack frame.
* Note that nested functions and member functions are disjoint.
*/
VarDeclaration *v = new ThisDeclaration(loc, Type::tvoid->pointerTo());
v->storage_class |= STCparameter;
if (type->ty == Tfunction)
{
TypeFunction *tf = (TypeFunction *)type;
if (tf->isreturn)
v->storage_class |= STCreturn;
if (tf->isscope)
v->storage_class |= STCscope;
}
if (flags & FUNCFLAGinferScope && !(v->storage_class & STCscope))
v->storage_class |= STCmaybescope;
v->semantic(sc);
if (!sc->insert(v))
assert(0);
v->parent = this;
return v;
}
return NULL;
}
bool FuncDeclaration::equals(RootObject *o)
{
if (this == o)
return true;
Dsymbol *s = isDsymbol(o);
if (s)
{
FuncDeclaration *fd1 = this;
FuncDeclaration *fd2 = s->isFuncDeclaration();
if (!fd2)
return false;
FuncAliasDeclaration *fa1 = fd1->isFuncAliasDeclaration();
FuncAliasDeclaration *fa2 = fd2->isFuncAliasDeclaration();
if (fa1 && fa2)
{
return fa1->toAliasFunc()->equals(fa2->toAliasFunc()) &&
fa1->hasOverloads == fa2->hasOverloads;
}
if (fa1 && (fd1 = fa1->toAliasFunc())->isUnique() && !fa1->hasOverloads)
fa1 = NULL;
if (fa2 && (fd2 = fa2->toAliasFunc())->isUnique() && !fa2->hasOverloads)
fa2 = NULL;
if ((fa1 != NULL) != (fa2 != NULL))
return false;
return fd1->toParent()->equals(fd2->toParent()) &&
fd1->ident->equals(fd2->ident) && fd1->type->equals(fd2->type);
}
return false;
}
/****************************************************
* Declare result variable lazily.
*/
void FuncDeclaration::buildResultVar(Scope *sc, Type *tret)
{
if (!vresult)
{
Loc loc = fensure ? fensure->loc : this->loc;
/* If inferRetType is true, tret may not be a correct return type yet.
* So, in here it may be a temporary type for vresult, and after
* fbody->semantic() running, vresult->type might be modified.
*/
vresult = new VarDeclaration(loc, tret, outId ? outId : Id::result, NULL);
vresult->storage_class |= STCnodtor;
if (outId == Id::result)
vresult->storage_class |= STCtemp;
if (!isVirtual())
vresult->storage_class |= STCconst;
vresult->storage_class |= STCresult;
// set before the semantic() for checkNestedReference()
vresult->parent = this;
}
if (sc && vresult->semanticRun == PASSinit)
{
TypeFunction *tf = type->toTypeFunction();
if (tf->isref)
vresult->storage_class |= STCref;
vresult->type = tret;
vresult->semantic(sc);
if (!sc->insert(vresult))
error("out result %s is already defined", vresult->toChars());
assert(vresult->parent == this);
}
}
/****************************************************
* Merge into this function the 'in' contracts of all it overrides.
* 'in's are OR'd together, i.e. only one of them needs to pass.
*/
Statement *FuncDeclaration::mergeFrequire(Statement *sf)
{
/* If a base function and its override both have an IN contract, then
* only one of them needs to succeed. This is done by generating:
*
* void derived.in() {
* try {
* base.in();
* }
* catch () {
* ... body of derived.in() ...
* }
* }
*
* So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid.
* If base.in() throws, then derived.in()'s body is executed.
*/
/* Implementing this is done by having the overriding function call
* nested functions (the fdrequire functions) nested inside the overridden
* function. This requires that the stack layout of the calling function's
* parameters and 'this' pointer be in the same place (as the nested
* function refers to them).
* This is easy for the parameters, as they are all on the stack in the same
* place by definition, since it's an overriding function. The problem is
* getting the 'this' pointer in the same place, since it is a local variable.
* We did some hacks in the code generator to make this happen:
* 1. always generate exception handler frame, or at least leave space for it
* in the frame (Windows 32 SEH only)
* 2. always generate an EBP style frame
* 3. since 'this' is passed in a register that is subsequently copied into
* a stack local, allocate that local immediately following the exception
* handler block, so it is always at the same offset from EBP.
*/
for (size_t i = 0; i < foverrides.dim; i++)
{
FuncDeclaration *fdv = foverrides[i];
/* The semantic pass on the contracts of the overridden functions must
* be completed before code generation occurs.
* https://issues.dlang.org/show_bug.cgi?id=3602
*/
if (fdv->frequire && fdv->semanticRun != PASSsemantic3done)
{
assert(fdv->_scope);
Scope *sc = fdv->_scope->push();
sc->stc &= ~STCoverride;
fdv->semantic3(sc);
sc->pop();
}
sf = fdv->mergeFrequire(sf);
if (sf && fdv->fdrequire)
{
//printf("fdv->frequire: %s\n", fdv->frequire->toChars());
/* Make the call:
* try { __require(); }
* catch (Throwable) { frequire; }
*/
Expression *eresult = NULL;
Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdrequire, false), eresult);
Statement *s2 = new ExpStatement(loc, e);
Catch *c = new Catch(loc, getThrowable(), NULL, sf);
c->internalCatch = true;
Catches *catches = new Catches();
catches->push(c);
sf = new TryCatchStatement(loc, s2, catches);
}
else
return NULL;
}
return sf;
}
/****************************************************
* Merge into this function the 'out' contracts of all it overrides.
* 'out's are AND'd together, i.e. all of them need to pass.
*/
Statement *FuncDeclaration::mergeFensure(Statement *sf, Identifier *oid)
{
/* Same comments as for mergeFrequire(), except that we take care
* of generating a consistent reference to the 'result' local by
* explicitly passing 'result' to the nested function as a reference
* argument.
* This won't work for the 'this' parameter as it would require changing
* the semantic code for the nested function so that it looks on the parameter
* list for the 'this' pointer, something that would need an unknown amount
* of tweaking of various parts of the compiler that I'd rather leave alone.
*/
for (size_t i = 0; i < foverrides.dim; i++)
{
FuncDeclaration *fdv = foverrides[i];
/* The semantic pass on the contracts of the overridden functions must
* be completed before code generation occurs.
* https://issues.dlang.org/show_bug.cgi?id=3602 and
* https://issues.dlang.org/show_bug.cgi?id=5230
*/
if (needsFensure(fdv) && fdv->semanticRun != PASSsemantic3done)
{
assert(fdv->_scope);
Scope *sc = fdv->_scope->push();
sc->stc &= ~STCoverride;
fdv->semantic3(sc);
sc->pop();
}
sf = fdv->mergeFensure(sf, oid);
if (fdv->fdensure)
{
//printf("fdv->fensure: %s\n", fdv->fensure->toChars());
// Make the call: __ensure(result)
Expression *eresult = NULL;
if (outId)
{
eresult = new IdentifierExp(loc, oid);
Type *t1 = fdv->type->nextOf()->toBasetype();
Type *t2 = this->type->nextOf()->toBasetype();
if (t1->isBaseOf(t2, NULL))
{
/* Making temporary reference variable is necessary
* in covariant return.
* See bugzilla 5204 and 10479.
*/
ExpInitializer *ei = new ExpInitializer(Loc(), eresult);
VarDeclaration *v = new VarDeclaration(Loc(), t1, Identifier::generateId("__covres"), ei);
v->storage_class |= STCtemp;
DeclarationExp *de = new DeclarationExp(Loc(), v);
VarExp *ve = new VarExp(Loc(), v);
eresult = new CommaExp(Loc(), de, ve);
}
}
Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, false), eresult);
Statement *s2 = new ExpStatement(loc, e);
if (sf)
{
sf = new CompoundStatement(sf->loc, s2, sf);
}
else
sf = s2;
}
}
return sf;
}
/****************************************************
* Determine if 'this' overrides fd.
* Return !=0 if it does.
*/
int FuncDeclaration::overrides(FuncDeclaration *fd)
{ int result = 0;
if (fd->ident == ident)
{
int cov = type->covariant(fd->type);
if (cov)
{ ClassDeclaration *cd1 = toParent()->isClassDeclaration();
ClassDeclaration *cd2 = fd->toParent()->isClassDeclaration();
if (cd1 && cd2 && cd2->isBaseOf(cd1, NULL))
result = 1;
}
}
return result;
}
/*************************************************
* Find index of function in vtbl[0..dim] that
* this function overrides.
* Prefer an exact match to a covariant one.
* Params:
* fix17349 = enable fix https://issues.dlang.org/show_bug.cgi?id=17349
* Returns:
* -1 didn't find one
* -2 can't determine because of forward references
*/
int FuncDeclaration::findVtblIndex(Dsymbols *vtbl, int dim, bool fix17349)
{
//printf("findVtblIndex() %s\n", toChars());
FuncDeclaration *mismatch = NULL;
StorageClass mismatchstc = 0;
int mismatchvi = -1;
int exactvi = -1;
int bestvi = -1;
for (int vi = 0; vi < dim; vi++)
{
FuncDeclaration *fdv = (*vtbl)[vi]->isFuncDeclaration();
if (fdv && fdv->ident == ident)
{
if (type->equals(fdv->type)) // if exact match
{
if (fdv->parent->isClassDeclaration())
{
if (fdv->isFuture())
{
bestvi = vi;
continue; // keep looking
}
return vi; // no need to look further
}
if (exactvi >= 0)
{
error("cannot determine overridden function");
return exactvi;
}
exactvi = vi;
bestvi = vi;
continue;
}
StorageClass stc = 0;
int cov = type->covariant(fdv->type, &stc, fix17349);
//printf("\tbaseclass cov = %d\n", cov);
switch (cov)
{
case 0: // types are distinct
break;
case 1:
bestvi = vi; // covariant, but not identical
break; // keep looking for an exact match
case 2:
mismatchvi = vi;
mismatchstc = stc;
mismatch = fdv; // overrides, but is not covariant
break; // keep looking for an exact match
case 3:
return -2; // forward references
default:
assert(0);
}
}
}
if (bestvi == -1 && mismatch)
{
//type->print();
//mismatch->type->print();
//printf("%s %s\n", type->deco, mismatch->type->deco);
//printf("stc = %llx\n", mismatchstc);
if (mismatchstc)
{ // Fix it by modifying the type to add the storage classes
type = type->addStorageClass(mismatchstc