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);
bestvi = mismatchvi;
}
}
return bestvi;
}
/*********************************
* If function a function in a base class,
* return that base class.
* Params:
* cd = class that function is in
* Returns:
* base class if overriding, NULL if not
*/
BaseClass *FuncDeclaration::overrideInterface()
{
ClassDeclaration *cd = parent->isClassDeclaration();
for (size_t i = 0; i < cd->interfaces.length; i++)
{
BaseClass *b = cd->interfaces.ptr[i];
int v = findVtblIndex((Dsymbols *)&b->sym->vtbl, (int)b->sym->vtbl.dim);
if (v >= 0)
return b;
}
return NULL;
}
/****************************************************
* Overload this FuncDeclaration with the new one f.
* Return true if successful; i.e. no conflict.
*/
bool FuncDeclaration::overloadInsert(Dsymbol *s)
{
//printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s->toChars(), toChars());
assert(s != this);
AliasDeclaration *ad = s->isAliasDeclaration();
if (ad)
{
if (overnext)
return overnext->overloadInsert(ad);
if (!ad->aliassym && ad->type->ty != Tident && ad->type->ty != Tinstance)
{
//printf("\tad = '%s'\n", ad->type->toChars());
return false;
}
overnext = ad;
//printf("\ttrue: no conflict\n");
return true;
}
TemplateDeclaration *td = s->isTemplateDeclaration();
if (td)
{
if (!td->funcroot)
td->funcroot = this;
if (overnext)
return overnext->overloadInsert(td);
overnext = td;
return true;
}
FuncDeclaration *fd = s->isFuncDeclaration();
if (!fd)
return false;
if (overnext)
{
td = overnext->isTemplateDeclaration();
if (td)
fd->overloadInsert(td);
else
return overnext->overloadInsert(fd);
}
overnext = fd;
//printf("\ttrue: no conflict\n");
return true;
}
/***************************************************
* Visit each overloaded function/template in turn, and call
* (*fp)(param, s) on it.
* Exit when no more, or (*fp)(param, f) returns nonzero.
* Returns:
* ==0 continue
* !=0 done
*/
int overloadApply(Dsymbol *fstart, void *param, int (*fp)(void *, Dsymbol *))
{
Dsymbol *d;
Dsymbol *next;
for (d = fstart; d; d = next)
{
if (OverDeclaration *od = d->isOverDeclaration())
{
if (od->hasOverloads)
{
if (int r = overloadApply(od->aliassym, param, fp))
return r;
}
else
{
if (int r = (*fp)(param, od->aliassym))
return r;
}
next = od->overnext;
}
else if (FuncAliasDeclaration *fa = d->isFuncAliasDeclaration())
{
if (fa->hasOverloads)
{
if (int r = overloadApply(fa->funcalias, param, fp))
return r;
}
else
{
FuncDeclaration *fd = fa->toAliasFunc();
if (!fd)
{
d->error("is aliased to a function");
break;
}
if (int r = (*fp)(param, fd))
return r;
}
next = fa->overnext;
}
else if (AliasDeclaration *ad = d->isAliasDeclaration())
{
next = ad->toAlias();
if (next == ad)
break;
if (next == fstart)
break;
}
else if (TemplateDeclaration *td = d->isTemplateDeclaration())
{
if (int r = (*fp)(param, td))
return r;
next = td->overnext;
}
else
{
FuncDeclaration *fd = d->isFuncDeclaration();
if (!fd)
{
d->error("is aliased to a function");
break; // BUG: should print error message?
}
if (int r = (*fp)(param, fd))
return r;
next = fd->overnext;
}
}
return 0;
}
/********************************************
* If there are no overloads of function f, return that function,
* otherwise return NULL.
*/
FuncDeclaration *FuncDeclaration::isUnique()
{
struct ParamUnique
{
static int fp(void *param, Dsymbol *s)
{
FuncDeclaration *f = s->isFuncDeclaration();
if (!f)
return 0;
FuncDeclaration **pf = (FuncDeclaration **)param;
if (*pf)
{
*pf = NULL;
return 1; // ambiguous, done
}
else
{
*pf = f;
return 0;
}
}
};
FuncDeclaration *result = NULL;
overloadApply(this, &result, &ParamUnique::fp);
return result;
}
/********************************************
* Find function in overload list that exactly matches t.
*/
FuncDeclaration *FuncDeclaration::overloadExactMatch(Type *t)
{
struct ParamExact
{
Type *t; // type to match
FuncDeclaration *f; // return value
static int fp(void *param, Dsymbol *s)
{
FuncDeclaration *f = s->isFuncDeclaration();
if (!f)
return 0;
ParamExact *p = (ParamExact *)param;
Type *t = p->t;
if (t->equals(f->type))
{
p->f = f;
return 1;
}
/* Allow covariant matches, as long as the return type
* is just a const conversion.
* This allows things like pure functions to match with an impure function type.
*/
if (t->ty == Tfunction)
{ TypeFunction *tf = (TypeFunction *)f->type;
if (tf->covariant(t) == 1 &&
tf->nextOf()->implicitConvTo(t->nextOf()) >= MATCHconst)
{
p->f = f;
return 1;
}
}
return 0;
}
};
ParamExact p;
p.t = t;
p.f = NULL;
overloadApply(this, &p, &ParamExact::fp);
return p.f;
}
void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod)
{
bool bothMutable = ((lhsMod & rhsMod) == 0);
bool sharedMismatch = ((lhsMod ^ rhsMod) & MODshared) != 0;
bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODshared);
if (lhsMod & MODshared)
buf->writestring("shared ");
else if (sharedMismatch && !(lhsMod & MODimmutable))
buf->writestring("non-shared ");
if (bothMutable && sharedMismatchOnly)
{ }
else if (lhsMod & MODimmutable)
buf->writestring("immutable ");
else if (lhsMod & MODconst)
buf->writestring("const ");
else if (lhsMod & MODwild)
buf->writestring("inout ");
else
buf->writestring("mutable ");
}
/********************************************
* Find function in overload list that matches to the 'this' modifier.
* There's four result types.
*
* 1. If the 'tthis' matches only one candidate, it's an "exact match".
* Returns the function and 'hasOverloads' is set to false.
* eg. If 'tthis" is mutable and there's only one mutable method.
* 2. If there's two or more match candidates, but a candidate function will be
* a "better match".
* Returns the better match function but 'hasOverloads' is set to true.
* eg. If 'tthis' is mutable, and there's both mutable and const methods,
* the mutable method will be a better match.
* 3. If there's two or more match candidates, but there's no better match,
* Returns NULL and 'hasOverloads' is set to true to represent "ambiguous match".
* eg. If 'tthis' is mutable, and there's two or more mutable methods.
* 4. If there's no candidates, it's "no match" and returns NULL with error report.
* e.g. If 'tthis' is const but there's no const methods.
*/
FuncDeclaration *FuncDeclaration::overloadModMatch(Loc loc, Type *tthis, bool &hasOverloads)
{
//printf("FuncDeclaration::overloadModMatch('%s')\n", toChars());
Match m;
memset(&m, 0, sizeof(m));
m.last = MATCHnomatch;
struct ParamMod
{
Match *m;
Type *tthis;
static int fp(void *param, Dsymbol *s)
{
if (FuncDeclaration *fd = s->isFuncDeclaration())
return ((ParamMod *)param)->fp(fd);
return 0;
}
int fp(FuncDeclaration *f)
{
if (f == m->lastf) // skip duplicates
return 0;
m->anyf = f;
TypeFunction *tf = f->type->toTypeFunction();
//printf("tf = %s\n", tf->toChars());
MATCH match;
if (tthis) // non-static functions are preferred than static ones
{
if (f->needThis())
match = f->isCtorDeclaration() ? MATCHexact : MODmethodConv(tthis->mod, tf->mod);
else
match = MATCHconst; // keep static funciton in overload candidates
}
else // static functions are preferred than non-static ones
{
if (f->needThis())
match = MATCHconvert;
else
match = MATCHexact;
}
if (match != MATCHnomatch)
{
if (match > m->last) goto LfIsBetter;
if (match < m->last) goto LlastIsBetter;
/* See if one of the matches overrides the other.
*/
if (m->lastf->overrides(f)) goto LlastIsBetter;
if (f->overrides(m->lastf)) goto LfIsBetter;
//printf("\tambiguous\n");
m->nextf = f;
m->count++;
return 0;
LlastIsBetter:
//printf("\tlastbetter\n");
m->count++; // count up
return 0;
LfIsBetter:
//printf("\tisbetter\n");
if (m->last <= MATCHconvert)
{
// clear last secondary matching
m->nextf = NULL;
m->count = 0;
}
m->last = match;
m->lastf = f;
m->count++; // count up
return 0;
}
return 0;
}
};
ParamMod p;
p.m = &m;
p.tthis = tthis;
overloadApply(this, &p, &ParamMod::fp);
if (m.count == 1) // exact match
{
hasOverloads = false;
}
else if (m.count > 1) // better or ambiguous match
{
hasOverloads = true;
}
else // no match
{
hasOverloads = true;
TypeFunction *tf = this->type->toTypeFunction();
assert(tthis);
assert(!MODimplicitConv(tthis->mod, tf->mod)); // modifier mismatch
{
OutBuffer thisBuf, funcBuf;
MODMatchToBuffer(&thisBuf, tthis->mod, tf->mod);
MODMatchToBuffer(&funcBuf, tf->mod, tthis->mod);
::error(loc, "%smethod %s is not callable using a %sobject",
funcBuf.peekString(), this->toPrettyChars(), thisBuf.peekString());
}
}
return m.lastf;
}
/********************************************
* Returns true if function was declared
* directly or indirectly in a unittest block
*/
bool FuncDeclaration::inUnittest()
{
Dsymbol *f = this;
do
{
if (f->isUnitTestDeclaration())
return true;
f = f->toParent();
} while (f);
return false;
}
/********************************************
* find function template root in overload list
*/
TemplateDeclaration *FuncDeclaration::findTemplateDeclRoot()
{
FuncDeclaration *f = this;
while (f && f->overnext)
{
//printf("f->overnext = %p %s\n", f->overnext, f->overnext->toChars());
TemplateDeclaration *td = f->overnext->isTemplateDeclaration();
if (td)
return td;
f = f->overnext->isFuncDeclaration();
}
return NULL;
}
/*************************************
* Determine partial specialization order of 'this' vs g.
* This is very similar to TemplateDeclaration::leastAsSpecialized().
* Returns:
* match 'this' is at least as specialized as g
* 0 g is more specialized than 'this'
*/
MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g)
{
/* This works by calling g() with f()'s parameters, and
* if that is possible, then f() is at least as specialized
* as g() is.
*/
TypeFunction *tf = type->toTypeFunction();
TypeFunction *tg = g->type->toTypeFunction();
size_t nfparams = Parameter::dim(tf->parameters);
/* If both functions have a 'this' pointer, and the mods are not
* the same and g's is not const, then this is less specialized.
*/
if (needThis() && g->needThis() && tf->mod != tg->mod)
{
if (isCtorDeclaration())
{
if (!MODimplicitConv(tg->mod, tf->mod))
return MATCHnomatch;
}
else
{
if (!MODimplicitConv(tf->mod, tg->mod))
return MATCHnomatch;
}
}
/* Create a dummy array of arguments out of the parameters to f()
*/
Expressions args;
args.setDim(nfparams);
for (size_t u = 0; u < nfparams; u++)
{
Parameter *p = Parameter::getNth(tf->parameters, u);
Expression *e;
if (p->storageClass & (STCref | STCout))
{
e = new IdentifierExp(Loc(), p->ident);
e->type = p->type;
}
else
e = p->type->defaultInitLiteral(Loc());
args[u] = e;
}
MATCH m = (MATCH) tg->callMatch(NULL, &args, 1);
if (m > MATCHnomatch)
{
/* A variadic parameter list is less specialized than a
* non-variadic one.
*/
if (tf->varargs && !tg->varargs)
goto L1; // less specialized
return m;
}
L1:
return MATCHnomatch;
}
/// Walk through candidate template overloads and print them in the diagnostics.
struct TemplateCandidateWalker
{
Loc loc;
int numToDisplay; // max num of overloads to print (-v overrides this).
/// Count template overloads.
struct CountWalker
{
int numOverloads;
static int fp(void *param, Dsymbol *)
{
CountWalker *p = (CountWalker *)param;
++(p->numOverloads);
return 0;
}
};
static int fp(void *param, Dsymbol *s)
{
TemplateDeclaration *t = s->isTemplateDeclaration();
if (!t) return 0;
TemplateCandidateWalker *p = (TemplateCandidateWalker *)param;
::errorSupplemental(t->loc, "%s", t->toPrettyChars());
if (!global.params.verbose && --(p->numToDisplay) == 0 && t->overnext)
{
// Too many overloads to sensibly display.
// Just show count of remaining overloads.
CountWalker cw;
cw.numOverloads = 0;
overloadApply(t->overnext, &cw, &CountWalker::fp);
if (cw.numOverloads > 0)
::errorSupplemental(p->loc, "... (%d more, -v to show) ...", cw.numOverloads);
return 1; // stop iterating
}
return 0;
}
};
/// Walk through candidate template overloads and print them in the diagnostics.
struct FuncCandidateWalker
{
Loc loc;
int numToDisplay; // max num of overloads to print (-v overrides this).
/// Count function overloads.
struct CountWalker
{
int numOverloads;
static int fp(void *param, Dsymbol *)
{
CountWalker *p = (CountWalker *)param;
++(p->numOverloads);
return 0;
}
};
static int fp(void *param, Dsymbol *s)
{
FuncDeclaration *fd = s->isFuncDeclaration();
TemplateDeclaration *td = s->isTemplateDeclaration();
if (fd)
{
if (fd->errors || fd->type->ty == Terror)
return 0;
TypeFunction *tf = (TypeFunction *)fd->type;
::errorSupplemental(fd->loc, "%s%s", fd->toPrettyChars(),
parametersTypeToChars(tf->parameters, tf->varargs));
}
else
{
::errorSupplemental(td->loc, "%s", td->toPrettyChars());
}
FuncCandidateWalker *p = (FuncCandidateWalker *)param;
if (global.params.verbose || --(p->numToDisplay) != 0 || !fd)
return 0;
// Too many overloads to sensibly display.
CountWalker cw;
cw.numOverloads = 0;
overloadApply(fd->overnext, &cw, &CountWalker::fp);
if (cw.numOverloads > 0)
::errorSupplemental(p->loc, "... (%d more, -v to show) ...", cw.numOverloads);
return 1; // stop iterating
}
};
/*******************************************
* Given a symbol that could be either a FuncDeclaration or
* a function template, resolve it to a function symbol.
* loc instantiation location
* sc instantiation scope
* tiargs initial list of template arguments
* tthis if !NULL, the 'this' pointer argument
* fargs arguments to function
* flags 1: do not issue error message on no match, just return NULL
* 2: overloadResolve only
*/
FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s,
Objects *tiargs, Type *tthis, Expressions *fargs, int flags)
{
if (!s)
return NULL; // no match
if ((tiargs && arrayObjectIsError(tiargs)) ||
(fargs && arrayObjectIsError((Objects *)fargs)))
{
return NULL;
}
Match m;
memset(&m, 0, sizeof(m));
m.last = MATCHnomatch;
functionResolve(&m, s, loc, sc, tiargs, tthis, fargs);
if (m.last > MATCHnomatch && m.lastf)
{
if (m.count == 1) // exactly one match
{
if (!(flags & 1))
m.lastf->functionSemantic();
return m.lastf;
}
if ((flags & 2) && !tthis && m.lastf->needThis())
{
return m.lastf;
}
}
/* Failed to find a best match.
* Do nothing or print error.
*/
if (m.last <= MATCHnomatch)
{
// error was caused on matched function
if (m.count == 1)
return m.lastf;
// if do not print error messages
if (flags & 1)
return NULL; // no match
}
FuncDeclaration *fd = s->isFuncDeclaration();
OverDeclaration *od = s->isOverDeclaration();
TemplateDeclaration *td = s->isTemplateDeclaration();
if (td && td->funcroot)
s = fd = td->funcroot;
OutBuffer tiargsBuf;
arrayObjectsToBuffer(&tiargsBuf, tiargs);
OutBuffer fargsBuf;
fargsBuf.writeByte('(');
argExpTypesToCBuffer(&fargsBuf, fargs);
fargsBuf.writeByte(')');
if (tthis)
tthis->modToBuffer(&fargsBuf);
const int numOverloadsDisplay = 5; // sensible number to display
if (!m.lastf && !(flags & 1)) // no match
{
if (td && !fd) // all of overloads are templates
{
::error(loc, "%s %s.%s cannot deduce function from argument types !(%s)%s, candidates are:",
td->kind(), td->parent->toPrettyChars(), td->ident->toChars(),
tiargsBuf.peekString(), fargsBuf.peekString());
// Display candidate templates (even if there are no multiple overloads)
TemplateCandidateWalker tcw;
tcw.loc = loc;
tcw.numToDisplay = numOverloadsDisplay;
overloadApply(td, &tcw, &TemplateCandidateWalker::fp);
}
else if (od)
{
::error(loc, "none of the overloads of '%s' are callable using argument types !(%s)%s",
od->ident->toChars(), tiargsBuf.peekString(), fargsBuf.peekString());
}
else
{
assert(fd);
bool hasOverloads = fd->overnext != NULL;
TypeFunction *tf = fd->type->toTypeFunction();
if (tthis && !MODimplicitConv(tthis->mod, tf->mod)) // modifier mismatch
{
OutBuffer thisBuf, funcBuf;
MODMatchToBuffer(&thisBuf, tthis->mod, tf->mod);
MODMatchToBuffer(&funcBuf, tf->mod, tthis->mod);
if (hasOverloads)
::error(loc, "none of the overloads of '%s' are callable using a %sobject, candidates are:",
fd->ident->toChars(), thisBuf.peekString());
else
::error(loc, "%smethod %s is not callable using a %sobject",
funcBuf.peekString(), fd->toPrettyChars(), thisBuf.peekString());
}
else
{
//printf("tf = %s, args = %s\n", tf->deco, (*fargs)[0]->type->deco);
if (hasOverloads)
::error(loc, "none of the overloads of '%s' are callable using argument types %s, candidates are:",
fd->ident->toChars(), fargsBuf.peekString());
else
fd->error(loc, "%s%s is not callable using argument types %s",
parametersTypeToChars(tf->parameters, tf->varargs),
tf->modToChars(),
fargsBuf.peekString());
}
// Display candidate functions
if (hasOverloads)
{
FuncCandidateWalker fcw;
fcw.loc = loc;
fcw.numToDisplay = numOverloadsDisplay;
overloadApply(fd, &fcw, &FuncCandidateWalker::fp);
}
}
}
else if (m.nextf)
{
TypeFunction *tf1 = m.lastf->type->toTypeFunction();
TypeFunction *tf2 = m.nextf->type->toTypeFunction();
const char *lastprms = parametersTypeToChars(tf1->parameters, tf1->varargs);
const char *nextprms = parametersTypeToChars(tf2->parameters, tf2->varargs);
::error(loc, "%s.%s called with argument types %s matches both:\n"
"%s: %s%s\nand:\n%s: %s%s",
s->parent->toPrettyChars(), s->ident->toChars(),
fargsBuf.peekString(),
m.lastf->loc.toChars(), m.lastf->toPrettyChars(), lastprms,
m.nextf->loc.toChars(), m.nextf->toPrettyChars(), nextprms);
}
return NULL;
}
/********************************
* Labels are in a separate scope, one per function.
*/
LabelDsymbol *FuncDeclaration::searchLabel(Identifier *ident)
{ Dsymbol *s;
if (!labtab)
labtab = new DsymbolTable(); // guess we need one
s = labtab->lookup(ident);
if (!s)
{
s = new LabelDsymbol(ident);
labtab->insert(s);
}
return (LabelDsymbol *)s;
}
/*****************************************
* Determine lexical level difference from 'this' to nested function 'fd'.
* Error if this cannot call fd.
* Returns:
* 0 same level
* >0 decrease nesting by number
* -1 increase nesting by 1 (fd is nested within 'this')
* -2 error
*/
int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd)
{
int level;
Dsymbol *s;
Dsymbol *fdparent;
//printf("FuncDeclaration::getLevel(fd = '%s')\n", fd->toChars());
fdparent = fd->toParent2();
if (fdparent == this)
return -1;
s = this;
level = 0;
while (fd != s && fdparent != s->toParent2())
{
//printf("\ts = %s, '%s'\n", s->kind(), s->toChars());
FuncDeclaration *thisfd = s->isFuncDeclaration();
if (thisfd)
{
if (!thisfd->isNested() && !thisfd->vthis && !sc->intypeof)
goto Lerr;
}
else
{
AggregateDeclaration *thiscd = s->isAggregateDeclaration();
if (thiscd)
{
/* AggregateDeclaration::isNested returns true only when
* it has a hidden pointer.
* But, calling the function belongs unrelated lexical scope
* is still allowed inside typeof.
*
* struct Map(alias fun) {
* typeof({ return fun(); }) RetType;
* // No member function makes Map struct 'not nested'.
* }
*/
if (!thiscd->isNested() && !sc->intypeof)
goto Lerr;
}
else
goto Lerr;
}
s = s->toParent2();
assert(s);
level++;
}
return level;
Lerr:
// Don't give error if in template constraint
if (!(sc->flags & SCOPEconstraint))
{
const char *xstatic = isStatic() ? "static " : "";
// better diagnostics for static functions
::error(loc, "%s%s %s cannot access frame of function %s",
xstatic, kind(), toPrettyChars(), fd->toPrettyChars());
return -2;
}
return 1;
}
const char *FuncDeclaration::toPrettyChars(bool QualifyTypes)
{
if (isMain())
return "D main";
else
return Dsymbol::toPrettyChars(QualifyTypes);
}
/** for diagnostics, e.g. 'int foo(int x, int y) pure' */
const char *FuncDeclaration::toFullSignature()
{
OutBuffer buf;
functionToBufferWithIdent(type->toTypeFunction(), &buf, toChars());
return buf.extractString();
}
bool FuncDeclaration::isMain()
{
return ident == Id::main &&
linkage != LINKc && !isMember() && !isNested();
}
bool FuncDeclaration::isCMain()
{
return ident == Id::main &&
linkage == LINKc && !isMember() && !isNested();
}
bool FuncDeclaration::isWinMain()
{
//printf("FuncDeclaration::isWinMain() %s\n", toChars());
return ident == Id::WinMain &&
linkage != LINKc && !isMember();
}
bool FuncDeclaration::isDllMain()
{
return ident == Id::DllMain &&
linkage != LINKc && !isMember();
}
bool FuncDeclaration::isExport() const
{
return protection.kind == PROTexport;
}
bool FuncDeclaration::isImportedSymbol() const
{
//printf("isImportedSymbol()\n");
//printf("protection = %d\n", protection);
return (protection.kind == PROTexport) && !fbody;
}
// Determine if function goes into virtual function pointer table
bool FuncDeclaration::isVirtual()
{
if (toAliasFunc() != this)
return toAliasFunc()->isVirtual();
Dsymbol *p = toParent();
return isMember() &&
!(isStatic() || protection.kind == PROTprivate || protection.kind == PROTpackage) &&
p->isClassDeclaration() &&
!(p->isInterfaceDeclaration() && isFinalFunc());
}
// Determine if a function is pedantically virtual
bool FuncDeclaration::isVirtualMethod()
{
if (toAliasFunc() != this)
return toAliasFunc()->isVirtualMethod();
//printf("FuncDeclaration::isVirtualMethod() %s\n", toChars());
if (!isVirtual())
return false;
// If it's a final method, and does not override anything, then it is not virtual
if (isFinalFunc() && foverrides.dim == 0)
{
return false;
}
return true;
}
bool FuncDeclaration::isFinalFunc()
{
if (toAliasFunc() != this)
return toAliasFunc()->isFinalFunc();
ClassDeclaration *cd;
return isMember() &&
(Declaration::isFinal() ||
((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal));
}
bool FuncDeclaration::isCodeseg() const
{
return true; // functions are always in the code segment
}
bool FuncDeclaration::isOverloadable()
{
return true; // functions can be overloaded
}
PURE FuncDeclaration::isPure()
{
//printf("FuncDeclaration::isPure() '%s'\n", toChars());
TypeFunction *tf = type->toTypeFunction();
if (flags & FUNCFLAGpurityInprocess)
setImpure();
if (tf->purity == PUREfwdref)
tf->purityLevel();
PURE purity = tf->purity;
if (purity > PUREweak && isNested())
purity = PUREweak;
if (purity > PUREweak && needThis())
{
// The attribute of the 'this' reference affects purity strength
if (type->mod & MODimmutable)
;
else if (type->mod & (MODconst | MODwild) && purity >= PUREconst)
purity = PUREconst;
else
purity = PUREweak;
}
tf->purity = purity;
// ^ This rely on the current situation that every FuncDeclaration has a
// unique TypeFunction.
return purity;
}
PURE FuncDeclaration::isPureBypassingInference()
{
if (flags & FUNCFLAGpurityInprocess)
return PUREfwdref;
else
return isPure();
}
/**************************************
* The function is doing something impure,
* so mark it as impure.
* If there's a purity error, return true.
*/
bool FuncDeclaration::setImpure()
{
if (flags & FUNCFLAGpurityInprocess)
{
flags &= ~FUNCFLAGpurityInprocess;
if (fes)
fes->func->setImpure();
}
else if (isPure())
return true;
return false;
}
bool FuncDeclaration::isSafe()
{
if (flags & FUNCFLAGsafetyInprocess)
setUnsafe();
return type->toTypeFunction()->trust == TRUSTsafe;
}
bool FuncDeclaration::isSafeBypassingInference()
{
return !(flags & FUNCFLAGsafetyInprocess) && isSafe();
}
bool FuncDeclaration::isTrusted()
{
if (flags & FUNCFLAGsafetyInprocess)
setUnsafe();
return type->toTypeFunction()->trust == TRUSTtrusted;
}
/**************************************
* The function is doing something unsave,
* so mark it as unsafe.
* If there's a safe error, return true.
*/
bool FuncDeclaration::setUnsafe()
{
if (flags & FUNCFLAGsafetyInprocess)
{
flags &= ~FUNCFLAGsafetyInprocess;
type->toTypeFunction()->trust = TRUSTsystem;
if (fes)
fes->func->setUnsafe();
}
else if (isSafe())
return true;
return false;
}
bool FuncDeclaration::isNogc()
{
if (flags & FUNCFLAGnogcInprocess)
setGC();
return type->toTypeFunction()->isnogc;
}
bool FuncDeclaration::isNogcBypassingInference()
{
return !(flags & FUNCFLAGnogcInprocess) && isNogc();
}
/**************************************
* The function is doing something that may allocate with the GC,
* so mark it as not nogc (not no-how).
* Returns:
* true if function is marked as @nogc, meaning a user error occurred
*/
bool FuncDeclaration::setGC()
{
if (flags & FUNCFLAGnogcInprocess)
{
flags &= ~FUNCFLAGnogcInprocess;
type->toTypeFunction()->isnogc = false;
if (fes)
fes->func->setGC();
}
else if (isNogc())
return true;
return false;
}
/**************************************
* Returns an indirect type one step from t.
*/
Type *getIndirection(Type *t)
{
t = t->baseElemOf();
if (t->ty == Tarray || t->ty == Tpointer)
return t->nextOf()->toBasetype();
if (t->ty == Taarray || t->ty == Tclass)
return t;
if (t->ty == Tstruct)
return t->hasPointers() ? t : NULL; // TODO
// should consider TypeDelegate?
return NULL;
}
/**************************************
* Returns true if memory reachable through a reference B to a value of type tb,
* which has been constructed with a reference A to a value of type ta
* available, can alias memory reachable from A based on the types involved
* (either directly or via any number of indirections).
*
* Note that this relation is not symmetric in the two arguments. For example,
* a const(int) reference can point to a pre-existing int, but not the other
* way round.
*/
bool traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool reversePass = false)
{
Type *source = ta;
Type *target = tb;
if (reversePass)
{
source = tb;
target = ta;
}
if (source->constConv(target))
return true;
else if (target->ty == Tvoid && MODimplicitConv(source->mod, target->mod))
return true;
// No direct match, so try breaking up one of the types (starting with tb).
Type *tbb = tb->toBasetype()->baseElemOf();
if (tbb != tb)
return traverseIndirections(ta, tbb, p, reversePass);
// context date to detect circular look up
struct Ctxt
{
Ctxt *prev;
Type *type;
};
Ctxt *ctxt = (Ctxt *)p;
if (tb->ty == Tclass || tb->ty == Tstruct)
{
for (Ctxt *c = ctxt; c; c = c->prev)
if (tb == c->type) return false;
Ctxt c;
c.prev = ctxt;
c.type = tb;
AggregateDeclaration *sym = tb->toDsymbol(NULL)->isAggregateDeclaration();
for (size_t i = 0; i < sym->fields.dim; i++)
{
VarDeclaration *v = sym->fields[i];
Type *tprmi = v->type->addMod(tb->mod);
//printf("\ttb = %s, tprmi = %s\n", tb->toChars(), tprmi->toChars());
if (traverseIndirections(ta, tprmi, &c, reversePass))
return true;
}
}
else if (tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tpointer)
{
Type *tind = tb->nextOf();
if (traverseIndirections(ta, tind, ctxt, reversePass))
return true;
}
else if (tb->hasPointers())
{
// FIXME: function pointer/delegate types should be considered.
return true;
}
// Still no match, so try breaking up ta if we have note done so yet.
if (!reversePass)
return traverseIndirections(tb, ta, ctxt, true);
return false;
}
/********************************************
* Returns true if the function return value has no indirection
* which comes from the parameters.
*/
bool FuncDeclaration::isolateReturn()
{
TypeFunction *tf = type->toTypeFunction();
assert(tf->next);
Type *treti = tf->next;
treti = tf->isref ? treti : getIndirection(treti);
if (!treti)
return true; // target has no mutable indirection
return parametersIntersect(treti);
}
/********************************************
* Returns true if an object typed t can have indirections
* which come from the parameters.
*/
bool FuncDeclaration::parametersIntersect(Type *t)
{
assert(t);
if (!isPureBypassingInference() || isNested())
return false;
TypeFunction *tf = type->toTypeFunction();
//printf("parametersIntersect(%s) t = %s\n", tf->toChars(), t->toChars());
size_t dim = Parameter::dim(tf->parameters);
for (size_t i = 0; i < dim; i++)
{
Parameter *fparam = Parameter::getNth(tf->parameters, i);
if (!fparam->type)
continue;
Type *tprmi = (fparam->storageClass & (STClazy | STCout | STCref))
? fparam->type : getIndirection(fparam->type);
if (!tprmi)
continue; // there is no mutable indirection
//printf("\t[%d] tprmi = %d %s\n", i, tprmi->ty, tprmi->toChars());
if (traverseIndirections(tprmi, t))
return false;
}
if (AggregateDeclaration *ad = isCtorDeclaration() ? NULL : isThis())
{
Type *tthis = ad->getType()->addMod(tf->mod);
//printf("\ttthis = %s\n", tthis->toChars());
if (traverseIndirections(tthis, t))
return false;
}
return true;
}
/****************************************
* Determine if function needs a static frame pointer.
* Returns:
* `true` if function is really nested within other function.
* Contracts:
* If isNested() returns true, isThis() should return false.
*/
bool FuncDeclaration::isNested()
{
FuncDeclaration *f = toAliasFunc();
//printf("\ttoParent2() = '%s'\n", f->toParent2()->toChars());
return ((f->storage_class & STCstatic) == 0) &&
(f->linkage == LINKd) &&
(f->toParent2()->isFuncDeclaration() != NULL);
}
/****************************************
* Determine if function is a non-static member function
* that has an implicit 'this' expression.
* Returns:
* The aggregate it is a member of, or null.
* Contracts:
* If isThis() returns true, isNested() should return false.
*/
AggregateDeclaration *FuncDeclaration::isThis()
{
//printf("+FuncDeclaration::isThis() '%s'\n", toChars());
AggregateDeclaration *ad = (storage_class & STCstatic) ? NULL : isMember2();
//printf("-FuncDeclaration::isThis() %p\n", ad);
return ad;
}
bool FuncDeclaration::needThis()
{
//printf("FuncDeclaration::needThis() '%s'\n", toChars());
return toAliasFunc()->isThis() != NULL;
}
bool FuncDeclaration::addPreInvariant()
{
AggregateDeclaration *ad = isThis();
ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL;
return (ad && !(cd && cd->isCPPclass()) &&
global.params.useInvariants &&
(protection.kind == PROTprotected || protection.kind == PROTpublic || protection.kind == PROTexport) &&
!naked);
}
bool FuncDeclaration::addPostInvariant()
{
AggregateDeclaration *ad = isThis();
ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL;
return (ad && !(cd && cd->isCPPclass()) &&
ad->inv &&
global.params.useInvariants &&
(protection.kind == PROTprotected || protection.kind == PROTpublic || protection.kind == PROTexport) &&
!naked);
}
/********************************************************
* Generate Expression to call the invariant.
* Input:
* ad aggregate with the invariant
* vthis variable with 'this'
* direct call invariant directly
* Returns:
* void expression that calls the invariant
*/
Expression *addInvariant(Loc loc, Scope *sc, AggregateDeclaration *ad, VarDeclaration *vthis, bool direct)
{
Expression *e = NULL;
if (direct)
{
// 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);
//e = e->semantic(sc2);
/* Bugzilla 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;
}
}
else
{
#if 1
// Workaround for bugzilla 13394: For the correct mangling,
// run attribute inference on inv if needed.
if (ad->isStructDeclaration() && ad->inv)
ad->inv->functionSemantic();
#endif
// Call invariant virtually
Expression *v = new ThisExp(Loc());
v->type = vthis->type;
if (ad->isStructDeclaration())
v = v->addressOf();
e = new StringExp(Loc(), const_cast<char *>("null this"));
e = new AssertExp(loc, v, e);
e = semantic(e, sc);
}
return e;
}
/**********************************
* Generate a FuncDeclaration for a runtime library function.
*/
FuncDeclaration *FuncDeclaration::genCfunc(Parameters *fparams, Type *treturn, const char *name, StorageClass stc)
{
return genCfunc(fparams, treturn, Identifier::idPool(name), stc);
}
FuncDeclaration *FuncDeclaration::genCfunc(Parameters *fparams, Type *treturn, Identifier *id, StorageClass stc)
{
FuncDeclaration *fd;
TypeFunction *tf;
Dsymbol *s;
static DsymbolTable *st = NULL;
//printf("genCfunc(name = '%s')\n", id->toChars());
//printf("treturn\n\t"); treturn->print();
// See if already in table
if (!st)
st = new DsymbolTable();
s = st->lookup(id);
if (s)
{
fd = s->isFuncDeclaration();
assert(fd);
assert(fd->type->nextOf()->equals(treturn));
}
else
{
tf = new TypeFunction(fparams, treturn, 0, LINKc, stc);
fd = new FuncDeclaration(Loc(), Loc(), id, STCstatic, tf);
fd->protection = Prot(PROTpublic);
fd->linkage = LINKc;
st->insert(fd);
}
return fd;
}
/******************
* Check parameters and return type of D main() function.
* Issue error messages.
*/
void FuncDeclaration::checkDmain()
{
TypeFunction *tf = type->toTypeFunction();
const size_t nparams = Parameter::dim(tf->parameters);
bool argerr = false;
if (nparams == 1)
{
Parameter *fparam0 = Parameter::getNth(tf->parameters, 0);
Type *t = fparam0->type->toBasetype();
if (t->ty != Tarray ||
t->nextOf()->ty != Tarray ||
t->nextOf()->nextOf()->ty != Tchar ||
fparam0->storageClass & (STCout | STCref | STClazy))
{
argerr = true;
}
}
if (!tf->nextOf())
error("must return int or void");
else if (tf->nextOf()->ty != Tint32 && tf->nextOf()->ty != Tvoid)
error("must return int or void, not %s", tf->nextOf()->toChars());
else if (tf->varargs || nparams >= 2 || argerr)
error("parameters must be main() or main(string[] args)");
}
const char *FuncDeclaration::kind() const
{
return generated ? "generated function" : "function";
}
/*********************************************
* In the current function, we are calling 'this' function.
* 1. Check to see if the current function can call 'this' function, issue error if not.
* 2. If the current function is not the parent of 'this' function, then add
* the current function to the list of siblings of 'this' function.
* 3. If the current function is a literal, and it's accessing an uplevel scope,
* then mark it as a delegate.
* Returns true if error occurs.
*/
bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
{
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
if (FuncLiteralDeclaration *fld = this->isFuncLiteralDeclaration())
{
if (fld->tok == TOKreserved)
{
fld->tok = TOKfunction;
fld->vthis = NULL;
}
}
if (!parent || parent == sc->parent)
return false;
if (ident == Id::require || ident == Id::ensure)
return false;
if (!isThis() && !isNested())
return false;
// The current function
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
if (!fdthis)
return false; // out of function scope
Dsymbol *p = toParent2();
// Function literals from fdthis to p must be delegates
checkNestedRef(fdthis, p);
if (isNested())
{
// The function that this function is in
FuncDeclaration *fdv = p->isFuncDeclaration();
if (!fdv)
return false;
if (fdv == fdthis)
return false;
//printf("this = %s in [%s]\n", this->toChars(), this->loc.toChars());
//printf("fdv = %s in [%s]\n", fdv->toChars(), fdv->loc.toChars());
//printf("fdthis = %s in [%s]\n", fdthis->toChars(), fdthis->loc.toChars());
// Add this function to the list of those which called us
if (fdthis != this)
{
bool found = false;
for (size_t i = 0; i < siblingCallers.dim; ++i)
{
if (siblingCallers[i] == fdthis)
found = true;
}
if (!found)
{
//printf("\tadding sibling %s\n", fdthis->toPrettyChars());
if (!sc->intypeof && !(sc->flags & SCOPEcompile))
siblingCallers.push(fdthis);
}
}
int lv = fdthis->getLevel(loc, sc, fdv);
if (lv == -2)
return true; // error
if (lv == -1)
return false; // downlevel call
if (lv == 0)
return false; // same level call
// Uplevel call
}
return false;
}
/* For all functions between outerFunc and f, mark them as needing
* a closure.
*/
void markAsNeedingClosure(Dsymbol *f, FuncDeclaration *outerFunc)
{
for (Dsymbol *sx = f; sx && sx != outerFunc; sx = sx->parent)
{
FuncDeclaration *fy = sx->isFuncDeclaration();
if (fy && fy->closureVars.dim)
{
/* fy needs a closure if it has closureVars[],
* because the frame pointer in the closure will be accessed.
*/
fy->requiresClosure = true;
}
}
}
/* Given a nested function f inside a function outerFunc, check
* if any sibling callers of f have escaped. If so, mark
* all the enclosing functions as needing closures.
* Return true if any closures were detected.
* This is recursive: we need to check the callers of our siblings.
* Note that nested functions can only call lexically earlier nested
* functions, so loops are impossible.
*/
bool checkEscapingSiblings(FuncDeclaration *f, FuncDeclaration *outerFunc, void *p = NULL)
{
struct PrevSibling
{
PrevSibling *p;
FuncDeclaration *f;
};
PrevSibling ps;
ps.p = (PrevSibling *)p;
ps.f = f;
//printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f->toChars(), outerFunc->toChars());
bool bAnyClosures = false;
for (size_t i = 0; i < f->siblingCallers.dim; ++i)
{
FuncDeclaration *g = f->siblingCallers[i];
if (g->isThis() || g->tookAddressOf)
{
markAsNeedingClosure(g, outerFunc);
bAnyClosures = true;
}
PrevSibling *prev = (PrevSibling *)p;
while (1)
{
if (!prev)
{
bAnyClosures |= checkEscapingSiblings(g, outerFunc, &ps);
break;
}
if (prev->f == g)
break;
prev = prev->p;
}
}
//printf("\t%d\n", bAnyClosures);
return bAnyClosures;
}
/*******************************
* Look at all the variables in this function that are referenced
* by nested functions, and determine if a closure needs to be
* created for them.
*/
bool FuncDeclaration::needsClosure()
{
/* Need a closure for all the closureVars[] if any of the
* closureVars[] are accessed by a
* function that escapes the scope of this function.
* We take the conservative approach and decide that a function needs
* a closure if it:
* 1) is a virtual function
* 2) has its address taken
* 3) has a parent that escapes
* 4) calls another nested function that needs a closure
*
* Note that since a non-virtual function can be called by
* a virtual one, if that non-virtual function accesses a closure
* var, the closure still has to be taken. Hence, we check for isThis()
* instead of isVirtual(). (thanks to David Friedman)
*
* When the function returns a local struct or class, `requiresClosure`
* is already set to `true` upon entering this function when the
* struct/class refers to a local variable and a closure is needed.
*/
//printf("FuncDeclaration::needsClosure() %s\n", toChars());
if (requiresClosure)
goto Lyes;
for (size_t i = 0; i < closureVars.dim; i++)
{
VarDeclaration *v = closureVars[i];
//printf("\tv = %s\n", v->toChars());
for (size_t j = 0; j < v->nestedrefs.dim; j++)
{
FuncDeclaration *f = v->nestedrefs[j];
assert(f != this);
//printf("\t\tf = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf);
/* Look to see if f escapes. We consider all parents of f within
* this, and also all siblings which call f; if any of them escape,
* so does f.
* Mark all affected functions as requiring closures.
*/
for (Dsymbol *s = f; s && s != this; s = s->parent)
{
FuncDeclaration *fx = s->isFuncDeclaration();
if (!fx)
continue;
if (fx->isThis() || fx->tookAddressOf)
{
//printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx->toChars(), fx->isVirtual(), fx->isThis(), fx->tookAddressOf);
/* Mark as needing closure any functions between this and f
*/
markAsNeedingClosure( (fx == f) ? fx->parent : fx, this);
requiresClosure = true;
}
/* We also need to check if any sibling functions that
* called us, have escaped. This is recursive: we need
* to check the callers of our siblings.
*/
if (checkEscapingSiblings(fx, this))
requiresClosure = true;
/* Bugzilla 12406: Iterate all closureVars to mark all descendant
* nested functions that access to the closing context of this funciton.
*/
}
}
}
if (requiresClosure)
goto Lyes;
return false;
Lyes:
//printf("\tneeds closure\n");
return true;
}
/***********************************************
* Check that the function contains any closure.
* If it's @nogc, report suitable errors.
* This is mostly consistent with FuncDeclaration::needsClosure().
*
* Returns:
* true if any errors occur.
*/
bool FuncDeclaration::checkClosure()
{
if (!needsClosure())
return false;
if (setGC())
{
error("is @nogc yet allocates closures with the GC");
if (global.gag) // need not report supplemental errors
return true;
}
else
{
printGCUsage(loc, "using closure causes GC allocation");
return false;
}
FuncDeclarations a;
for (size_t i = 0; i < closureVars.dim; i++)
{
VarDeclaration *v = closureVars[i];
for (size_t j = 0; j < v->nestedrefs.dim; j++)
{
FuncDeclaration *f = v->nestedrefs[j];
assert(f != this);
for (Dsymbol *s = f; s && s != this; s = s->parent)
{
FuncDeclaration *fx = s->isFuncDeclaration();
if (!fx)
continue;
if (fx->isThis() || fx->tookAddressOf)
goto Lfound;
if (checkEscapingSiblings(fx, this))
goto Lfound;
}
continue;
Lfound:
for (size_t k = 0; ; k++)
{
if (k == a.dim)
{
a.push(f);
::errorSupplemental(f->loc, "%s closes over variable %s at %s",
f->toPrettyChars(), v->toChars(), v->loc.toChars());
break;
}
if (a[k] == f)
break;
}
continue;
}
}
return true;
}
/***********************************************
* Determine if function's variables are referenced by a function
* nested within it.
*/
bool FuncDeclaration::hasNestedFrameRefs()
{
if (closureVars.dim)
return true;
/* If a virtual function has contracts, assume its variables are referenced
* by those contracts, even if they aren't. Because they might be referenced
* by the overridden or overriding function's contracts.
* This can happen because frequire and fensure are implemented as nested functions,
* and they can be called directly by an overriding function and the overriding function's
* context had better match, or Bugzilla 7335 will bite.
*/
if (fdrequire || fdensure)
return true;
if (foverrides.dim && isVirtualMethod())
{
for (size_t i = 0; i < foverrides.dim; i++)
{
FuncDeclaration *fdv = foverrides[i];
if (fdv->hasNestedFrameRefs())
return true;
}
}
return false;
}
/*********************************************
* Return the function's parameter list, and whether
* it is variadic or not.
*/
Parameters *FuncDeclaration::getParameters(int *pvarargs)
{
Parameters *fparameters = NULL;
int fvarargs = 0;
if (type)
{
TypeFunction *fdtype = type->toTypeFunction();
fparameters = fdtype->parameters;
fvarargs = fdtype->varargs;
}
if (pvarargs)
*pvarargs = fvarargs;
return fparameters;
}
/****************************** FuncAliasDeclaration ************************/
// Used as a way to import a set of functions from another scope into this one.
FuncAliasDeclaration::FuncAliasDeclaration(Identifier *ident, FuncDeclaration *funcalias, bool hasOverloads)
: FuncDeclaration(funcalias->loc, funcalias->endloc, ident,
funcalias->storage_class, funcalias->type)
{
assert(funcalias != this);
this->funcalias = funcalias;
this->hasOverloads = hasOverloads;
if (hasOverloads)
{
if (FuncAliasDeclaration *fad = funcalias->isFuncAliasDeclaration())
this->hasOverloads = fad->hasOverloads;
}
else
{ // for internal use
assert(!funcalias->isFuncAliasDeclaration());
this->hasOverloads = false;
}
userAttribDecl = funcalias->userAttribDecl;
}
const char *FuncAliasDeclaration::kind() const
{
return "function alias";
}
FuncDeclaration *FuncAliasDeclaration::toAliasFunc()
{
return funcalias->toAliasFunc();
}
/****************************** FuncLiteralDeclaration ************************/
FuncLiteralDeclaration::FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type,
TOK tok, ForeachStatement *fes, Identifier *id)
: FuncDeclaration(loc, endloc, NULL, STCundefined, type)
{
this->ident = id ? id : Id::empty;
this->tok = tok;
this->fes = fes;
this->treq = NULL;
this->deferToObj = false;
//printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this->ident->toChars(), type->toChars());
}
Dsymbol *FuncLiteralDeclaration::syntaxCopy(Dsymbol *s)
{
//printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars());
assert(!s);
FuncLiteralDeclaration *f = new FuncLiteralDeclaration(loc, endloc,
type->syntaxCopy(), tok, fes, ident);
f->treq = treq; // don't need to copy
return FuncDeclaration::syntaxCopy(f);
}
bool FuncLiteralDeclaration::isNested()
{
//printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars());
return (tok != TOKfunction) && !isThis();
}
AggregateDeclaration *FuncLiteralDeclaration::isThis()
{
//printf("FuncLiteralDeclaration::isThis() '%s'\n", toChars());
return tok == TOKdelegate ? FuncDeclaration::isThis() : NULL;
}
bool FuncLiteralDeclaration::isVirtual()
{
return false;
}
bool FuncLiteralDeclaration::addPreInvariant()
{
return false;
}
bool FuncLiteralDeclaration::addPostInvariant()
{
return false;
}
/*******************************
* Modify all expression type of return statements to tret.
*
* On function literals, return type may be modified based on the context type
* after its semantic3 is done, in FuncExp::implicitCastTo.
*
* A function() dg = (){ return new B(); } // OK if is(B : A) == true
*
* If B to A conversion is convariant that requires offseet adjusting,
* all return statements should be adjusted to return expressions typed A.
*/
void FuncLiteralDeclaration::modifyReturns(Scope *sc, Type *tret)
{
class RetWalker : public StatementRewriteWalker
{
public:
Scope *sc;
Type *tret;
FuncLiteralDeclaration *fld;
void visit(ReturnStatement *s)
{
Expression *exp = s->exp;
if (exp && !exp->type->equals(tret))
{
s->exp = exp->castTo(sc, tret);
}
}
};
if (semanticRun < PASSsemantic3done)
return;
if (fes)
return;
RetWalker w;
w.sc = sc;
w.tret = tret;
w.fld = this;
fbody->accept(&w);
// Also update the inferred function type to match the new return type.
// This is required so the code generator does not try to cast the
// modified returns back to the original type.
if (inferRetType && type->nextOf() != tret)
type->toTypeFunction()->next = tret;
}
const char *FuncLiteralDeclaration::kind() const
{
return (tok != TOKfunction) ? "delegate" : "function";
}
const char *FuncLiteralDeclaration::toPrettyChars(bool QualifyTypes)
{
if (parent)
{
TemplateInstance *ti = parent->isTemplateInstance();
if (ti)
return ti->tempdecl->toPrettyChars(QualifyTypes);
}
return Dsymbol::toPrettyChars(QualifyTypes);
}
/********************************* CtorDeclaration ****************************/
CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type)
: FuncDeclaration(loc, endloc, Id::ctor, stc, type)
{
//printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars());
}
Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
CtorDeclaration *f = new CtorDeclaration(loc, endloc, storage_class, type->syntaxCopy());
return FuncDeclaration::syntaxCopy(f);
}
void CtorDeclaration::semantic(Scope *sc)
{
//printf("CtorDeclaration::semantic() %s\n", toChars());
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *p = toParent2();
AggregateDeclaration *ad = p->isAggregateDeclaration();
if (!ad)
{
::error(loc, "constructor can only be a member of aggregate, not %s %s",
p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
sc = sc->push();
sc->stc &= ~STCstatic; // not a static constructor
sc->flags |= SCOPEctor;
FuncDeclaration::semantic(sc);
sc->pop();
if (errors)
return;
TypeFunction *tf = type->toTypeFunction();
/* See if it's the default constructor
* But, template constructor should not become a default constructor.
*/
if (ad && (!parent->isTemplateInstance() || parent->isTemplateMixin()))
{
const size_t dim = Parameter::dim(tf->parameters);
if (StructDeclaration *sd = ad->isStructDeclaration())
{
if (dim == 0 && tf->varargs == 0) // empty default ctor w/o any varargs
{
if (fbody || !(storage_class & STCdisable) || dim)
{
error("default constructor for structs only allowed "
"with @disable, no body, and no parameters");
storage_class |= STCdisable;
fbody = NULL;
}
sd->noDefaultCtor = true;
}
else if (dim == 0 && tf->varargs) // allow varargs only ctor
{
}
else if (dim && Parameter::getNth(tf->parameters, 0)->defaultArg)
{
// if the first parameter has a default argument, then the rest does as well
if (storage_class & STCdisable)
{
deprecation("@disable'd constructor cannot have default "
"arguments for all parameters.");
deprecationSupplemental(loc, "Use @disable this(); if you want to disable default initialization.");
}
else
deprecation("all parameters have default arguments, "
"but structs cannot have default constructors.");
}
}
else if (dim == 0 && tf->varargs == 0)
{
ad->defaultCtor = this;
}
}
}
const char *CtorDeclaration::kind() const
{
return "constructor";
}
const char *CtorDeclaration::toChars()
{
return "this";
}
bool CtorDeclaration::isVirtual()
{
return false;
}
bool CtorDeclaration::addPreInvariant()
{
return false;
}
bool CtorDeclaration::addPostInvariant()
{
return (isThis() && vthis && global.params.useInvariants);
}
/********************************* PostBlitDeclaration ****************************/
PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
: FuncDeclaration(loc, endloc, id, stc, NULL)
{
}
Dsymbol *PostBlitDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, storage_class, ident);
return FuncDeclaration::syntaxCopy(dd);
}
void PostBlitDeclaration::semantic(Scope *sc)
{
//printf("PostBlitDeclaration::semantic() %s\n", toChars());
//printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor);
//printf("stc = x%llx\n", sc->stc);
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *p = toParent2();
StructDeclaration *ad = p->isStructDeclaration();
if (!ad)
{
::error(loc, "postblit can only be a member of struct/union, not %s %s",
p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
if (ident == Id::postblit && semanticRun < PASSsemantic)
ad->postblits.push(this);
if (!type)
type = new TypeFunction(NULL, Type::tvoid, false, LINKd, storage_class);
sc = sc->push();
sc->stc &= ~STCstatic; // not static
sc->linkage = LINKd;
FuncDeclaration::semantic(sc);
sc->pop();
}
bool PostBlitDeclaration::overloadInsert(Dsymbol *)
{
return false; // cannot overload postblits
}
bool PostBlitDeclaration::addPreInvariant()
{
return false;
}
bool PostBlitDeclaration::addPostInvariant()
{
return (isThis() && vthis && global.params.useInvariants);
}
bool PostBlitDeclaration::isVirtual()
{
return false;
}
/********************************* DtorDeclaration ****************************/
DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc)
: FuncDeclaration(loc, endloc, Id::dtor, STCundefined, NULL)
{
}
DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
: FuncDeclaration(loc, endloc, id, stc, NULL)
{
}
Dsymbol *DtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
DtorDeclaration *dd = new DtorDeclaration(loc, endloc, storage_class, ident);
return FuncDeclaration::syntaxCopy(dd);
}
void DtorDeclaration::semantic(Scope *sc)
{
//printf("DtorDeclaration::semantic() %s\n", toChars());
//printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor);
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *p = toParent2();
AggregateDeclaration *ad = p->isAggregateDeclaration();
if (!ad)
{
::error(loc, "destructor can only be a member of aggregate, not %s %s",
p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
if (ident == Id::dtor && semanticRun < PASSsemantic)
ad->dtors.push(this);
if (!type)
type = new TypeFunction(NULL, Type::tvoid, false, LINKd, storage_class);
sc = sc->push();
sc->stc &= ~STCstatic; // not a static destructor
if (sc->linkage != LINKcpp)
sc->linkage = LINKd;
FuncDeclaration::semantic(sc);
sc->pop();
}
bool DtorDeclaration::overloadInsert(Dsymbol *)
{
return false; // cannot overload destructors
}
bool DtorDeclaration::addPreInvariant()
{
return (isThis() && vthis && global.params.useInvariants);
}
bool DtorDeclaration::addPostInvariant()
{
return false;
}
const char *DtorDeclaration::kind() const
{
return "destructor";
}
const char *DtorDeclaration::toChars()
{
return "~this";
}
bool DtorDeclaration::isVirtual()
{
// false so that dtor's don't get put into the vtbl[]
return false;
}
/********************************* StaticCtorDeclaration ****************************/
StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc)
: FuncDeclaration(loc, endloc,
Identifier::generateId("_staticCtor"), STCstatic | stc, NULL)
{
}
StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc)
: FuncDeclaration(loc, endloc,
Identifier::generateId(name), STCstatic | stc, NULL)
{
}
Dsymbol *StaticCtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
StaticCtorDeclaration *scd = new StaticCtorDeclaration(loc, endloc, storage_class);
return FuncDeclaration::syntaxCopy(scd);
}
void StaticCtorDeclaration::semantic(Scope *sc)
{
//printf("StaticCtorDeclaration::semantic()\n");
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *p = parent->pastMixin();
if (!p->isScopeDsymbol())
{
const char *s = (isSharedStaticCtorDeclaration() ? "shared " : "");
::error(loc, "%sstatic constructor can only be member of module/aggregate/template, not %s %s",
s, p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
if (!type)
type = new TypeFunction(NULL, Type::tvoid, false, LINKd, storage_class);
/* If the static ctor appears within a template instantiation,
* it could get called multiple times by the module constructors
* for different modules. Thus, protect it with a gate.
*/
if (isInstantiated() && semanticRun < PASSsemantic)
{
/* Add this prefix to the function:
* static int gate;
* if (++gate != 1) return;
* Note that this is not thread safe; should not have threads
* during static construction.
*/
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL);
v->storage_class = STCtemp | (isSharedStaticCtorDeclaration() ? STCstatic : STCtls);
Statements *sa = new Statements();
Statement *s = new ExpStatement(Loc(), v);
sa->push(s);
Expression *e = new IdentifierExp(Loc(), v->ident);
e = new AddAssignExp(Loc(), e, new IntegerExp(1));
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(1));
s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL, Loc());
sa->push(s);
if (fbody)
sa->push(fbody);
fbody = new CompoundStatement(Loc(), sa);
}
FuncDeclaration::semantic(sc);
// We're going to need ModuleInfo
Module *m = getModule();
if (!m)
m = sc->_module;
if (m)
{
m->needmoduleinfo = 1;
//printf("module1 %s needs moduleinfo\n", m->toChars());
}
}
AggregateDeclaration *StaticCtorDeclaration::isThis()
{
return NULL;
}
bool StaticCtorDeclaration::isVirtual()
{
return false;
}
bool StaticCtorDeclaration::hasStaticCtorOrDtor()
{
return true;
}
bool StaticCtorDeclaration::addPreInvariant()
{
return false;
}
bool StaticCtorDeclaration::addPostInvariant()
{
return false;
}
/********************************* SharedStaticCtorDeclaration ****************************/
SharedStaticCtorDeclaration::SharedStaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc)
: StaticCtorDeclaration(loc, endloc, "_sharedStaticCtor", stc)
{
}
Dsymbol *SharedStaticCtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
SharedStaticCtorDeclaration *scd = new SharedStaticCtorDeclaration(loc, endloc, storage_class);
return FuncDeclaration::syntaxCopy(scd);
}
/********************************* StaticDtorDeclaration ****************************/
StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc, StorageClass stc)
: FuncDeclaration(loc, endloc,
Identifier::generateId("_staticDtor"), STCstatic | stc, NULL)
{
vgate = NULL;
}
StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc)
: FuncDeclaration(loc, endloc,
Identifier::generateId(name), STCstatic | stc, NULL)
{
vgate = NULL;
}
Dsymbol *StaticDtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
StaticDtorDeclaration *sdd = new StaticDtorDeclaration(loc, endloc, storage_class);
return FuncDeclaration::syntaxCopy(sdd);
}
void StaticDtorDeclaration::semantic(Scope *sc)
{
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *p = parent->pastMixin();
if (!p->isScopeDsymbol())
{
const char *s = (isSharedStaticDtorDeclaration() ? "shared " : "");
::error(loc, "%sstatic destructor can only be member of module/aggregate/template, not %s %s",
s, p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
if (!type)
type = new TypeFunction(NULL, Type::tvoid, false, LINKd, storage_class);
/* If the static ctor appears within a template instantiation,
* it could get called multiple times by the module constructors
* for different modules. Thus, protect it with a gate.
*/
if (isInstantiated() && semanticRun < PASSsemantic)
{
/* Add this prefix to the function:
* static int gate;
* if (--gate != 0) return;
* Increment gate during constructor execution.
* Note that this is not thread safe; should not have threads
* during static destruction.
*/
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL);
v->storage_class = STCtemp | (isSharedStaticDtorDeclaration() ? STCstatic : STCtls);
Statements *sa = new Statements();
Statement *s = new ExpStatement(Loc(), v);
sa->push(s);
Expression *e = new IdentifierExp(Loc(), v->ident);
e = new AddAssignExp(Loc(), e, new IntegerExp(-1));
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0));
s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL, Loc());
sa->push(s);
if (fbody)
sa->push(fbody);
fbody = new CompoundStatement(Loc(), sa);
vgate = v;
}
FuncDeclaration::semantic(sc);
// We're going to need ModuleInfo
Module *m = getModule();
if (!m)
m = sc->_module;
if (m)
{
m->needmoduleinfo = 1;
//printf("module2 %s needs moduleinfo\n", m->toChars());
}
}
AggregateDeclaration *StaticDtorDeclaration::isThis()
{
return NULL;
}
bool StaticDtorDeclaration::isVirtual()
{
return false;
}
bool StaticDtorDeclaration::hasStaticCtorOrDtor()
{
return true;
}
bool StaticDtorDeclaration::addPreInvariant()
{
return false;
}
bool StaticDtorDeclaration::addPostInvariant()
{
return false;
}
/********************************* SharedStaticDtorDeclaration ****************************/
SharedStaticDtorDeclaration::SharedStaticDtorDeclaration(Loc loc, Loc endloc, StorageClass stc)
: StaticDtorDeclaration(loc, endloc, "_sharedStaticDtor", stc)
{
}
Dsymbol *SharedStaticDtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
SharedStaticDtorDeclaration *sdd = new SharedStaticDtorDeclaration(loc, endloc, storage_class);
return FuncDeclaration::syntaxCopy(sdd);
}
/********************************* InvariantDeclaration ****************************/
InvariantDeclaration::InvariantDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
: FuncDeclaration(loc, endloc,
id ? id : Identifier::generateId("__invariant"),
stc, NULL)
{
}
Dsymbol *InvariantDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
InvariantDeclaration *id = new InvariantDeclaration(loc, endloc, storage_class);
return FuncDeclaration::syntaxCopy(id);
}
void InvariantDeclaration::semantic(Scope *sc)
{
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *p = parent->pastMixin();
AggregateDeclaration *ad = p->isAggregateDeclaration();
if (!ad)
{
::error(loc, "invariant can only be a member of aggregate, not %s %s",
p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
if (ident != Id::classInvariant &&
semanticRun < PASSsemantic &&
!ad->isUnionDeclaration() // users are on their own with union fields
)
ad->invs.push(this);
if (!type)
type = new TypeFunction(NULL, Type::tvoid, false, LINKd, storage_class);
sc = sc->push();
sc->stc &= ~STCstatic; // not a static invariant
sc->stc |= STCconst; // invariant() is always const
sc->flags = (sc->flags & ~SCOPEcontract) | SCOPEinvariant;
sc->linkage = LINKd;
FuncDeclaration::semantic(sc);
sc->pop();
}
bool InvariantDeclaration::isVirtual()
{
return false;
}
bool InvariantDeclaration::addPreInvariant()
{
return false;
}
bool InvariantDeclaration::addPostInvariant()
{
return false;
}
/********************************* UnitTestDeclaration ****************************/
/*******************************
* Generate unique unittest function Id so we can have multiple
* instances per module.
*/
static Identifier *unitTestId(Loc loc)
{
OutBuffer buf;
buf.printf("__unittestL%u_", loc.linnum);
return Identifier::generateId(buf.peekString());
}
UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc, StorageClass stc, char *codedoc)
: FuncDeclaration(loc, endloc, unitTestId(loc), stc, NULL)
{
this->codedoc = codedoc;
}
Dsymbol *UnitTestDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
UnitTestDeclaration *utd = new UnitTestDeclaration(loc, endloc, storage_class, codedoc);
return FuncDeclaration::syntaxCopy(utd);
}
void UnitTestDeclaration::semantic(Scope *sc)
{
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
protection = sc->protection;
parent = sc->parent;
Dsymbol *p = parent->pastMixin();
if (!p->isScopeDsymbol())
{
::error(loc, "unittest can only be a member of module/aggregate/template, not %s %s",
p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
if (global.params.useUnitTests)
{
if (!type)
type = new TypeFunction(NULL, Type::tvoid, false, LINKd, storage_class);
Scope *sc2 = sc->push();
sc2->linkage = LINKd;
FuncDeclaration::semantic(sc2);
sc2->pop();
}
}
AggregateDeclaration *UnitTestDeclaration::isThis()
{
return NULL;
}
bool UnitTestDeclaration::isVirtual()
{
return false;
}
bool UnitTestDeclaration::addPreInvariant()
{
return false;
}
bool UnitTestDeclaration::addPostInvariant()
{
return false;
}
/********************************* NewDeclaration ****************************/
NewDeclaration::NewDeclaration(Loc loc, Loc endloc, StorageClass stc, Parameters *fparams, int varargs)
: FuncDeclaration(loc, endloc, Id::classNew, STCstatic | stc, NULL)
{
this->parameters = fparams;
this->varargs = varargs;
}
Dsymbol *NewDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
NewDeclaration *f = new NewDeclaration(loc, endloc,
storage_class, Parameter::arraySyntaxCopy(parameters), varargs);
return FuncDeclaration::syntaxCopy(f);
}
void NewDeclaration::semantic(Scope *sc)
{
//printf("NewDeclaration::semantic()\n");
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *p = parent->pastMixin();
if (!p->isAggregateDeclaration())
{
::error(loc, "allocator can only be a member of aggregate, not %s %s",
p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
Type *tret = Type::tvoid->pointerTo();
if (!type)
type = new TypeFunction(parameters, tret, varargs, LINKd, storage_class);
type = type->semantic(loc, sc);
// Check that there is at least one argument of type size_t
TypeFunction *tf = type->toTypeFunction();
if (Parameter::dim(tf->parameters) < 1)
{
error("at least one argument of type size_t expected");
}
else
{
Parameter *fparam = Parameter::getNth(tf->parameters, 0);
if (!fparam->type->equals(Type::tsize_t))
error("first argument must be type size_t, not %s", fparam->type->toChars());
}
FuncDeclaration::semantic(sc);
}
const char *NewDeclaration::kind() const
{
return "allocator";
}
bool NewDeclaration::isVirtual()
{
return false;
}
bool NewDeclaration::addPreInvariant()
{
return false;
}
bool NewDeclaration::addPostInvariant()
{
return false;
}
/********************************* DeleteDeclaration ****************************/
DeleteDeclaration::DeleteDeclaration(Loc loc, Loc endloc, StorageClass stc, Parameters *fparams)
: FuncDeclaration(loc, endloc, Id::classDelete, STCstatic | stc, NULL)
{
this->parameters = fparams;
}
Dsymbol *DeleteDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
DeleteDeclaration *f = new DeleteDeclaration(loc, endloc,
storage_class, Parameter::arraySyntaxCopy(parameters));
return FuncDeclaration::syntaxCopy(f);
}
void DeleteDeclaration::semantic(Scope *sc)
{
//printf("DeleteDeclaration::semantic()\n");
if (semanticRun >= PASSsemanticdone)
return;
if (_scope)
{
sc = _scope;
_scope = NULL;
}
parent = sc->parent;
Dsymbol *p = parent->pastMixin();
if (!p->isAggregateDeclaration())
{
::error(loc, "deallocator can only be a member of aggregate, not %s %s",
p->kind(), p->toChars());
type = Type::terror;
errors = true;
return;
}
if (!type)
type = new TypeFunction(parameters, Type::tvoid, 0, LINKd, storage_class);
type = type->semantic(loc, sc);
// Check that there is only one argument of type void*
TypeFunction *tf = type->toTypeFunction();
if (Parameter::dim(tf->parameters) != 1)
{
error("one argument of type void* expected");
}
else
{
Parameter *fparam = Parameter::getNth(tf->parameters, 0);
if (!fparam->type->equals(Type::tvoid->pointerTo()))
error("one argument of type void* expected, not %s", fparam->type->toChars());
}
FuncDeclaration::semantic(sc);
}
const char *DeleteDeclaration::kind() const
{
return "deallocator";
}
bool DeleteDeclaration::isDelete()
{
return true;
}
bool DeleteDeclaration::isVirtual()
{
return false;
}
bool DeleteDeclaration::addPreInvariant()
{
return false;
}
bool DeleteDeclaration::addPostInvariant()
{
return false;
}