blob: b8e1e3183760c98018fa588321dfcdadc0990d5b [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* 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 "statement_rewrite_walker.h"
#include "template.h"
#include "hdrgen.h"
#include "target.h"
#include "parse.h"
#include "root/rmem.h"
#include "visitor.h"
bool checkNestedRef(Dsymbol *s, Dsymbol *p);
int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow);
TypeIdentifier *getThrowable();
bool MODimplicitConv(MOD modfrom, MOD modto);
MATCH MODmethodConv(MOD modfrom, MOD modto);
/***********************************************************
* Tuple of result identifier (possibly null) and statement.
* This is used to store out contracts: out(id){ ensure }
*/
Ensure::Ensure()
{
this->id = NULL;
this->ensure = NULL;
}
Ensure::Ensure(Identifier *id, Statement *ensure)
{
this->id = id;
this->ensure = ensure;
}
Ensure Ensure::syntaxCopy()
{
return Ensure(id, ensure->syntaxCopy());
}
/*****************************************
* Do syntax copy of an array of Ensure's.
*/
Ensures *Ensure::arraySyntaxCopy(Ensures *a)
{
Ensures *b = NULL;
if (a)
{
b = a->copy();
for (size_t i = 0; i < a->length; i++)
(*b)[i] = (*a)[i].syntaxCopy();
}
return b;
}
/********************************* 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;
vresult = NULL;
returnLabel = NULL;
fensure = NULL;
frequires = NULL;
fensures = 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->frequires = frequires ? Statement::arraySyntaxCopy(frequires) : NULL;
f->fensures = fensures ? Ensure::arraySyntaxCopy(fensures) : NULL;
f->fbody = fbody ? fbody->syntaxCopy() : NULL;
assert(!fthrows); // deprecated
return f;
}
// Returns true if a contract can appear without a function body.
bool allowsContractWithoutBody(FuncDeclaration *funcdecl)
{
assert(!funcdecl->fbody);
/* Contracts can only appear without a body when they are virtual
* interface functions or abstract.
*/
Dsymbol *parent = funcdecl->toParent();
InterfaceDeclaration *id = parent->isInterfaceDeclaration();
if (!funcdecl->isAbstract() &&
(funcdecl->fensures || funcdecl->frequires) &&
!(id && funcdecl->isVirtual()))
{
ClassDeclaration *cd = parent->isClassDeclaration();
if (!(cd && cd->isAbstract()))
return false;
}
return true;
}
/****************************************************
* 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
*/
bool FuncDeclaration::needsFensure(FuncDeclaration *fd)
{
if (fd->fensures)
return true;
for (size_t i = 0; i < fd->foverrides.length; i++)
{
FuncDeclaration *fdv = fd->foverrides[i];
if (fdv->fensure)
return true;
if (needsFensure(fdv))
return true;
}
return false;
}
/****************************************************
* Check whether result variable can be built.
* Returns:
* `true` if the function has a return type that
* is different from `void`.
*/
static bool canBuildResultVar(FuncDeclaration *fd)
{
TypeFunction *f = (TypeFunction *)fd->type;
return f && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid;
}
/****************************************************
* Rewrite contracts as statements.
*/
void FuncDeclaration::buildEnsureRequire()
{
if (frequires)
{
/* in { statements1... }
* in { statements2... }
* ...
* becomes:
* in { { statements1... } { statements2... } ... }
*/
assert(frequires->length);
Loc loc = (*frequires)[0]->loc;
Statements *s = new Statements;
for (size_t i = 0; i < frequires->length; i++)
{
Statement *r = (*frequires)[i];
s->push(new ScopeStatement(r->loc, r, r->loc));
}
frequire = new CompoundStatement(loc, s);
}
if (fensures)
{
/* out(id1) { statements1... }
* out(id2) { statements2... }
* ...
* becomes:
* out(__result) { { ref id1 = __result; { statements1... } }
* { ref id2 = __result; { statements2... } } ... }
*/
assert(fensures->length);
Loc loc = (*fensures)[0].ensure->loc;
Statements *s = new Statements;
for (size_t i = 0; i < fensures->length; i++)
{
Ensure r = (*fensures)[i];
if (r.id && canBuildResultVar(this))
{
Loc rloc = r.ensure->loc;
IdentifierExp *resultId = new IdentifierExp(rloc, Id::result);
ExpInitializer *init = new ExpInitializer(rloc, resultId);
StorageClass stc = STCref | STCtemp | STCresult;
VarDeclaration *decl = new VarDeclaration(rloc, NULL, r.id, init);
decl->storage_class = stc;
ExpStatement *sdecl = new ExpStatement(rloc, decl);
s->push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc));
}
else
{
s->push(r.ensure);
}
}
fensure = new CompoundStatement(loc, s);
}
if (!isVirtual())
return;
/* Rewrite contracts as nested functions, then call them. Doing it as nested
* functions means that overriding functions can call them.
*/
TypeFunction *f = (TypeFunction *)type;
if (frequire)
{
/* in { ... }
* becomes:
* void __require() { ... }
* __require();
*/
Loc loc = frequire->loc;
TypeFunction *tf = new TypeFunction(ParameterList(), Type::tvoid, 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 = 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);
frequire = new CompoundStatement(loc, s1, s2);
fdrequire = fd;
}
if (fensure)
{
/* out (result) { ... }
* becomes:
* void __ensure(ref tret result) { ... }
* __ensure(result);
*/
Loc loc = fensure->loc;
Parameters *fparams = new Parameters();
Parameter *p = NULL;
if (canBuildResultVar(this))
{
p = new Parameter(STCref | STCconst, f->nextOf(), Id::result, NULL, NULL);
fparams->push(p);
}
TypeFunction *tf = new TypeFunction(ParameterList(fparams), Type::tvoid, 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 = fensure;
Statement *s1 = new ExpStatement(loc, fd);
Expression *eresult = NULL;
if (canBuildResultVar(this))
eresult = new IdentifierExp(loc, Id::result);
Expression *e = new CallExp(loc, new VarExp(loc, fd, false), eresult);
Statement *s2 = new ExpStatement(loc, e);
fensure = new CompoundStatement(loc, s1, s2);
fdensure = fd;
}
}
/****************************************************
* 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;
dsymbolSemantic(this, _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(this, _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;
dsymbolSemantic(v, 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;
dsymbolSemantic(v, 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, Id::result, NULL);
vresult->storage_class |= STCnodtor | 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;
dsymbolSemantic(vresult, 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.length; 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->frequires && fdv->semanticRun != PASSsemantic3done)
{
assert(fdv->_scope);
Scope *sc = fdv->_scope->push();
sc->stc &= ~STCoverride;
semantic3(fdv, 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.length; 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;
semantic3(fdv, 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 (canBuildResultVar(this))
{
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..length] 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.length);
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.peekChars(), this->toPrettyChars(), thisBuf.peekChars());
}
}
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 = tf->parameterList.length();
/* 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 = tf->parameterList[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->parameterList.varargs && !tg->parameterList.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->parameterList));
}
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;
const char *failMessage = NULL;
functionResolve(&m, s, loc, sc, tiargs, tthis, fargs, &failMessage);
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.peekChars(), fargsBuf.peekChars());
// 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.peekChars(), fargsBuf.peekChars());
}
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.peekChars());
else
::error(loc, "%smethod `%s` is not callable using a %sobject",
funcBuf.peekChars(), fd->toPrettyChars(), thisBuf.peekChars());
}
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.peekChars());
else
{
fd->error(loc, "%s `%s%s%s` is not callable using argument types `%s`",
fd->kind(), fd->toPrettyChars(), parametersTypeToChars(tf->parameterList),
tf->modToChars(), fargsBuf.peekChars());
if (failMessage)
errorSupplemental(loc, failMessage);
}
}
// 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->parameterList);
const char *nextprms = parametersTypeToChars(tf2->parameterList);
::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.peekChars(),
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.extractChars();
}
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 == Prot::export_;
}
bool FuncDeclaration::isImportedSymbol() const
{
//printf("isImportedSymbol()\n");
//printf("protection = %d\n", protection);
return (protection.kind == Prot::export_) && !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 == Prot::private_ || protection.kind == Prot::package_) &&
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.length == 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.length; 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 = tf->parameterList.length();
for (size_t i = 0; i < dim; i++)
{
Parameter *fparam = tf->parameterList[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 == CHECKENABLEon &&
(protection.kind == Prot::protected_ || protection.kind == Prot::public_ || protection.kind == Prot::export_) &&
!naked);
}
bool FuncDeclaration::addPostInvariant()
{
AggregateDeclaration *ad = isThis();
ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL;
return (ad && !(cd && cd->isCPPclass()) &&
ad->inv &&
global.params.useInvariants == CHECKENABLEon &&
(protection.kind == Prot::protected_ || protection.kind == Prot::public_ || protection.kind == Prot::export_) &&
!naked);
}
/**********************************
* 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(ParameterList(fparams), treturn, LINKc, stc);
fd = new FuncDeclaration(Loc(), Loc(), id, STCstatic, tf);
fd->protection = Prot(Prot::public_);
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 = tf->parameterList.length();
bool argerr = false;
if (nparams == 1)
{
Parameter *fparam0 = tf->parameterList[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->parameterList.varargs || nparams >= 2 || argerr)
error("parameters must be main() or main(string[] args)");
}
/***********************************************
* Check all return statements for a function to verify that returning
* using NRVO is possible.
*
* Returns:
* `false` if the result cannot be returned by hidden reference.
*/
bool FuncDeclaration::checkNRVO()
{
if (!nrvo_can || returns == NULL)
return false;
TypeFunction *tf = type->toTypeFunction();
if (tf->isref)
return false;
for (size_t i = 0; i < returns->length; i++)
{
ReturnStatement *rs = (*returns)[i];
if (VarExp *ve = rs->exp->isVarExp())
{
VarDeclaration *v = ve->var->isVarDeclaration();
if (!v || v->isOut() || v->isRef())
return false;
else if (nrvo_var == NULL)
{
// Variables in the data segment (e.g. globals, TLS or not),
// parameters and closure variables cannot be NRVOed.
if (v->isDataseg() || v->isParameter() || v->toParent2() != this)
return false;
//printf("Setting nrvo to %s\n", v->toChars());
nrvo_var = v;
}
else if (nrvo_var != v)
return false;
}
else //if (!exp->isLvalue()) // keep NRVO-ability
return false;
}
return true;
}
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.length; ++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.length)
{
/* 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.length; ++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.length; i++)
{
VarDeclaration *v = closureVars[i];
//printf("\tv = %s\n", v->toChars());
for (size_t j = 0; j < v->nestedrefs.length; 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.length; i++)
{
VarDeclaration *v = closureVars[i];
for (size_t j = 0; j < v->nestedrefs.length; 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.length)
{
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.length)
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.length && isVirtualMethod())
{
for (size_t i = 0; i < foverrides.length; 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.
*/
ParameterList FuncDeclaration::getParameterList()
{
if (type)
{
TypeFunction *fdtype = type->toTypeFunction();
return fdtype->parameterList;
}
return ParameterList();
}
/****************************** 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);
}
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 == CHECKENABLEon);
}
/********************************* 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);
}
bool PostBlitDeclaration::overloadInsert(Dsymbol *)
{
return false; // cannot overload postblits
}
bool PostBlitDeclaration::addPreInvariant()
{
return false;
}
bool PostBlitDeclaration::addPostInvariant()
{
return (isThis() && vthis && global.params.useInvariants == CHECKENABLEon);
}
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);
}
bool DtorDeclaration::overloadInsert(Dsymbol *)
{
return false; // cannot overload destructors
}
bool DtorDeclaration::addPreInvariant()
{
return (isThis() && vthis && global.params.useInvariants == CHECKENABLEon);
}
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);
}
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);
}
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);
}
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.peekChars());
}
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);
}
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, VarArg 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);
}
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);
}
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;
}