Files
ldc/dmd2/func.c
David Nadlinger 787c147986 Use Module::members -> Dsymbol::codegen to define symbols.
This commit fundamentally changes the way symbol emission in
LDC works: Previously, whenever a declaration was used in some
way, the compiler would check whether it actually needs to be
defined in the currently processed module, based only on the
symbol itself. This lack of contextual information proved to
be a major problem in correctly handling emission of templates
(see e.g. #454).

Now, the DtoResolve…() family of functions and similar only
ever declare the symbols, and definition is handled by doing
a single pass over Module::members for the root module. This
is the same strategy that DMD uses as well, which should
also reduce the maintainance burden down the road (which is
important as during the last few releases, there was pretty
much always a symbol emission related problem slowing us
down).

Our old approach might have been a bit better tuned w.r.t.
avoiding emission of unneeded template instances, but 2.064
will bring improvements here (DMD: FuncDeclaration::toObjFile).
Barring such issues, the change shoud also marginally improve
compile times because of declarations no longer being emitted
when they are not needed.

In the future, we should also consider refactoring the code
so that it no longer directly accesses Dsymbol::ir but uses
wrapper functions that ensure that the appropriate
DtoResolve…() function has been called.

GitHub: Fixes #454.
2013-10-13 19:18:24 +02:00

4979 lines
151 KiB
C

// Compiler implementation of the D programming language
// Copyright (c) 1999-2012 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
#include <stdio.h>
#include <assert.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"
void functionToCBuffer2(TypeFunction *t, OutBuffer *buf, HdrGenState *hgs, int mod, const char *kind);
/********************************* 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)
this->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR);
this->loc = loc;
this->endloc = endloc;
fthrows = NULL;
frequire = NULL;
fdrequire = NULL;
fdensure = NULL;
fdrequireParams = NULL;
fdensureParams = NULL;
outId = NULL;
vresult = NULL;
returnLabel = NULL;
scout = NULL;
fensure = NULL;
fbody = NULL;
localsymtab = NULL;
vthis = NULL;
v_arguments = NULL;
#ifdef IN_GCC
v_argptr = NULL;
v_arguments_var = NULL;
#endif
v_argsave = NULL;
parameters = NULL;
labtab = NULL;
overnext = NULL;
vtblIndex = -1;
hasReturnExp = 0;
naked = 0;
inlineStatusExp = ILSuninitialized;
inlineStatusStmt = ILSuninitialized;
inlineNest = 0;
isArrayOp = 0;
dArrayOp = NULL;
semanticRun = PASSinit;
semantic3Errors = 0;
#if DMDV1
nestedFrameRef = 0;
#endif
fes = 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;
#if IN_DMD
shidden = NULL;
#endif
#if DMDV2
builtin = BUILTINunknown;
tookAddressOf = 0;
requiresClosure = false;
flags = 0;
#endif
returns = NULL;
#if IN_LLVM
// LDC
isArrayOp = false;
allowInlining = false;
neverInline = false;
availableExternally = true; // assume this unless proven otherwise
#endif
}
Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)
{
FuncDeclaration *f;
//printf("FuncDeclaration::syntaxCopy('%s')\n", toChars());
if (s)
f = (FuncDeclaration *)s;
else
f = 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
#if IN_LLVM
f->intrinsicName = intrinsicName;
#endif
return f;
}
#if IN_LLVM
static int outToRefDg(void *ctx, size_t n, Parameter *p)
{
if (p->storageClass & STCout)
{
// Cannot just use syntaxCopy() here, because it would cause the
// parameter type to be semantic()ed again, in the wrong scope. So,
// just copy the outer layer to modify the storage class.
void *cpy = malloc(sizeof(Parameter));
memcpy(cpy, (void *)p, sizeof(Parameter));
p = (Parameter *)cpy;
p->storageClass &= ~STCout;
p->storageClass |= STCref;
}
((Parameters *)ctx)->push(p);
return 0;
}
static Parameters *outToRef(Parameters* params)
{
Parameters *result = new Parameters();
Parameter::foreach(params, &outToRefDg, result);
return result;
}
#endif
// Do the semantic analysis on the external interface to the function.
void FuncDeclaration::semantic(Scope *sc)
{ TypeFunction *f;
AggregateDeclaration *ad;
StructDeclaration *sd;
ClassDeclaration *cd;
InterfaceDeclaration *id;
Dsymbol *pd;
bool doesoverride;
#if 0
printf("FuncDeclaration::semantic(sc = %p, this = %p, '%s', linkage = %d)\n", sc, this, toPrettyChars(), sc->linkage);
if (isFuncLiteralDeclaration())
printf("\tFuncLiteralDeclaration()\n");
printf("sc->parent = %s, parent = %s\n", sc->parent->toChars(), parent ? parent->toChars() : "");
printf("type: %p, %s\n", type, type->toChars());
#endif
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;
}
parent = sc->parent;
Dsymbol *parent = toParent();
if (semanticRun >= PASSsemanticdone)
{
if (!parent->isClassDeclaration())
{
return;
}
// need to re-run semantic() in order to set the class's vtbl[]
}
else
{
assert(semanticRun <= PASSsemantic);
semanticRun = PASSsemantic;
}
if (scope)
{ sc = scope;
scope = NULL;
}
unsigned dprogress_save = Module::dprogress;
foverrides.setDim(0); // reset in case semantic() is being retried for this function
storage_class |= sc->stc & ~STCref;
ad = isThis();
if (ad)
{
storage_class |= ad->storage_class & (STC_TYPECTOR | STCsynchronized);
if (StructDeclaration *sd = ad->isStructDeclaration())
sd->makeNested();
}
// 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)
linkage = ((TypeFunction *)fld->treq->nextOf())->linkage;
else
linkage = sc->linkage;
protection = sc->protection;
userAttributes = sc->userAttributes;
if (!originalType)
originalType = type->syntaxCopy();
if (!type->deco)
{
sc = sc->push();
sc->stc |= storage_class & STCdisable; // forward to function type
TypeFunction *tf = (TypeFunction *)type;
#if 1
/* If the parent is @safe, then this function defaults to safe
* too.
* If the parent's @safe-ty is inferred, then this function's @safe-ty needs
* to be inferred first.
*/
if (tf->trust == TRUSTdefault &&
!(//isFuncLiteralDeclaration() ||
parent->isTemplateInstance() ||
ad && ad->parent && ad->parent->isTemplateInstance()))
{
for (Dsymbol *p = sc->func; p; p = p->toParent2())
{ FuncDeclaration *fd = p->isFuncDeclaration();
if (fd)
{
if (fd->isSafeBypassingInference())
tf->trust = TRUSTsafe; // default to @safe
break;
}
}
}
#endif
if (tf->isref) sc->stc |= STCref;
if (tf->isnothrow) sc->stc |= STCnothrow;
if (tf->isproperty) sc->stc |= STCproperty;
if (tf->purity == PUREfwdref) sc->stc |= STCpure;
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;
if (!ad || parent->isUnionDeclaration())
{
error("constructors are only for class or struct definitions");
tret = Type::tvoid;
}
else
{ tret = ad->handle;
assert(tret);
tret = tret->addStorageClass(storage_class | sc->stc);
tret = tret->addMod(type->mod);
}
tf->next = tret;
if (ad && ad->isStructDeclaration())
sc->stc |= STCref;
}
sc->linkage = linkage;
if (!tf->isNaked() && !(isThis() || isNested()))
{
OutBuffer buf;
MODtoBuffer(&buf, tf->mod);
error("without 'this' cannot be %s", buf.toChars());
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 | STCconst | STCshared:
case STCimmutable | STCshared:
case STCimmutable | STCwild:
case STCimmutable | STCconst | STCwild:
case STCimmutable | STCconst | STCshared | STCwild:
case STCimmutable | STCshared | STCwild:
// Don't use toInvariant(), as that will do a merge()
type = type->makeInvariant();
break;
case STCconst:
case STCconst | STCwild:
type = type->makeConst();
break;
case STCshared | STCconst:
case STCshared | STCconst | STCwild:
type = type->makeSharedConst();
break;
case STCshared:
type = type->makeShared();
break;
case STCwild:
type = type->makeWild();
break;
case STCshared | STCwild:
type = type->makeSharedWild();
break;
case 0:
break;
default:
assert(0);
}
type = type->semantic(loc, sc);
sc = sc->pop();
}
storage_class &= ~STCref;
if (type->ty != Tfunction)
{
error("%s must be a function instead of %s", toChars(), type->toChars());
return;
}
f = (TypeFunction *)type;
size_t nparams = Parameter::dim(f->parameters);
if (storage_class & STCscope)
error("functions cannot be scope");
if (isAbstract() && !isVirtual())
{
const char *sfunc;
if (isStatic())
sfunc = "static";
else if (protection == PROTprivate || protection == PROTpackage)
sfunc = Pprotectionnames[protection];
else
sfunc = "non-virtual";
error("%s functions cannot be abstract", sfunc);
}
if (isOverride() && !isVirtual())
error("cannot override a non-virtual function");
if (isAbstract() && isFinal())
error("cannot be both final and abstract");
#if 0
if (isAbstract() && fbody)
error("abstract functions cannot have bodies");
#endif
#if 0
if (isStaticConstructor() || isStaticDestructor())
{
if (!isStatic() || type->nextOf()->ty != Tvoid)
error("static constructors / destructors must be static void");
if (f->arguments && f->arguments->dim)
error("static constructors / destructors must have empty parameter list");
// BUG: check for invalid storage classes
}
#endif
sd = parent->isStructDeclaration();
if (sd)
{
if (isCtorDeclaration())
{
goto Ldone;
}
#if 0
// Verify no constructors, destructors, etc.
if (isCtorDeclaration()
//||isDtorDeclaration()
//|| isInvariantDeclaration()
//|| isUnitTestDeclaration()
)
{
error("special member functions not allowed for %ss", sd->kind());
}
if (isInvariantDeclaration())
sd->invs.push(this);
if (!sd->aggNew)
sd->aggNew = isNewDeclaration();
if (isDelete())
{
if (sd->aggDelete)
error("multiple delete's for struct %s", sd->toChars());
sd->aggDelete = (DeleteDeclaration *)(this);
}
#endif
}
id = parent->isInterfaceDeclaration();
if (id)
{
storage_class |= STCabstract;
if (isCtorDeclaration() ||
#if DMDV2
isPostBlitDeclaration() ||
#endif
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());
}
/* 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");
cd = parent->isClassDeclaration();
if (cd)
{ size_t vi;
CtorDeclaration *ctor;
DtorDeclaration *dtor;
if (isCtorDeclaration())
{
// ctor = (CtorDeclaration *)this;
// if (!cd->ctor)
// cd->ctor = ctor;
goto Ldone;
}
#if 0
dtor = isDtorDeclaration();
if (dtor)
{
if (cd->dtor)
error("multiple destructors for class %s", cd->toChars());
cd->dtor = dtor;
}
if (isInvariantDeclaration())
{
cd->invs.push(this);
}
if (isNewDeclaration())
{
if (!cd->aggNew)
cd->aggNew = (NewDeclaration *)(this);
}
if (isDelete())
{
if (cd->aggDelete)
error("multiple delete's for class %s", cd->toChars());
cd->aggDelete = (DeleteDeclaration *)(this);
}
#endif
if (storage_class & STCabstract)
cd->isabstract = 1;
// 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;
if (cd->baseClass)
{
Dsymbol *cbd = cd->baseClass;
if (cbd->parent && cbd->parent->isTemplateInstance())
{
for (size_t i = 0; i < cd->baseClass->vtbl.dim; i++)
{
FuncDeclaration *f = cd->baseClass->vtbl[i]->isFuncDeclaration();
if (f && f->ident == ident && !f->functionSemantic())
goto Ldone;
}
}
}
/* Find index of existing function in base class's vtbl[] to override
* (the index will be the same as in cd's current vtbl[])
*/
vi = cd->baseClass ? findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, cd->baseClass->vtbl.dim)
: -1;
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, 0);
if (s)
{
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
{
f = f->overloadExactMatch(type);
if (f && f->isFinal() && f->prot() != PROTprivate)
error("cannot override final function %s", f->toPrettyChars());
}
}
}
if (isFinal())
{
// 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
{
// Append to end of vtbl[]
//printf("\tintroducing function\n");
introducing = 1;
vi = cd->vtbl.dim;
cd->vtbl.push(this);
vtblIndex = vi;
}
break;
case -2: // can't determine because of fwd refs
cd->sizeok = SIZEOKfwd; // can't finish due to forward reference
Module::dprogress = dprogress_save;
return;
default:
{ FuncDeclaration *fdv = cd->baseClass->vtbl[vi]->isFuncDeclaration();
FuncDeclaration *fdc = cd->vtbl[vi]->isFuncDeclaration();
// This function is covariant with fdv
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->isFinal())
error("cannot override final function %s", fdv->toPrettyChars());
doesoverride = TRUE;
#if DMDV2
if (!isOverride())
::deprecation(loc, "overriding base class function without using override attribute is deprecated (%s overrides %s)", toPrettyChars(), fdv->toPrettyChars());
#endif
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.
*/
for (size_t i = 0; i < cd->interfaces_dim; i++)
{
BaseClass *b = cd->interfaces[i];
vi = findVtblIndex((Dsymbols *)&b->base->vtbl, b->base->vtbl.dim);
switch (vi)
{
case -1:
break;
case -2:
cd->sizeok = SIZEOKfwd; // can't finish due to forward reference
Module::dprogress = dprogress_save;
return;
default:
{ FuncDeclaration *fdv = (FuncDeclaration *)b->base->vtbl[vi];
Type *ti = NULL;
/* Remember which functions this overrides
*/
foverrides.push(fdv);
#if DMDV2
/* 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());
#endif
if (fdv->tintro)
ti = fdv->tintro;
else if (!type->equals(fdv->type))
{
/* Only need to have a tintro if the vptr
* offsets differ
*/
unsigned errors = global.errors;
global.gag++; // suppress printing of error messages
int offset;
int baseOf = fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset);
global.gag--; // suppress printing of error messages
if (errors != global.errors)
{
// any error in isBaseOf() is a forward reference error, so we bail out
global.errors = errors;
cd->sizeok = SIZEOKfwd; // can't finish due to forward reference
Module::dprogress = dprogress_save;
return;
}
if (baseOf)
{
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())
{
Dsymbol *s = NULL;
for (size_t i = 0; i < cd->baseclasses->dim; i++)
{
s = (*cd->baseclasses)[i]->base->search_correct(ident);
if (s) break;
}
if (s)
error("does not override any function, did you mean to override '%s'?", 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_dim; i++)
{
BaseClass *b = cd->interfaces[i];
if (b->base)
{
Dsymbol *s = search_function(b->base, ident);
if (s)
{
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
{
f = f->overloadExactMatch(type);
if (f && f->isFinal() && f->prot() != PROTprivate)
error("cannot override final function %s.%s", b->base->toChars(), f->toPrettyChars());
}
}
}
}
}
else if (isOverride() && !parent->isTemplateInstance())
error("override only applies to class member functions");
/* 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())
{
// Check parameters to see if they are either () or (char[][] args)
switch (nparams)
{
case 0:
break;
case 1:
{
Parameter *arg0 = Parameter::getNth(f->parameters, 0);
if (arg0->type->ty != Tarray ||
arg0->type->nextOf()->ty != Tarray ||
arg0->type->nextOf()->nextOf()->ty != Tchar ||
arg0->storageClass & (STCout | STCref | STClazy))
goto Lmainerr;
break;
}
default:
goto Lmainerr;
}
if (!f->nextOf())
error("must return int or void");
else if (f->nextOf()->ty != Tint32 && f->nextOf()->ty != Tvoid)
error("must return int or void, not %s", f->nextOf()->toChars());
if (f->varargs)
{
Lmainerr:
error("parameters must be main() or main(string[] args)");
}
}
if (isVirtual() && semanticRun != PASSsemanticdone)
{
/* Rewrite contracts as nested functions, then call them.
* Doing it as nested functions means that overriding functions
* can call them.
*/
if (frequire)
{
#if IN_LLVM
/* In LDC, we can't rely on the codegen hacks DMD has to be able
* to just magically call the contract function parameterless with
* the parameters being picked up from the outer stack frame.
*
* Thus, we actually pass all the function parameters to the
* __require call, rewriting out parameters to ref ones because
* they have already been zeroed in the outer function.
*
* Also initialize fdrequireParams here - it will get filled in
* in semantic3.
*/
fdrequireParams = new Expressions();
Parameters *params = outToRef(((TypeFunction*)type)->parameters);
Type *tf = new TypeFunction(params, Type::tvoid, 0, LINKd);
#else
/* in { ... }
* becomes:
* void __require() { ... }
* __require();
*/
TypeFunction *tf = new TypeFunction(NULL, Type::tvoid, 0, LINKd);
#endif
Loc loc = frequire->loc;
FuncDeclaration *fd = new FuncDeclaration(loc, loc,
Id::require, STCundefined, tf);
fd->fbody = frequire;
Statement *s1 = new ExpStatement(loc, fd);
#if IN_LLVM
Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), fdrequireParams);
#else
Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), (Expressions *)NULL);
#endif
Statement *s2 = new ExpStatement(loc, e);
frequire = new CompoundStatement(loc, s1, s2);
fdrequire = fd;
}
if (!outId && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid)
outId = Id::result; // provide a default
#if IN_LLVM
/* We need to initialize fdensureParams here and not in the block below
* to have the parameter available when calling a base class ensure(),
* even if this functions doesn't have an out contract.
*/
fdensureParams = new Expressions();
if (outId)
fdensureParams->push(new IdentifierExp(loc, outId));
#endif
if (fensure)
{
#if IN_LLVM
/* Same as for in contracts, see above. */
Parameters *arguments = outToRef(((TypeFunction*)type)->parameters);
#else
/* out (result) { ... }
* becomes:
* tret __ensure(ref tret result) { ... }
* __ensure(result);
*/
Parameters *arguments = new Parameters();
#endif
Loc loc = fensure->loc;
Parameter *a = NULL;
if (outId)
{ a = new Parameter(STCref | STCconst, f->nextOf(), outId, NULL);
arguments->insert(0, a);
}
TypeFunction *tf = new TypeFunction(arguments, Type::tvoid, 0, LINKd);
FuncDeclaration *fd = new FuncDeclaration(loc, loc,
Id::ensure, STCundefined, tf);
fd->fbody = fensure;
Statement *s1 = new ExpStatement(loc, fd);
#if IN_LLVM
Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), fdensureParams);
#else
Expression *eresult = NULL;
if (outId)
eresult = new IdentifierExp(loc, outId);
Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), eresult);
#endif
Statement *s2 = new ExpStatement(loc, e);
fensure = new CompoundStatement(loc, s1, s2);
fdensure = fd;
}
}
Ldone:
/* Purity and safety can be inferred for some functions by examining
* the function body.
*/
if (fbody &&
(isFuncLiteralDeclaration() ||
parent->isTemplateInstance() ||
ad && ad->parent && ad->parent->isTemplateInstance() && !isVirtualMethod()))
{
/* isVirtualMethod() needs setting correct foverrides
*/
if (f->purity == PUREimpure) // purity not specified
flags |= FUNCFLAGpurityInprocess;
if (f->trust == TRUSTdefault)
flags |= FUNCFLAGsafetyInprocess;
if (!f->isnothrow)
flags |= FUNCFLAGnothrowInprocess;
}
Module::dprogress++;
//LDC relies on semanticRun variable not being reset here
if(semanticRun < PASSsemanticdone)
semanticRun = PASSsemanticdone;
/* Save scope for possible later use (if we need the
* function internals)
*/
scope = new Scope(*sc);
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(), 1);
printf("%-10s%-10s\t%s\n", "entry", type, name);
}
}
return;
}
void FuncDeclaration::semantic2(Scope *sc)
{
}
// Do the semantic analysis on the internals of the function.
void FuncDeclaration::semantic3(Scope *sc)
{ TypeFunction *f;
VarDeclaration *argptr = NULL;
VarDeclaration *_arguments = NULL;
int nerrors = global.errors;
if (!parent)
{
if (global.errors)
return;
//printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc);
assert(0);
}
//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);
//printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract));
if (semanticRun >= PASSsemantic3)
return;
semanticRun = PASSsemantic3;
semantic3Errors = 0;
#if IN_LLVM
if (!global.inExtraInliningSemantic)
availableExternally = false;
#endif
if (!type || type->ty != Tfunction)
return;
f = (TypeFunction *)type;
if (!inferRetType && f->next->ty == Terror)
return;
#if 0
// Check the 'throws' clause
if (fthrows)
{
for (size_t i = 0; i < fthrows->dim; i++)
{
Type *t = (*fthrows)[i];
t = t->semantic(loc, sc);
if (!t->isClassHandle())
error("can only throw classes, not %s", t->toChars());
}
}
#endif
if (!fbody && inferRetType && !type->nextOf())
{
error("has no function body with return type inference");
return;
}
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;
}
}
}
frequire = mergeFrequire(frequire);
fensure = mergeFensure(fensure);
if (fbody || frequire || fensure)
{
/* 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();
ss->parent = sc->scopesym;
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 | STCabstract |
STCdeprecated | STCoverride |
STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref |
STCproperty | STCsafe | STCtrusted | STCsystem);
sc2->protection = PROTpublic;
sc2->explicitProtection = 0;
sc2->structalign = STRUCTALIGN_DEFAULT;
if (this->ident != Id::require && this->ident != Id::ensure)
sc2->flags = sc->flags & ~SCOPEcontract;
#if IN_LLVM
sc2->enclosingFinally = NULL;
sc2->enclosingScopeExit = NULL;
#else
sc2->tf = NULL;
#endif
sc2->noctor = 0;
sc2->speculative = sc->speculative || isSpeculative() != NULL;
sc2->userAttributes = NULL;
if (sc2->intypeof == 1) sc2->intypeof = 2;
// Declare 'this'
AggregateDeclaration *ad = isThis();
if (ad)
{
if (isFuncLiteralDeclaration() && isNested() && !sc->intypeof)
{
error("function literals cannot be class members");
return;
}
else
assert(!isNested() || sc->intypeof); // can't be both member and nested
}
vthis = declareThis(sc2, ad);
// Declare hidden variable _arguments[] and _argptr
if (f->varargs == 1)
{
Type *t;
#if !IN_GCC && !IN_LLVM
if (global.params.is64bit && !global.params.isWindows)
{ // Declare save area for varargs registers
Type *t = new TypeIdentifier(loc, Id::va_argsave_t);
t = t->semantic(loc, sc);
if (t == Type::terror)
{
error("must import core.vararg to use variadic functions");
return;
}
else
{
v_argsave = new VarDeclaration(loc, t, Id::va_argsave, NULL);
v_argsave->semantic(sc2);
sc2->insert(v_argsave);
v_argsave->parent = this;
}
}
#endif
if (f->linkage == LINKd)
{ // Declare _arguments[]
v_arguments = new VarDeclaration(Loc(), Type::typeinfotypelist->type, Id::_arguments_typeinfo, NULL);
v_arguments->storage_class = STCparameter;
v_arguments->semantic(sc2);
sc2->insert(v_arguments);
v_arguments->parent = this;
//t = Type::typeinfo->type->constOf()->arrayOf();
t = Type::typeinfo->type->arrayOf();
_arguments = new VarDeclaration(Loc(), t, Id::_arguments, NULL);
_arguments->semantic(sc2);
sc2->insert(_arguments);
_arguments->parent = this;
}
if (f->linkage == LINKd || (f->parameters && Parameter::dim(f->parameters)))
{ // Declare _argptr
t = Type::tvalist;
argptr = new VarDeclaration(Loc(), t, Id::_argptr, NULL);
argptr->semantic(sc2);
sc2->insert(argptr);
argptr->parent = this;
}
}
#if IN_LLVM
// Make sure semantic analysis has been run on argument types. This is
// e.g. needed for TypeTuple!(int, int) to be picked up as two int
// parameters by the Parameter functions.
if (f->parameters)
{
for (size_t i = 0; i < Parameter::dim(f->parameters); i++)
{ Parameter *arg = (Parameter *)Parameter::getNth(f->parameters, i);
Type* nw = arg->type->semantic(Loc(), sc);
if (arg->type != nw) {
arg->type = nw;
// Examine this index again.
// This is important if it turned into a tuple.
// In particular, the empty tuple should be handled or the
// next parameter will be skipped.
// LDC_FIXME: Maybe we only need to do this for tuples,
// and can add tuple.length after decrement?
i--;
}
}
}
#endif
#if 0
// Propagate storage class from tuple parameters to their element-parameters.
if (f->parameters)
{
for (size_t i = 0; i < f->parameters->dim; i++)
{ Parameter *arg = (*f->parameters)[i];
//printf("[%d] arg->type->ty = %d %s\n", i, arg->type->ty, arg->type->toChars());
if (arg->type->ty == Ttuple)
{ TypeTuple *t = (TypeTuple *)arg->type;
size_t dim = Parameter::dim(t->arguments);
for (size_t j = 0; j < dim; j++)
{ Parameter *narg = Parameter::getNth(t->arguments, j);
narg->storageClass = arg->storageClass;
}
}
}
}
#endif
/* 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 *arg = Parameter::getNth(f->parameters, i);
Identifier *id = arg->ident;
if (!id)
{
/* Generate identifier for un-named parameter,
* because we need it later on.
*/
arg->ident = id = Identifier::generateId("_param_", i);
}
Type *vtype = arg->type;
VarDeclaration *v = new VarDeclaration(loc, vtype, id, NULL);
//printf("declaring parameter %s of type %s\n", v->toChars(), v->type->toChars());
v->storage_class |= STCparameter;
if (f->varargs == 2 && i + 1 == nparams)
v->storage_class |= STCvariadic;
v->storage_class |= arg->storageClass & (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor);
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;
#if IN_LLVM
if (fdrequireParams)
fdrequireParams->push(new VarExp(loc, v));
if (fdensureParams)
fdensureParams->push(new VarExp(loc, v));
#endif
}
}
// 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 *arg = (*f->parameters)[i];
if (!arg->ident)
continue; // never used, so ignore
if (arg->type->ty == Ttuple)
{ TypeTuple *t = (TypeTuple *)arg->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(arg->ident);
TupleDeclaration *v = new TupleDeclaration(loc, arg->ident, exps);
//printf("declaring tuple %s\n", v->toChars());
v->isexp = 1;
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 = NULL;
if (isDtorDeclaration())
{
// 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)
{
e = new DsymbolExp(Loc(), inv);
e = new CallExp(Loc(), e);
e = e->semantic(sc2);
}
}
else
{ // Call invariant virtually
#if IN_LLVM
// We actually need a valid 'var' for codegen.
ThisExp* tv = new ThisExp(Loc());
tv->var = vthis;
Expression *v = tv;
#else
Expression *v = new ThisExp(Loc());
#endif
v->type = vthis->type;
if (ad->isStructDeclaration())
v = v->addressOf(sc);
Expression *se = new StringExp(Loc(), (char *)"null this");
se = se->semantic(sc);
se->type = Type::tchar->arrayOf();
e = new AssertExp(loc, v, se);
}
if (e)
fpreinv = new ExpStatement(Loc(), e);
}
// Postcondition invariant
Statement *fpostinv = NULL;
if (addPostInvariant())
{
Expression *e = NULL;
if (isCtorDeclaration())
{
// 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)
{
e = new DsymbolExp(Loc(), inv);
e = new CallExp(Loc(), e);
e = e->semantic(sc2);
}
}
else
{ // Call invariant virtually
#if IN_LLVM
// We actually need a valid 'var' for codegen.
ThisExp* tv = new ThisExp(Loc());
tv->var = vthis;
Expression *v = tv;
#else
Expression *v = new ThisExp(Loc());
#endif
v->type = vthis->type;
if (ad->isStructDeclaration())
v = v->addressOf(sc);
e = new AssertExp(Loc(), v);
}
if (e)
fpostinv = new ExpStatement(Loc(), e);
}
if (fensure || addPostInvariant())
{
if ((fensure && 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;
scout = sc2->push(sym);
}
if (fbody)
{
ScopeDsymbol *sym = new ScopeDsymbol();
sym->parent = sc2->scopesym;
sc2 = sc2->push(sym);
AggregateDeclaration *ad = isAggregateMember2();
/* If this is a class constructor
*/
if (ad && isCtorDeclaration())
{
for (size_t i = 0; i < ad->fields.dim; i++)
{ VarDeclaration *v = ad->fields[i];
v->ctorinit = 0;
}
}
if (!inferRetType && f->retStyle() != RETstack)
nrvo_can = 0;
fbody = fbody->semantic(sc2);
if (!fbody)
fbody = new CompoundStatement(Loc(), new Statements());
if (inferRetType)
{ // If no return type inferred yet, then infer a void
if (!type->nextOf())
{
f->next = Type::tvoid;
//type = type->semantic(loc, sc); // Removed with 6902
}
else if (returns && f->next->ty != Tvoid)
{
for (size_t i = 0; i < returns->dim; i++)
{ Expression *exp = (*returns)[i]->exp;
if (!f->next->invariantOf()->equals(exp->type->invariantOf()))
{ exp = exp->castTo(sc2, f->next);
exp = exp->optimize(WANTvalue);
(*returns)[i]->exp = exp;
}
//printf("[%d] %s %s\n", i, exp->type->toChars(), exp->toChars());
}
}
assert(type == f);
}
if (isStaticCtorDeclaration())
{ /* It's a static constructor. Ensure that all
* ctor consts were initialized.
*/
Dsymbol *p = toParent();
ScopeDsymbol *pd = p->isScopeDsymbol();
if (!pd)
{
error("static constructor can only be member of struct/class/module, not %s %s", p->kind(), p->toChars());
}
else
{
for (size_t i = 0; i < pd->members->dim; i++)
{ Dsymbol *s = (*pd->members)[i];
s->checkCtorConstInit();
}
}
}
if (isCtorDeclaration() && ad)
{
#if DMDV2
// Check for errors related to 'nothrow'.
int nothrowErrors = global.errors;
int blockexit = fbody->blockExit(f->isnothrow);
if (f->isnothrow && (global.errors != nothrowErrors) )
error("'%s' is nothrow yet may throw", toChars());
if (flags & FUNCFLAGnothrowInprocess)
f->isnothrow = !(blockexit & BEthrow);
#endif
//printf("callSuper = x%x\n", sc2->callSuper);
ClassDeclaration *cd = ad->isClassDeclaration();
// Verify that all the ctorinit fields got initialized
if (!(sc2->callSuper & CSXthis_ctor))
{
for (size_t i = 0; i < ad->fields.dim; i++)
{ VarDeclaration *v = ad->fields[i];
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("field %s must be initialized in constructor", v->toChars());
else if (v->type->needsNested())
error("field %s must be initialized in constructor, because it is nested struct", v->toChars());
}
}
}
if (cd &&
!(sc2->callSuper & CSXany_ctor) &&
cd->baseClass && cd->baseClass->ctor)
{
sc2->callSuper = 0;
// Insert implicit super() at start of fbody
if (!resolveFuncCall(Loc(), sc2, cd->baseClass->ctor, NULL, NULL, NULL, 1))
{
error("no match for implicit super() call in constructor");
}
else
{
Expression *e1 = new SuperExp(Loc());
Expression *e = new CallExp(Loc(), e1);
e = e->semantic(sc2);
Statement *s = new ExpStatement(Loc(), e);
fbody = new CompoundStatement(Loc(), s, fbody);
}
}
/* Append:
* return this;
* to function body
*/
if (blockexit & BEfallthru)
{
Expression *e = new ThisExp(loc);
if (cd)
e->type = cd->type;
Statement *s = new ReturnStatement(loc, e);
s = s->semantic(sc2);
fbody = new CompoundStatement(loc, fbody, s);
}
}
else if (fes)
{ // For foreach(){} body, append a return 0;
Expression *e = new IntegerExp(0);
Statement *s = new ReturnStatement(Loc(), e);
fbody = new CompoundStatement(Loc(), fbody, s);
assert(!returnLabel);
}
else if (!hasReturnExp && type->nextOf()->ty != Tvoid)
error("has no return statement, but is expected to return a value of type %s", type->nextOf()->toChars());
else if (hasReturnExp & 8) // if inline asm
{
flags &= ~FUNCFLAGnothrowInprocess;
}
else
{
#if DMDV2
// Check for errors related to 'nothrow'.
int nothrowErrors = global.errors;
int blockexit = fbody->blockExit(f->isnothrow);
if (f->isnothrow && (global.errors != nothrowErrors) )
error("'%s' is nothrow yet may throw", toChars());
if (flags & FUNCFLAGnothrowInprocess)
{
if (type == f) f = f->copy();
f->isnothrow = !(blockexit & BEthrow);
}
int offend = blockexit & BEfallthru;
#endif
if (type->nextOf()->ty != Tvoid)
{
if (offend)
{ Expression *e;
#if DMDV1
warning(loc, "no return exp; or assert(0); at end of function");
#else
error("no return exp; or assert(0); at end of function");
#endif
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, (char *)"missing return expression")
);
}
else
e = new HaltExp(endloc);
e = new CommaExp(Loc(), e, type->nextOf()->defaultInit());
e = e->semantic(sc2);
Statement *s = new ExpStatement(Loc(), e);
fbody = new CompoundStatement(Loc(), fbody, s);
}
}
}
sc2 = sc2->pop();
}
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;
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 = freq->semantic(sc2);
sc2 = sc2->pop();
if (!global.params.useIn)
freq = NULL;
}
if (fens)
{ /* fensure is composed of the [out] contracts
*/
if (type->nextOf()->ty == Tvoid && outId)
{
error("void functions have no result");
}
if (type->nextOf()->ty != Tvoid)
buildResultVar();
sc2 = scout; //push
sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPEensure;
// BUG: need to treat parameters as const
// BUG: need to disallow returns and throws
fens = fens->semantic(sc2);
sc2 = sc2->pop();
if (!global.params.useOut)
fens = NULL;
}
{
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));
}
}
}
// we'll handle variadics ourselves
#if !IN_LLVM
if (argptr)
{ // Initialize _argptr
#if IN_GCC
// Handled in FuncDeclaration::toObjFile
v_argptr = argptr;
v_argptr->init = new VoidInitializer(loc);
#else
Type *t = argptr->type;
if (global.params.is64bit && !global.params.isWindows)
{ // Initialize _argptr to point to v_argsave
Expression *e1 = new VarExp(Loc(), argptr);
Expression *e = new SymOffExp(Loc(), v_argsave, 6*8 + 8*16);
e->type = argptr->type;
e = new AssignExp(Loc(), e1, e);
e = e->semantic(sc);
a->push(new ExpStatement(Loc(), e));
}
else
{ // Initialize _argptr to point past non-variadic arg
VarDeclaration *p;
unsigned offset = 0;
Expression *e;
Expression *e1 = new VarExp(Loc(), argptr);
// Find the last non-ref parameter
if (parameters && parameters->dim)
{
size_t lastNonref = parameters->dim -1;
p = (*parameters)[lastNonref];
/* The trouble with out and ref parameters is that taking
* the address of it doesn't work, because later processing
* adds in an extra level of indirection. So we skip over them.
*/
while (p->storage_class & (STCout | STCref))
{
offset += Target::ptrsize;
if (lastNonref-- == 0)
{
p = v_arguments;
break;
}
p = (*parameters)[lastNonref];
}
}
else
p = v_arguments; // last parameter is _arguments[]
if (global.params.is64bit && global.params.isWindows)
{ offset += Target::ptrsize;
if (p->storage_class & STClazy || p->type->size() > Target::ptrsize)
{
/* Necessary to offset the extra level of indirection the Win64
* ABI demands
*/
e = new SymOffExp(Loc(),p,0);
e->type = Type::tvoidptr;
e = new AddrExp(Loc(), e);
e->type = Type::tvoidptr;
e = new AddExp(Loc(), e, new IntegerExp(offset));
e->type = Type::tvoidptr;
goto L1;
}
}
else if (p->storage_class & STClazy)
// If the last parameter is lazy, it's the size of a delegate
offset += Target::ptrsize * 2;
else
offset += p->type->size();
offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); // assume stack aligns on pointer size
e = new SymOffExp(Loc(), p, offset);
e->type = Type::tvoidptr;
//e = e->semantic(sc);
L1:
e = new AssignExp(Loc(), e1, e);
e->type = t;
a->push(new ExpStatement(Loc(), e));
p->isargptr = TRUE;
}
#endif
}
if (_arguments)
{
#ifdef IN_GCC
v_arguments_var = _arguments;
v_arguments_var->init = new VoidInitializer(loc);
#endif
/* 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);
Expression *e1 = new VarExp(Loc(), _arguments);
e = new ConstructExp(Loc(), e1, e);
e = e->semantic(sc2);
a->push(new ExpStatement(Loc(), e));
}
#endif // !IN_LLVM
// 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 (type->nextOf()->ty != Tvoid && vresult)
{
#if IN_LLVM
Expression *e = 0;
if (isCtorDeclaration()) {
ThisExp *te = new ThisExp(Loc());
te->type = vthis->type;
te->var = vthis;
e = te;
} else {
e = new VarExp(Loc(), vresult);
}
#else
// Create: return vresult;
Expression *e = new VarExp(Loc(), vresult);
#endif
if (tintro)
{ e = e->implicitCastTo(sc, tintro->nextOf());
e = e->semantic(sc);
}
ReturnStatement *s = new ReturnStatement(Loc(), e);
a->push(s);
}
}
if (isMain() && type->nextOf()->ty == Tvoid)
{ // Add a return 0; statement
Statement *s = new ReturnStatement(Loc(), new IntegerExp(0));
a->push(s);
}
fbody = new CompoundStatement(Loc(), a);
#if DMDV2
/* 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->noscope)
continue;
Expression *e = v->edtor;
if (e)
{ Statement *s = new ExpStatement(Loc(), e);
s = s->semantic(sc2);
int nothrowErrors = global.errors;
bool isnothrow = f->isnothrow & !(flags & FUNCFLAGnothrowInprocess);
int blockexit = s->blockExit(isnothrow);
if (f->isnothrow && (global.errors != nothrowErrors) )
error("'%s' is nothrow yet may throw", toChars());
if (flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow)
f->isnothrow = FALSE;
if (fbody->blockExit(f->isnothrow) == BEfallthru)
fbody = new CompoundStatement(Loc(), fbody, s);
else
fbody = new TryFinallyStatement(Loc(), fbody, s);
}
}
}
// from this point on all possible 'throwers' are checked
flags &= ~FUNCFLAGnothrowInprocess;
#endif
if (isSynchronized())
{ /* Wrap the entire function body in a synchronized statement
*/
AggregateDeclaration *ad = isThis();
ClassDeclaration *cd = ad ? ad->isClassDeclaration() : parent->isClassDeclaration();
if (cd)
{
if (!global.params.is64bit &&
global.params.isWindows &&
!isStatic() && !fbody->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, new DsymbolExp(loc, cd), Id::classinfo);
}
else
{ // 'this' is the monitor
vsync = new VarExp(loc, vthis);
}
fbody = new PeelStatement(fbody); // don't redo semantic()
fbody = new SynchronizedStatement(loc, vsync, fbody);
fbody = fbody->semantic(sc2);
}
}
else
{
error("synchronized function %s must be a member of a class", toChars());
}
}
}
sc2->callSuper = 0;
sc2->pop();
}
/* If function survived being marked as impure, then it is pure
*/
if (flags & FUNCFLAGpurityInprocess)
{
flags &= ~FUNCFLAGpurityInprocess;
if (type == f) f = f->copy();
f->purity = PUREfwdref;
}
if (flags & FUNCFLAGsafetyInprocess)
{
flags &= ~FUNCFLAGsafetyInprocess;
if (type == f) f = f->copy();
f->trust = TRUSTsafe;
}
// 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)
{
sc = sc->push();
sc->stc = 0;
sc->linkage = linkage; // Bugzilla 8496
type = f->semantic(loc, sc);
sc = sc->pop();
}
if (global.gag && global.errors != nerrors)
{
/* Errors happened when compiling this function.
*/
semanticRun = PASSsemanticdone; // Ensure errors get reported again
/* Except that re-running semantic3() doesn't always produce errors a second
* time through.
* See Bugzilla 8348
* Need a better way to deal with this than gagging.
*/
}
else
{
semanticRun = PASSsemantic3done;
semantic3Errors = global.errors - nerrors;
}
//printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars());
//fflush(stdout);
}
bool FuncDeclaration::functionSemantic()
{
if (scope && !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;
if (olderrs != global.errors) // if errors compiling this function
return false;
}
// if inferring return type, sematic3 needs to be run
TemplateInstance *ti;
AggregateDeclaration *ad;
if (scope &&
(inferRetType && type && !type->nextOf() ||
(ti = parent->isTemplateInstance()) != NULL && !ti->isTemplateMixin() && ti->name == ident ||
(ad = isThis()) != NULL && ad->parent && ad->parent->isTemplateInstance() && !isVirtualMethod()))
{
return functionSemantic3();
}
return true;
}
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 speculative
* 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;
if (olderrs != global.errors) // if errors compiling this function
return false;
}
return true;
}
void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
//printf("FuncDeclaration::toCBuffer() '%s'\n", toChars());
StorageClassDeclaration::stcToCBuffer(buf, storage_class);
type->toCBuffer(buf, ident, hgs);
if(hgs->hdrgen == 1)
{
if(storage_class & STCauto)
{
hgs->autoMember++;
bodyToCBuffer(buf, hgs);
hgs->autoMember--;
}
else if(hgs->tpltMember == 0 &&
#if IN_LLVM
!global.params.hdrKeepAllBodies
#else
global.params.useInline == 0
#endif
)
buf->writestring(";");
else
bodyToCBuffer(buf, hgs);
}
else
bodyToCBuffer(buf, hgs);
buf->writenl();
}
VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad)
{
if (ad)
{ VarDeclaration *v;
{
assert(ad->handle);
Type *thandle = ad->handle;
thandle = thandle->addMod(type->mod);
thandle = thandle->addStorageClass(storage_class);
v = new ThisDeclaration(loc, thandle);
//v = new ThisDeclaration(loc, isCtorDeclaration() ? ad->handle : thandle);
v->storage_class |= STCparameter;
if (thandle->ty == Tstruct)
v->storage_class |= STCref;
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;
v->semantic(sc);
if (!sc->insert(v))
assert(0);
v->parent = this;
return v;
}
return NULL;
}
int FuncDeclaration::equals(Object *o)
{
if (this == o)
return TRUE;
Dsymbol *s = isDsymbol(o);
if (s)
{
FuncDeclaration *fd1 = this->toAliasFunc();
FuncDeclaration *fd2 = s->isFuncDeclaration();
if (fd2)
{
fd2 = fd2->toAliasFunc();
return fd1->toParent()->equals(fd2->toParent()) &&
fd1->ident->equals(fd2->ident) && fd1->type->equals(fd2->type);
}
}
return FALSE;
}
void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (fbody && (!hgs->hdrgen ||
#if IN_LLVM
global.params.hdrKeepAllBodies ||
#else
global.params.useInline ||
#endif
hgs->autoMember || hgs->tpltMember))
{
int savetlpt = hgs->tpltMember;
int saveauto = hgs->autoMember;
hgs->tpltMember = 0;
hgs->autoMember = 0;
buf->writenl();
// in{}
if (frequire)
{
buf->writestring("in");
buf->writenl();
frequire->toCBuffer(buf, hgs);
}
// out{}
if (fensure)
{
buf->writestring("out");
if (outId)
{ buf->writebyte('(');
buf->writestring(outId->toChars());
buf->writebyte(')');
}
buf->writenl();
fensure->toCBuffer(buf, hgs);
}
if (frequire || fensure)
{
buf->writestring("body");
buf->writenl();
}
buf->writebyte('{');
buf->writenl();
buf->level++;
fbody->toCBuffer(buf, hgs);
buf->level--;
buf->writebyte('}');
buf->writenl();
hgs->tpltMember = savetlpt;
hgs->autoMember = saveauto;
}
else
{ buf->writeByte(';');
buf->writenl();
}
}
/****************************************************
* Declare result variable lazily.
*/
void FuncDeclaration::buildResultVar()
{
if (vresult)
return;
assert(type->nextOf());
assert(type->nextOf()->toBasetype()->ty != Tvoid);
TypeFunction *tf = (TypeFunction *)type;
Loc loc = this->loc;
if (fensure)
loc = fensure->loc;
if (!outId)
outId = Id::result; // provide a default
VarDeclaration *v = new VarDeclaration(loc, type->nextOf(), outId, NULL);
v->noscope = 1;
v->storage_class |= STCresult;
#if DMDV2
if (!isVirtual())
v->storage_class |= STCconst;
if (tf->isref)
{
v->storage_class |= STCref | STCforeach;
}
#endif
v->semantic(scout);
if (!scout->insert(v))
error("out result %s is already defined", v->toChars());
v->parent = this;
vresult = v;
// vresult gets initialized with the function return value
// in ReturnStatement::semantic()
}
/****************************************************
* 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, Expressions *params)
{
if (!params)
params = fdrequireParams;
/* 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.
*/
#if IN_LLVM
/* In LDC, we can't rely on these codegen hacks - we explicitly pass
* parameters on to the contract functions.
*/
#else
/* 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.
*/
#endif
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 (bug 3602).
*/
if (fdv->fdrequire && fdv->fdrequire->semanticRun != PASSsemantic3done)
{
assert(fdv->scope);
Scope *sc = fdv->scope->push();
sc->stc &= ~STCoverride;
fdv->semantic3(sc);
sc->pop();
}
sf = fdv->mergeFrequire(sf, params);
if (sf && fdv->fdrequire)
{
//printf("fdv->frequire: %s\n", fdv->frequire->toChars());
/* Make the call:
* try { __require(); }
* catch { frequire; }
*/
Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdrequire, 0), params);
Statement *s2 = new ExpStatement(loc, e);
Catch *c = new Catch(loc, NULL, 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, Expressions *params)
{
if (!params)
params = fdensureParams;
/* 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 (bug 3602 and 5230).
*/
if (fdv->fdensure && fdv->fdensure->semanticRun != PASSsemantic3done)
{
assert(fdv->scope);
Scope *sc = fdv->scope->push();
sc->stc &= ~STCoverride;
fdv->semantic3(sc);
sc->pop();
}
sf = fdv->mergeFensure(sf, params);
if (fdv->fdensure)
{
//printf("fdv->fensure: %s\n", fdv->fensure->toChars());
// Make the call: __ensure(result)
Expression *eresult = NULL;
if (outId)
{
#if IN_LLVM
eresult = (*params)[0];
#else
eresult = new IdentifierExp(loc, outId);
#endif
Type *t1 = fdv->type->nextOf()->toBasetype();
#if IN_LLVM
// We actually check for matching types in CommaExp::toElem,
// 'testcontract' breaks without this.
t1 = t1->constOf();
#endif
Type *t2 = this->type->nextOf()->toBasetype();
int offset;
if (t1->isBaseOf(t2, &offset) && offset != 0)
{
/* Making temporary reference variable is necessary
* to match offset difference in covariant return.
* See bugzilla 5204.
*/
ExpInitializer *ei = new ExpInitializer(Loc(), eresult);
VarDeclaration *v = new VarDeclaration(Loc(), t1, Lexer::uniqueId("__covres"), ei);
DeclarationExp *de = new DeclarationExp(Loc(), v);
VarExp *ve = new VarExp(Loc(), v);
eresult = new CommaExp(Loc(), de, ve);
}
}
#if IN_LLVM
if (eresult)
(*params)[0] = eresult;
Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), params);
#else
Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), eresult);
#endif
Statement *s2 = new ExpStatement(loc, e);
if (sf)
{
sf = new CompoundStatement(fensure->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.
* Returns:
* -1 didn't find one
* -2 can't determine because of forward references
*/
int FuncDeclaration::findVtblIndex(Dsymbols *vtbl, int dim)
{
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())
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);
//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;
}
else
error("of type %s overrides but is not covariant with %s of type %s",
type->toChars(), mismatch->toPrettyChars(), mismatch->type->toChars());
}
return bestvi;
}
/****************************************************
* Overload this FuncDeclaration with the new one f.
* Return !=0 if successful; i.e. no conflict.
*/
int FuncDeclaration::overloadInsert(Dsymbol *s)
{
//printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s->toChars(), toChars());
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;
}
FuncDeclaration *fd = s->isFuncDeclaration();
if (!fd)
return FALSE;
#if 0
/* Disable this check because:
* const void foo();
* semantic() isn't run yet on foo(), so the const hasn't been
* applied yet.
*/
if (type)
{ printf("type = %s\n", type->toChars());
printf("fd->type = %s\n", fd->type->toChars());
}
if (type && fd->type && // can be NULL for overloaded constructors
fd->type->covariant(type) &&
fd->type->mod == type->mod &&
!isFuncAliasDeclaration())
{
//printf("\tfalse: conflict %s\n", kind());
return FALSE;
}
#endif
if (overnext)
return overnext->overloadInsert(fd);
overnext = fd;
//printf("\ttrue: no conflict\n");
return TRUE;
}
/********************************************
* Find function in overload list that exactly matches t.
*/
/***************************************************
* Visit each overloaded function in turn, and call
* (*fp)(param, f) on it.
* Exit when no more, or (*fp)(param, f) returns 1.
* Returns:
* 0 continue
* 1 done
*/
int overloadApply(FuncDeclaration *fstart,
int (*fp)(void *, FuncDeclaration *),
void *param)
{
FuncDeclaration *f;
Declaration *d;
Declaration *next;
for (d = fstart; d; d = next)
{ FuncAliasDeclaration *fa = d->isFuncAliasDeclaration();
if (fa)
{
if (fa->hasOverloads)
{
if (overloadApply(fa->funcalias, fp, param))
return 1;
}
else
{
f = fa->toAliasFunc();
if (!f)
{ d->error("is aliased to a function");
break;
}
if ((*fp)(param, f))
return 1;
}
next = fa->overnext;
}
else
{
AliasDeclaration *a = d->isAliasDeclaration();
if (a)
{
Dsymbol *s = a->toAlias();
next = s->isDeclaration();
if (next == a)
break;
if (next == fstart)
break;
}
else
{
f = d->isFuncDeclaration();
if (!f)
{ d->error("is aliased to a function");
break; // BUG: should print error message?
}
if ((*fp)(param, f))
return 1;
next = f->overnext;
}
}
}
return 0;
}
/********************************************
* If there are no overloads of function f, return that function,
* otherwise return NULL.
*/
static int fpunique(void *param, FuncDeclaration *f)
{ FuncDeclaration **pf = (FuncDeclaration **)param;
if (*pf)
{ *pf = NULL;
return 1; // ambiguous, done
}
else
{ *pf = f;
return 0;
}
}
FuncDeclaration *FuncDeclaration::isUnique()
{ FuncDeclaration *result = NULL;
overloadApply(this, &fpunique, &result);
return result;
}
/********************************************
* Find function in overload list that exactly matches t.
*/
struct Param1
{
Type *t; // type to match
FuncDeclaration *f; // return value
};
int fp1(void *param, FuncDeclaration *f)
{ Param1 *p = (Param1 *)param;
Type *t = p->t;
if (t->equals(f->type))
{ p->f = f;
return 1;
}
#if DMDV2
/* 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;
}
}
#endif
return 0;
}
FuncDeclaration *FuncDeclaration::overloadExactMatch(Type *t)
{
Param1 p;
p.t = t;
p.f = NULL;
overloadApply(this, &fp1, &p);
return p.f;
}
/********************************************
* Decide which function matches the arguments best.
* flags 1: do not issue error message on no match, just return NULL
* 2: do not issue error on ambiguous matches and need explicit this
*/
struct Param2
{
Match *m;
#if DMDV2
Type *tthis;
int property; // 0: unintialized
// 1: seen @property
// 2: not @property
#endif
Expressions *arguments;
};
int fp2(void *param, FuncDeclaration *f)
{
Param2 *p = (Param2 *)param;
Match *m = p->m;
Expressions *arguments = p->arguments;
if (f != m->lastf) // skip duplicates
{
m->anyf = f;
TypeFunction *tf = (TypeFunction *)f->type;
int property = (tf->isproperty) ? 1 : 2;
if (p->property == 0)
p->property = property;
else if (p->property != property)
error(f->loc, "cannot overload both property and non-property functions");
/* For constructors, qualifier check will be opposite direction.
* Qualified constructor always makes qualified object, then will be checked
* that it is implicitly convertible to tthis.
*/
Type *tthis = f->needThis() ? p->tthis : NULL;
if (tthis && f->isCtorDeclaration())
{
//printf("%s tf->mod = x%x tthis->mod = x%x %d\n", tf->toChars(),
// tf->mod, tthis->mod, f->isolateReturn());
if (MODimplicitConv(tf->mod, tthis->mod) ||
tf->isWild() && tf->isShared() == tthis->isShared() ||
f->isolateReturn()/* && tf->isShared() == tthis->isShared()*/)
{ // Uniquely constructed object can ignore shared qualifier.
// TODO: Is this appropriate?
tthis = NULL;
}
else
return 0; // MATCHnomatch
}
MATCH match = tf->callMatch(tthis, arguments);
//printf("test1: match = %d\n", match);
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;
else if (f->overrides(m->lastf))
goto LfIsBetter;
#if DMDV2
/* Try to disambiguate using template-style partial ordering rules.
* In essence, if f() and g() are ambiguous, if f() can call g(),
* but g() cannot call f(), then pick f().
* This is because f() is "more specialized."
*/
{
MATCH c1 = f->leastAsSpecialized(m->lastf);
MATCH c2 = m->lastf->leastAsSpecialized(f);
//printf("c1 = %d, c2 = %d\n", c1, c2);
if (c1 > c2)
goto LfIsBetter;
if (c1 < c2)
goto LlastIsBetter;
}
/* If the two functions are the same function, like:
* int foo(int);
* int foo(int x) { ... }
* then pick the one with the body.
*/
if (tf->equals(m->lastf->type) &&
f->storage_class == m->lastf->storage_class &&
f->parent == m->lastf->parent &&
f->protection == m->lastf->protection &&
f->linkage == m->lastf->linkage)
{
if (f->fbody && !m->lastf->fbody)
goto LfIsBetter;
else if (!f->fbody && m->lastf->fbody)
goto LlastIsBetter;
}
#endif
Lambiguous:
m->nextf = f;
m->count++;
return 0;
LfIsBetter:
m->last = match;
m->lastf = f;
m->count = 1;
return 0;
LlastIsBetter:
return 0;
}
}
return 0;
}
void overloadResolveX(Match *m, FuncDeclaration *fstart,
Type *tthis, Expressions *arguments)
{
Param2 p;
p.m = m;
p.tthis = tthis;
p.property = 0;
p.arguments = arguments;
overloadApply(fstart, &fp2, &p);
}
static void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod)
{
bool bothMutable = ((lhsMod & rhsMod) == 0);
bool sharedMismatch = ((lhsMod ^ rhsMod) & MODshared);
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 ");
}
FuncDeclaration *FuncDeclaration::overloadResolve(Loc loc, Type *tthis, Expressions *arguments, int flags)
{
#if 0
printf("FuncDeclaration::overloadResolve('%s')\n", toChars());
if (arguments)
{
for (size_t i = 0; i < arguments->dim; i++)
{
Expression *arg = (*arguments)[i];
assert(arg->type);
printf("\t%s: ", arg->toChars());
arg->type->print();
}
}
#endif
Match m;
memset(&m, 0, sizeof(m));
m.last = MATCHnomatch;
overloadResolveX(&m, this, tthis, arguments);
if (m.count == 1) // exactly one match
{
if (!(flags & 1))
m.lastf->functionSemantic();
return m.lastf;
}
if (m.last != MATCHnomatch && (flags & 2) && !tthis && m.lastf->needThis())
{
return m.lastf;
}
/* Failed to find a best match.
* Do nothing or print error.
*/
if (m.last == MATCHnomatch && (flags & 1))
{ // if do not print error messages
}
else
{
OutBuffer buf;
buf.writeByte('(');
if (arguments && arguments->dim)
{
HdrGenState hgs;
argExpTypesToCBuffer(&buf, arguments, &hgs);
}
buf.writeByte(')');
if (tthis)
tthis->modToBuffer(&buf);
if (m.last == MATCHnomatch)
{
TypeFunction *tf = (TypeFunction *)type;
if (tthis && !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.toChars(), this->toPrettyChars(), thisBuf.toChars());
}
else
{
//printf("tf = %s, args = %s\n", tf->deco, (*arguments)[0]->type->deco);
error(loc, "%s%s is not callable using argument types %s",
Parameter::argsTypesToChars(tf->parameters, tf->varargs),
tf->modToChars(),
buf.toChars());
}
}
else
{
TypeFunction *t1 = (TypeFunction *)m.lastf->type;
TypeFunction *t2 = (TypeFunction *)m.nextf->type;
error(loc, "called with argument types:\n\t(%s)\nmatches both:\n\t%s(%d): %s%s\nand:\n\t%s(%d): %s%s",
buf.toChars(),
m.lastf->loc.filename, m.lastf->loc.linnum, m.lastf->toPrettyChars(), Parameter::argsTypesToChars(t1->parameters, t1->varargs),
m.nextf->loc.filename, m.nextf->loc.linnum, m.nextf->toPrettyChars(), Parameter::argsTypesToChars(t2->parameters, t2->varargs));
}
}
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'
*/
#if DMDV2
MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g)
{
#define LOG_LEASTAS 0
#if LOG_LEASTAS
printf("%s.leastAsSpecialized(%s)\n", toChars(), g->toChars());
printf("%s, %s\n", type->toChars(), g->type->toChars());
#endif
/* 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 = (TypeFunction *)type;
TypeFunction *tg = (TypeFunction *)g->type;
size_t nfparams = Parameter::dim(tf->parameters);
size_t ngparams = Parameter::dim(tg->parameters);
MATCH match = MATCHexact;
/* 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))
match = MATCHconst;
else
return MATCHnomatch;
}
else
{
if (MODimplicitConv(tf->mod, tg->mod))
match = MATCHconst;
else
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)
{
/* A variadic parameter list is less specialized than a
* non-variadic one.
*/
if (tf->varargs && !tg->varargs)
goto L1; // less specialized
#if LOG_LEASTAS
printf(" matches %d, so is least as specialized\n", m);
#endif
return m;
}
L1:
#if LOG_LEASTAS
printf(" doesn't match, so is not as specialized\n");
#endif
return MATCHnomatch;
}
/*******************************************
* 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 *arguments,
int flags)
{
if (!s)
return NULL; // no match
if (tiargs && arrayObjectIsError(tiargs) ||
arguments && arrayObjectIsError((Objects *)arguments))
{
return NULL;
}
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
f = f->overloadResolve(loc, tthis, arguments, flags);
else
{ TemplateDeclaration *td = s->isTemplateDeclaration();
assert(td);
if (!sc) sc = td->scope;
f = td->deduceFunctionTemplate(loc, sc, tiargs, tthis, arguments, flags);
}
return f;
}
#endif
/********************************
* 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;
}
/****************************************
* If non-static member function that has a 'this' pointer,
* return the aggregate it is a member of.
* Otherwise, return NULL.
*/
AggregateDeclaration *FuncDeclaration::isThis()
{ AggregateDeclaration *ad;
//printf("+FuncDeclaration::isThis() '%s'\n", toChars());
ad = NULL;
if ((storage_class & STCstatic) == 0)
{
ad = isMember2();
}
//printf("-FuncDeclaration::isThis() %p\n", ad);
return ad;
}
AggregateDeclaration *FuncDeclaration::isMember2()
{ AggregateDeclaration *ad;
//printf("+FuncDeclaration::isMember2() '%s'\n", toChars());
ad = NULL;
for (Dsymbol *s = this; s; s = s->parent)
{
//printf("\ts = '%s', parent = '%s', kind = %s\n", s->toChars(), s->parent->toChars(), s->parent->kind());
ad = s->isMember();
if (ad)
{
break;
}
if (!s->parent ||
(!s->parent->isTemplateInstance()))
{
break;
}
}
//printf("-FuncDeclaration::isMember2() %p\n", ad);
return ad;
}
/*****************************************
* Determine lexical level difference from 'this' to nested function 'fd'.
* Error if this cannot call fd.
* Returns:
* 0 same level
* -1 increase nesting by 1 (fd is nested within 'this')
* >0 decrease nesting by number
*/
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 & SCOPEstaticif) && parent->isTemplateDeclaration()))
error(loc, "cannot access frame of function %s", fd->toPrettyChars());
return 1;
}
void FuncDeclaration::appendExp(Expression *e)
{ Statement *s;
s = new ExpStatement(Loc(), e);
appendState(s);
}
void FuncDeclaration::appendState(Statement *s)
{
if (!fbody)
fbody = s;
else
{
CompoundStatement *cs = fbody->isCompoundStatement();
if (cs)
{
if (!cs->statements)
fbody = s;
else
cs->statements->push(s);
}
else
fbody = new CompoundStatement(Loc(), fbody, s);
}
}
const char *FuncDeclaration::toPrettyChars()
{
if (isMain())
return "D main";
else
return Dsymbol::toPrettyChars();
}
/** for diagnostics, e.g. 'int foo(int x, int y) pure' */
const char *FuncDeclaration::toFullSignature()
{
OutBuffer buf;
HdrGenState hgs;
functionToCBuffer2((TypeFunction *)type, &buf, &hgs, 0, toChars());
buf.writeByte(0);
return buf.extractData();
}
int FuncDeclaration::isMain()
{
return ident == Id::main &&
linkage != LINKc && !isMember() && !isNested();
}
int FuncDeclaration::isWinMain()
{
//printf("FuncDeclaration::isWinMain() %s\n", toChars());
#if 0
int x = ident == Id::WinMain &&
linkage != LINKc && !isMember();
printf("%s\n", x ? "yes" : "no");
return x;
#else
return ident == Id::WinMain &&
linkage != LINKc && !isMember();
#endif
}
int FuncDeclaration::isDllMain()
{
return ident == Id::DllMain &&
linkage != LINKc && !isMember();
}
int FuncDeclaration::isExport()
{
return protection == PROTexport;
}
int FuncDeclaration::isImportedSymbol()
{
//printf("isImportedSymbol()\n");
//printf("protection = %d\n", protection);
return (protection == PROTexport) && !fbody;
}
// Determine if function goes into virtual function pointer table
int FuncDeclaration::isVirtual()
{
if (toAliasFunc() != this)
return toAliasFunc()->isVirtual();
Dsymbol *p = toParent();
#if 0
printf("FuncDeclaration::isVirtual(%s)\n", toChars());
printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), protection == PROTprivate, isCtorDeclaration(), linkage != LINKd);
printf("result is %d\n",
isMember() &&
!(isStatic() || protection == PROTprivate || protection == PROTpackage) &&
p->isClassDeclaration() &&
!(p->isInterfaceDeclaration() && isFinal()));
#endif
return isMember() &&
!(isStatic() || protection == PROTprivate || protection == PROTpackage) &&
p->isClassDeclaration() &&
!(p->isInterfaceDeclaration() && isFinal());
}
// Determine if a function is pedantically virtual
int FuncDeclaration::isVirtualMethod()
{
if (toAliasFunc() != this)
return toAliasFunc()->isVirtualMethod();
//printf("FuncDeclaration::isVirtualMethod() %s\n", toChars());
if (!isVirtual())
return 0;
// If it's a final method, and does not override anything, then it is not virtual
if (isFinal() && foverrides.dim == 0)
{
return 0;
}
return 1;
}
int FuncDeclaration::isFinal()
{
if (toAliasFunc() != this)
return toAliasFunc()->isFinal();
ClassDeclaration *cd;
#if 0
printf("FuncDeclaration::isFinal(%s), %x\n", toChars(), Declaration::isFinal());
printf("%p %d %d %d\n", isMember(), isStatic(), Declaration::isFinal(), ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal));
printf("result is %d\n",
isMember() &&
(Declaration::isFinal() ||
((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal)));
if (cd)
printf("\tmember of %s\n", cd->toChars());
#if 0
!(isStatic() || protection == PROTprivate || protection == PROTpackage) &&
(cd = toParent()->isClassDeclaration()) != NULL &&
cd->storage_class & STCfinal);
#endif
#endif
return isMember() &&
(Declaration::isFinal() ||
((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal));
}
int FuncDeclaration::isAbstract()
{
return storage_class & STCabstract;
}
int FuncDeclaration::isCodeseg()
{
return TRUE; // functions are always in the code segment
}
int FuncDeclaration::isOverloadable()
{
return 1; // functions can be overloaded
}
int FuncDeclaration::hasOverloads()
{
return overnext != NULL;
}
enum PURE FuncDeclaration::isPure()
{
//printf("FuncDeclaration::isPure() '%s'\n", toChars());
assert(type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)type;
if (flags & FUNCFLAGpurityInprocess)
setImpure();
if (tf->purity == PUREfwdref)
tf->purityLevel();
enum 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;
}
enum 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;
}
else if (isPure())
return TRUE;
return FALSE;
}
int FuncDeclaration::isSafe()
{
assert(type->ty == Tfunction);
if (flags & FUNCFLAGsafetyInprocess)
setUnsafe();
return ((TypeFunction *)type)->trust == TRUSTsafe;
}
bool FuncDeclaration::isSafeBypassingInference()
{
if (flags & FUNCFLAGsafetyInprocess)
return false;
else
return isSafe();
}
int FuncDeclaration::isTrusted()
{
assert(type->ty == Tfunction);
if (flags & FUNCFLAGsafetyInprocess)
setUnsafe();
return ((TypeFunction *)type)->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;
((TypeFunction *)type)->trust = TRUSTsystem;
}
else if (isSafe())
return TRUE;
return FALSE;
}
/**************************************
* Returns an indirect type one step from t.
*/
Type *getIndirection(Type *t)
{
t = t->toBasetype();
if (t->ty == Tsarray)
{ while (t->ty == Tsarray)
t = t->nextOf()->toBasetype();
}
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;
}
/**************************************
* Traverse this and t, and then check the indirections convertibility.
*/
int traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool a2b = true)
{
if (a2b) // check ta appears in tb
{
//printf("\ttraverse(1) %s appears in %s\n", ta->toChars(), tb->toChars());
if (ta->constConv(tb))
return 1;
else if (ta->invariantOf()->equals(tb->invariantOf()))
return 0;
else if (tb->ty == Tvoid && MODimplicitConv(ta->mod, tb->mod))
return 1;
}
else // check tb appears in ta
{
//printf("\ttraverse(2) %s appears in %s\n", tb->toChars(), ta->toChars());
if (tb->constConv(ta))
return 1;
else if (tb->invariantOf()->equals(ta->invariantOf()))
return 0;
else if (ta->ty == Tvoid && MODimplicitConv(tb->mod, ta->mod))
return 1;
}
// context date to detect circular look up
struct Ctxt
{
Ctxt *prev;
Type *type;
};
Ctxt *ctxt = (Ctxt *)p;
Type *tbb = tb->toBasetype();
if (tbb != tb)
return traverseIndirections(ta, tbb, ctxt, a2b);
if (tb->ty == Tsarray)
{ while (tb->toBasetype()->ty == Tsarray)
tb = tb->toBasetype()->nextOf();
}
if (tb->ty == Tclass || tb->ty == Tstruct)
{
for (Ctxt *c = ctxt; c; c = c->prev)
if (tb == c->type) return 0;
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);
if (!(v->storage_class & STCref))
tprmi = getIndirection(tprmi);
if (!tprmi)
continue;
//printf("\ttb = %s, tprmi = %s\n", tb->toChars(), tprmi->toChars());
if (traverseIndirections(ta, tprmi, &c, a2b))
return 1;
}
}
else if (tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tpointer)
{
Type *tind = tb->nextOf();
if (traverseIndirections(ta, tind, ctxt, a2b))
return 1;
}
else if (tb->hasPointers())
{
// FIXME: function pointer/delegate types should be considered.
return 1;
}
if (a2b)
return traverseIndirections(tb, ta, ctxt, false);
return 0;
}
/********************************************
* Returns true if the function return value has no indirection
* which comes from the parameters.
*/
bool FuncDeclaration::isolateReturn()
{
assert(type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)type;
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;
assert(type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)type;
//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 ? ad->getType()->addMod(tf->mod) : NULL;
//printf("\ttthis = %s\n", tthis->toChars());
if (traverseIndirections(tthis, t))
return false;
}
return true;
}
// Determine if function needs
// a static frame pointer to its lexically enclosing function
int 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);
}
int FuncDeclaration::needThis()
{
//printf("FuncDeclaration::needThis() '%s'\n", toChars());
return toAliasFunc()->isThis() != NULL;
}
int FuncDeclaration::addPreInvariant()
{
AggregateDeclaration *ad = isThis();
return (ad &&
//ad->isClassDeclaration() &&
global.params.useInvariants &&
(protection == PROTprotected || protection == PROTpublic || protection == PROTexport) &&
!naked &&
ident != Id::cpctor);
}
int FuncDeclaration::addPostInvariant()
{
AggregateDeclaration *ad = isThis();
return (ad &&
ad->inv &&
//ad->isClassDeclaration() &&
global.params.useInvariants &&
(protection == PROTprotected || protection == PROTpublic || protection == PROTexport) &&
!naked &&
ident != Id::cpctor);
}
/**********************************
* Generate a FuncDeclaration for a runtime library function.
*/
//
// LDC: Adjusted to give argument info to the runtime function decl.
//
FuncDeclaration *FuncDeclaration::genCfunc(Parameters *args, Type *treturn, const char *name)
{
return genCfunc(args, treturn, Lexer::idPool(name));
}
FuncDeclaration *FuncDeclaration::genCfunc(Parameters *args, Type *treturn, Identifier *id)
{
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
{
#if IN_LLVM
tf = new TypeFunction(args, treturn, 0, LINKc);
#else
tf = new TypeFunction(NULL, treturn, 0, LINKc);
#endif
fd = new FuncDeclaration(Loc(), Loc(), id, STCstatic, tf);
fd->protection = PROTpublic;
fd->linkage = LINKc;
st->insert(fd);
}
return fd;
}
const char *FuncDeclaration::kind()
{
return "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.
*/
void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
{
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
if (parent && parent != sc->parent && this->isNested() &&
this->ident != Id::require && this->ident != Id::ensure)
{
// The function that this function is in
FuncDeclaration *fdv2 = toParent2()->isFuncDeclaration();
// The current function
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
//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());
if (fdv2 && fdthis && fdv2 != fdthis)
{
// Add this function to the list of those which called us
if (fdthis != this)
{
bool found = false;
for (int i = 0; i < siblingCallers.dim; ++i)
{ if (siblingCallers[i] == fdthis)
found = true;
}
if (!found)
{
//printf("\tadding sibling %s\n", fdthis->toPrettyChars());
siblingCallers.push(fdthis);
}
}
}
FuncDeclaration *fdv = toParent()->isFuncDeclaration();
fdv = toParent()->isFuncDeclaration();
if (fdv && fdthis && fdv != fdthis)
{
int lv = fdthis->getLevel(loc, sc, fdv);
if (lv == -1)
return; // downlevel call
if (lv == 0)
return; // same level call
// Uplevel call
// BUG: may need to walk up outer scopes like Declaration::checkNestedReference() does
// function literal has reference to enclosing scope is delegate
if (FuncLiteralDeclaration *fld = fdthis->isFuncLiteralDeclaration())
fld->tok = TOKdelegate;
}
}
}
/* 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)
{
//printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f->toChars(), outerFunc->toChars());
bool bAnyClosures = false;
for (int i = 0; i < f->siblingCallers.dim; ++i)
{
FuncDeclaration *g = f->siblingCallers[i];
if (g->isThis() || g->tookAddressOf)
{
markAsNeedingClosure(g, outerFunc);
bAnyClosures = true;
}
bAnyClosures |= checkEscapingSiblings(g, outerFunc);
}
//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.
*/
#if DMDV2
int 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
* -or-
* 5) this function returns a local struct/class
*
* 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)
*/
//printf("FuncDeclaration::needsClosure() %s\n", toChars());
if (requiresClosure)
goto Lyes;
for (size_t i = 0; i < closureVars.dim; i++)
{ VarDeclaration *v = closureVars[i];
assert(v->isVarDeclaration());
//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 && (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);
goto Lyes;
}
/* 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 (fx && checkEscapingSiblings(fx, this))
goto Lyes;
}
}
}
/* Look for case (5)
*/
if (closureVars.dim)
{
assert(type->ty == Tfunction);
Type *tret = ((TypeFunction *)type)->next;
assert(tret);
tret = tret->toBasetype();
//printf("\t\treturning %s\n", tret->toChars());
if (tret->ty == Tclass || tret->ty == Tstruct)
{ Dsymbol *st = tret->toDsymbol(NULL);
//printf("\t\treturning class/struct %s\n", tret->toChars());
for (Dsymbol *s = st->parent; s; s = s->parent)
{
//printf("\t\t\tparent = %s %s\n", s->kind(), s->toChars());
if (s == this)
{ //printf("\t\treturning local %s\n", st->toChars());
goto Lyes;
}
}
}
}
return 0;
Lyes:
//printf("\tneeds closure\n");
return 1;
}
#endif
/***********************************************
* Determine if function's variables are referenced by a function
* nested within it.
*/
int FuncDeclaration::hasNestedFrameRefs()
{
#if DMDV2
if (closureVars.dim)
#else
if (nestedFrameRef)
#endif
return 1;
/* If a virtual method 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 7337 will bite.
*/
if ((fdrequire || fdensure) && isVirtualMethod())
return 1;
if (foverrides.dim && isVirtualMethod())
{
for (size_t i = 0; i < foverrides.dim; i++)
{
FuncDeclaration *fdv = foverrides[i];
if (fdv->hasNestedFrameRefs())
return 1;
}
}
return 0;
}
/*********************************************
* Return the function's parameter list, and whether
* it is variadic or not.
*/
Parameters *FuncDeclaration::getParameters(int *pvarargs)
{ Parameters *fparameters;
int fvarargs;
if (type)
{
assert(type->ty == Tfunction);
TypeFunction *fdtype = (TypeFunction *)type;
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(FuncDeclaration *funcalias, int hasOverloads)
: FuncDeclaration(funcalias->loc, funcalias->endloc, funcalias->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 = 0;
}
userAttributes = funcalias->userAttributes;
}
const char *FuncAliasDeclaration::kind()
{
return "function alias";
}
FuncDeclaration *FuncAliasDeclaration::toAliasFunc()
{
return funcalias->toAliasFunc();
}
/****************************** FuncLiteralDeclaration ************************/
FuncLiteralDeclaration::FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type,
enum TOK tok, ForeachStatement *fes)
: FuncDeclaration(loc, endloc, NULL, STCundefined, type)
{
const char *id;
if (fes)
id = "__foreachbody";
else if (tok == TOKreserved)
id = "__lambda";
else if (tok == TOKdelegate)
id = "__dgliteral";
else
id = "__funcliteral";
this->ident = Lexer::uniqueId(id);
this->tok = tok;
this->fes = fes;
this->treq = NULL;
//printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this->ident->toChars(), type->toChars());
#if IN_LLVM
this->owningTemplate = NULL;
#endif
}
Dsymbol *FuncLiteralDeclaration::syntaxCopy(Dsymbol *s)
{
FuncLiteralDeclaration *f;
//printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars());
if (s)
f = (FuncLiteralDeclaration *)s;
else
{ f = new FuncLiteralDeclaration(loc, endloc, type->syntaxCopy(), tok, fes);
f->ident = ident; // keep old identifier
f->treq = treq; // don't need to copy
}
FuncDeclaration::syntaxCopy(f);
return f;
}
int FuncLiteralDeclaration::isNested()
{
//printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars());
return (tok != TOKfunction);
}
int FuncLiteralDeclaration::isVirtual()
{
return FALSE;
}
const char *FuncLiteralDeclaration::kind()
{
// GCC requires the (char*) casts
return (tok != TOKfunction) ? (char*)"delegate" : (char*)"function";
}
void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (tok != TOKreserved)
{
buf->writestring(kind());
buf->writeByte(' ');
}
TypeFunction *tf = (TypeFunction *)type;
// Don't print tf->mod, tf->trust, and tf->linkage
if (tf->next)
tf->next->toCBuffer2(buf, hgs, 0);
Parameter::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs);
ReturnStatement *ret = !fbody->isCompoundStatement() ?
fbody->isReturnStatement() : NULL;
if (ret && ret->exp)
{
buf->writestring(" => ");
ret->exp->toCBuffer(buf, hgs);
}
else
{
hgs->tpltMember++;
bodyToCBuffer(buf, hgs);
hgs->tpltMember--;
}
}
/********************************* 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)
{
CtorDeclaration *f = new CtorDeclaration(loc, endloc, 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;
}
void CtorDeclaration::semantic(Scope *sc)
{
//printf("CtorDeclaration::semantic() %s\n", toChars());
TypeFunction *tf = (TypeFunction *)type;
assert(tf && tf->ty == Tfunction);
if (scope)
{ sc = scope;
scope = NULL;
}
sc = sc->push();
sc->stc &= ~STCstatic; // not a static constructor
sc->flags |= SCOPEctor;
FuncDeclaration::semantic(sc);
sc->pop();
Dsymbol *parent = toParent2();
AggregateDeclaration *ad = parent->isAggregateDeclaration();
/* See if it's the default constructor
* But, template constructor should not become a default constructor.
*/
if (ad && tf->varargs == 0 && Parameter::dim(tf->parameters) == 0
&& (!this->parent->isTemplateInstance() || this->parent->isTemplateMixin()))
{
StructDeclaration *sd = ad->isStructDeclaration();
if (sd)
{
if (fbody || !(storage_class & STCdisable))
{ error("default constructor for structs only allowed with @disable and no body");
storage_class |= STCdisable;
fbody = NULL;
}
sd->noDefaultCtor = TRUE;
}
else
{
ad->defaultCtor = this;
}
}
}
const char *CtorDeclaration::kind()
{
return "constructor";
}
char *CtorDeclaration::toChars()
{
return (char *)"this";
}
int CtorDeclaration::isVirtual()
{
return FALSE;
}
int CtorDeclaration::addPreInvariant()
{
return FALSE;
}
int CtorDeclaration::addPostInvariant()
{
return (isThis() && vthis && global.params.useInvariants);
}
/********************************* PostBlitDeclaration ****************************/
#if DMDV2
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 (scope)
{ sc = scope;
scope = NULL;
}
parent = sc->parent;
Dsymbol *parent = toParent();
StructDeclaration *ad = parent->isStructDeclaration();
if (!ad)
{
error("post blits are only for struct/union definitions, not %s %s", parent->kind(), parent->toChars());
}
else 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();
}
int PostBlitDeclaration::overloadInsert(Dsymbol *s)
{
return FALSE; // cannot overload postblits
}
int PostBlitDeclaration::addPreInvariant()
{
return FALSE;
}
int PostBlitDeclaration::addPostInvariant()
{
return (isThis() && vthis && global.params.useInvariants);
}
int PostBlitDeclaration::isVirtual()
{
return FALSE;
}
void PostBlitDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("this(this)");
bodyToCBuffer(buf, hgs);
}
#endif
/********************************* 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 (scope)
{ sc = scope;
scope = NULL;
}
parent = sc->parent;
Dsymbol *parent = toParent();
AggregateDeclaration *ad = parent->isAggregateDeclaration();
if (!ad)
{
error("destructors are only for class/struct/union definitions, not %s %s", parent->kind(), parent->toChars());
fatal();
}
else 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
sc->linkage = LINKd;
FuncDeclaration::semantic(sc);
sc->pop();
}
int DtorDeclaration::overloadInsert(Dsymbol *s)
{
return FALSE; // cannot overload destructors
}
int DtorDeclaration::addPreInvariant()
{
return (isThis() && vthis && global.params.useInvariants);
}
int DtorDeclaration::addPostInvariant()
{
return FALSE;
}
const char *DtorDeclaration::kind()
{
return "destructor";
}
char *DtorDeclaration::toChars()
{
return (char *)"~this";
}
int DtorDeclaration::isVirtual()
{
// FALSE so that dtor's don't get put into the vtbl[]
return FALSE;
}
void DtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("~this()");
bodyToCBuffer(buf, hgs);
}
/********************************* StaticCtorDeclaration ****************************/
StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc)
: FuncDeclaration(loc, endloc,
Identifier::generateId("_staticCtor"), STCstatic, NULL)
{
}
StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, const char *name)
: FuncDeclaration(loc, endloc,
Identifier::generateId(name), STCstatic, NULL)
{
}
Dsymbol *StaticCtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
StaticCtorDeclaration *scd = new StaticCtorDeclaration(loc, endloc);
return FuncDeclaration::syntaxCopy(scd);
}
void StaticCtorDeclaration::semantic(Scope *sc)
{
//printf("StaticCtorDeclaration::semantic()\n");
if (scope)
{ sc = scope;
scope = NULL;
}
if (!type)
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd);
/* 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 (inTemplateInstance() && 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.
*/
Identifier *id = Lexer::idPool("__gate");
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL);
v->storage_class = isSharedStaticCtorDeclaration() ? STCstatic : STCtls;
Statements *sa = new Statements();
Statement *s = new ExpStatement(Loc(), v);
sa->push(s);
Expression *e = new IdentifierExp(Loc(), id);
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);
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;
}
int StaticCtorDeclaration::isVirtual()
{
return FALSE;
}
bool StaticCtorDeclaration::hasStaticCtorOrDtor()
{
return TRUE;
}
int StaticCtorDeclaration::addPreInvariant()
{
return FALSE;
}
int StaticCtorDeclaration::addPostInvariant()
{
return FALSE;
}
void StaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (hgs->hdrgen && !hgs->tpltMember)
{
buf->writestring("static this();");
buf->writenl();
return;
}
buf->writestring("static this()");
bodyToCBuffer(buf, hgs);
}
/********************************* SharedStaticCtorDeclaration ****************************/
SharedStaticCtorDeclaration::SharedStaticCtorDeclaration(Loc loc, Loc endloc)
: StaticCtorDeclaration(loc, endloc, "_sharedStaticCtor")
{
}
Dsymbol *SharedStaticCtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
SharedStaticCtorDeclaration *scd = new SharedStaticCtorDeclaration(loc, endloc);
return FuncDeclaration::syntaxCopy(scd);
}
void SharedStaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("shared ");
StaticCtorDeclaration::toCBuffer(buf, hgs);
}
/********************************* StaticDtorDeclaration ****************************/
StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc)
: FuncDeclaration(loc, endloc,
Identifier::generateId("_staticDtor"), STCstatic, NULL)
{
vgate = NULL;
}
StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc, const char *name)
: FuncDeclaration(loc, endloc,
Identifier::generateId(name), STCstatic, NULL)
{
vgate = NULL;
}
Dsymbol *StaticDtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
StaticDtorDeclaration *sdd = new StaticDtorDeclaration(loc, endloc);
return FuncDeclaration::syntaxCopy(sdd);
}
void StaticDtorDeclaration::semantic(Scope *sc)
{
if (scope)
{ sc = scope;
scope = NULL;
}
ClassDeclaration *cd = sc->scopesym->isClassDeclaration();
if (!type)
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd);
/* 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 (inTemplateInstance() && 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.
*/
Identifier *id = Lexer::idPool("__gate");
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL);
v->storage_class = isSharedStaticDtorDeclaration() ? STCstatic : STCtls;
Statements *sa = new Statements();
Statement *s = new ExpStatement(Loc(), v);
sa->push(s);
Expression *e = new IdentifierExp(Loc(), id);
e = new AddAssignExp(Loc(), e, new IntegerExp(-1)); // LDC_FIXME: Previously had (uint64_t)-1, double-check this.
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0));
s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL);
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;
}
int StaticDtorDeclaration::isVirtual()
{
return FALSE;
}
bool StaticDtorDeclaration::hasStaticCtorOrDtor()
{
return TRUE;
}
int StaticDtorDeclaration::addPreInvariant()
{
return FALSE;
}
int StaticDtorDeclaration::addPostInvariant()
{
return FALSE;
}
void StaticDtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (hgs->hdrgen)
return;
buf->writestring("static ~this()");
bodyToCBuffer(buf, hgs);
}
/********************************* SharedStaticDtorDeclaration ****************************/
SharedStaticDtorDeclaration::SharedStaticDtorDeclaration(Loc loc, Loc endloc)
: StaticDtorDeclaration(loc, endloc, "_sharedStaticDtor")
{
}
Dsymbol *SharedStaticDtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
SharedStaticDtorDeclaration *sdd = new SharedStaticDtorDeclaration(loc, endloc);
return FuncDeclaration::syntaxCopy(sdd);
}
void SharedStaticDtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (!hgs->hdrgen)
{
buf->writestring("shared ");
StaticDtorDeclaration::toCBuffer(buf, hgs);
}
}
/********************************* 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)
{
InvariantDeclaration *id;
assert(!s);
id = new InvariantDeclaration(loc, endloc, storage_class);
FuncDeclaration::syntaxCopy(id);
return id;
}
void InvariantDeclaration::semantic(Scope *sc)
{
if (scope)
{ sc = scope;
scope = NULL;
}
parent = sc->parent;
Dsymbol *parent = toParent();
AggregateDeclaration *ad = parent->isAggregateDeclaration();
if (!ad)
{
error("invariants are only for struct/union/class definitions");
return;
}
if (ident != Id::classInvariant && semanticRun < PASSsemantic)
{
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();
}
int InvariantDeclaration::isVirtual()
{
return FALSE;
}
int InvariantDeclaration::addPreInvariant()
{
return FALSE;
}
int InvariantDeclaration::addPostInvariant()
{
return FALSE;
}
void InvariantDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (hgs->hdrgen)
return;
buf->writestring("invariant");
bodyToCBuffer(buf, hgs);
}
/********************************* UnitTestDeclaration ****************************/
/*******************************
* Generate unique unittest function Id so we can have multiple
* instances per module.
*/
static Identifier *unitTestId(Loc loc)
{
char name[24];
#if __DMC__ || _MSC_VER
_snprintf(name, 24, "__unittestL%u_", loc.linnum);
#else
snprintf(name, 24, "__unittestL%u_", loc.linnum);
#endif
return Lexer::uniqueId(name);
}
UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc, char *codedoc)
: FuncDeclaration(loc, endloc, unitTestId(loc), STCundefined, NULL)
{
this->codedoc = codedoc;
}
Dsymbol *UnitTestDeclaration::syntaxCopy(Dsymbol *s)
{
UnitTestDeclaration *utd;
assert(!s);
utd = new UnitTestDeclaration(loc, endloc, codedoc);
return FuncDeclaration::syntaxCopy(utd);
}
void UnitTestDeclaration::semantic(Scope *sc)
{
protection = sc->protection;
if (scope)
{ sc = scope;
scope = NULL;
}
if (global.params.useUnitTests)
{
if (!type)
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd);
Scope *sc2 = sc->push();
// It makes no sense for unit tests to be pure or nothrow.
sc2->stc &= ~(STCnothrow | STCpure);
sc2->linkage = LINKd;
FuncDeclaration::semantic(sc2);
sc2->pop();
}
#if 0
// We're going to need ModuleInfo even if the unit tests are not
// compiled in, because other modules may import this module and refer
// to this ModuleInfo.
// (This doesn't make sense to me?)
Module *m = getModule();
if (!m)
m = sc->module;
if (m)
{
//printf("module3 %s needs moduleinfo\n", m->toChars());
m->needmoduleinfo = 1;
}
#endif
}
AggregateDeclaration *UnitTestDeclaration::isThis()
{
return NULL;
}
int UnitTestDeclaration::isVirtual()
{
return FALSE;
}
int UnitTestDeclaration::addPreInvariant()
{
return FALSE;
}
int UnitTestDeclaration::addPostInvariant()
{
return FALSE;
}
void UnitTestDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (hgs->hdrgen)
return;
buf->writestring("unittest");
bodyToCBuffer(buf, hgs);
}
/********************************* NewDeclaration ****************************/
NewDeclaration::NewDeclaration(Loc loc, Loc endloc, Parameters *arguments, int varargs)
: FuncDeclaration(loc, endloc, Id::classNew, STCstatic, NULL)
{
this->arguments = arguments;
this->varargs = varargs;
}
Dsymbol *NewDeclaration::syntaxCopy(Dsymbol *s)
{
NewDeclaration *f;
f = new NewDeclaration(loc, endloc, NULL, varargs);
FuncDeclaration::syntaxCopy(f);
f->arguments = Parameter::arraySyntaxCopy(arguments);
return f;
}
void NewDeclaration::semantic(Scope *sc)
{
//printf("NewDeclaration::semantic()\n");
if (scope)
{ sc = scope;
scope = NULL;
}
parent = sc->parent;
Dsymbol *parent = toParent();
ClassDeclaration *cd = parent->isClassDeclaration();
if (!cd && !parent->isStructDeclaration())
{
error("new allocators only are for class or struct definitions");
}
Type *tret = Type::tvoid->pointerTo();
if (!type)
type = new TypeFunction(arguments, tret, varargs, LINKd);
type = type->semantic(loc, sc);
assert(type->ty == Tfunction);
// Check that there is at least one argument of type size_t
TypeFunction *tf = (TypeFunction *)type;
if (Parameter::dim(tf->parameters) < 1)
{
error("at least one argument of type size_t expected");
}
else
{
Parameter *a = Parameter::getNth(tf->parameters, 0);
if (!a->type->equals(Type::tsize_t))
error("first argument must be type size_t, not %s", a->type->toChars());
}
FuncDeclaration::semantic(sc);
}
const char *NewDeclaration::kind()
{
return "allocator";
}
int NewDeclaration::isVirtual()
{
return FALSE;
}
int NewDeclaration::addPreInvariant()
{
return FALSE;
}
int NewDeclaration::addPostInvariant()
{
return FALSE;
}
void NewDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("new");
Parameter::argsToCBuffer(buf, hgs, arguments, varargs);
bodyToCBuffer(buf, hgs);
}
/********************************* DeleteDeclaration ****************************/
DeleteDeclaration::DeleteDeclaration(Loc loc, Loc endloc, Parameters *arguments)
: FuncDeclaration(loc, endloc, Id::classDelete, STCstatic, NULL)
{
this->arguments = arguments;
}
Dsymbol *DeleteDeclaration::syntaxCopy(Dsymbol *s)
{
DeleteDeclaration *f;
f = new DeleteDeclaration(loc, endloc, NULL);
FuncDeclaration::syntaxCopy(f);
f->arguments = Parameter::arraySyntaxCopy(arguments);
return f;
}
void DeleteDeclaration::semantic(Scope *sc)
{
//printf("DeleteDeclaration::semantic()\n");
if (scope)
{ sc = scope;
scope = NULL;
}
parent = sc->parent;
Dsymbol *parent = toParent();
ClassDeclaration *cd = parent->isClassDeclaration();
if (!cd && !parent->isStructDeclaration())
{
error("new allocators only are for class or struct definitions");
}
if (!type)
type = new TypeFunction(arguments, Type::tvoid, 0, LINKd);
type = type->semantic(loc, sc);
assert(type->ty == Tfunction);
// Check that there is only one argument of type void*
TypeFunction *tf = (TypeFunction *)type;
if (Parameter::dim(tf->parameters) != 1)
{
error("one argument of type void* expected");
}
else
{
Parameter *a = Parameter::getNth(tf->parameters, 0);
if (!a->type->equals(Type::tvoid->pointerTo()))
error("one argument of type void* expected, not %s", a->type->toChars());
}
FuncDeclaration::semantic(sc);
}
const char *DeleteDeclaration::kind()
{
return "deallocator";
}
int DeleteDeclaration::isDelete()
{
return TRUE;
}
int DeleteDeclaration::isVirtual()
{
return FALSE;
}
int DeleteDeclaration::addPreInvariant()
{
return FALSE;
}
int DeleteDeclaration::addPostInvariant()
{
return FALSE;
}
void DeleteDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("delete");
Parameter::argsToCBuffer(buf, hgs, arguments, 0);
bodyToCBuffer(buf, hgs);
}