Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

4412 lines (3855 sloc) 132.983 kB
// 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"
#ifdef IN_GCC
#include "d-dmd-gcc.h"
#endif
/********************************* 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;
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;
flags = 0;
#endif
#if IN_LLVM
// LDC
isArrayOp = false;
allowInlining = false;
availableExternally = true; // assume this unless proven otherwise
// function types in ldc don't merge if the context parameter differs
// so we actually don't care about the function declaration, but only
// what kind of context parameter it has.
// however, this constructor is usually called from the parser, which
// unfortunately doesn't provide the information needed to get to the
// aggregate type. So we have to stick with the FuncDeclaration and
// just be sure we don't actually rely on the symbol it points to,
// but rather just the type of its context parameter.
// this means some function might have a function type pointing to
// another function declaration
if (type)
{
assert(type->ty == Tfunction && "invalid function type");
TypeFunction* tf = (TypeFunction*)type;
if (tf->funcdecl == NULL)
tf->funcdecl = this;
}
#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, int flags)
{
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);
//printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", storage_class, sc->stc, Declaration::isFinal());
if (!originalType)
originalType = type;
if (!type->deco)
{
sc = sc->push();
sc->stc |= storage_class & STCdisable; // forward to function type
TypeFunction *tf = (TypeFunction *)type;
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;
/* 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);
linkage = sc->linkage;
protection = sc->protection;
/* Purity and safety can be inferred for some functions by examining
* the function body.
*/
if (fbody &&
(isFuncLiteralDeclaration() || parent->isTemplateInstance()))
{
if (f->purity == PUREimpure) // purity not specified
flags |= FUNCFLAGpurityInprocess;
if (f->trust == TRUSTdefault)
flags |= FUNCFLAGsafetyInprocess;
if (!f->isnothrow)
flags |= FUNCFLAGnothrowInprocess;
}
if (storage_class & STCscope)
error("functions cannot be scope");
if (isAbstract() && !isVirtual())
error("non-virtual functions cannot be abstract");
if (isOverride() && !isVirtual())
error("cannot override a non-virtual function");
if ((f->isConst() || f->isImmutable()) && !isThis())
error("without 'this' cannot be const/immutable");
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 (!sd->inv)
sd->inv = isInvariantDeclaration();
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() ||
isUnitTestDeclaration() || isNewDeclaration() || isDelete())
error("constructors, destructors, postblits, invariants, unittests, new and delete functions are not allowed in interface %s", id->toChars());
if (fbody && isVirtual())
error("function body is not abstract 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)
{ int vi;
CtorDeclaration *ctor;
DtorDeclaration *dtor;
InvariantDeclaration *inv;
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;
}
inv = isInvariantDeclaration();
if (inv)
{
cd->inv = inv;
}
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;
/* 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();
f = f->overloadExactMatch(type, getModule());
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 = (FuncDeclaration *)cd->baseClass->vtbl[vi];
// This function is covariant with fdv
if (fdv->isFinal())
error("cannot override final function %s", fdv->toPrettyChars());
doesoverride = TRUE;
#if DMDV2
if (!isOverride())
warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars());
#endif
FuncDeclaration *fdc = ((Dsymbol *)cd->vtbl.data[vi])->isFuncDeclaration();
if (fdc->toParent() == parent)
{
// fdc overrides fdv exactly, then this introduces new function.
if (fdc->type->mod == fdv->type->mod && this->type->mod != fdv->type->mod)
goto Lintro;
// If both are mixins, then error.
// If either is not, the one that is not overrides the other.
if (this->parent->isClassDeclaration() && fdc->parent->isClassDeclaration())
error("multiple overrides of same function");
// if (this is mixin) && (fdc is not mixin) then fdc overrides
else if (!this->parent->isClassDeclaration() && fdc->parent->isClassDeclaration())
break;
else if (!this->parent->isClassDeclaration() // if both are mixins then error
#if DMDV2
&& !isPostBlitDeclaration()
#endif
)
error("multiple overrides of same function");
}
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 (int 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 = cd->search_correct(ident);
if (s)
error("does not override any function, did you mean '%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 (int 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, getModule());
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 (ident == Id::assign && (sd || cd))
{ // Disallow identity assignment operator.
// opAssign(...)
if (nparams == 0)
{ if (f->varargs == 1)
goto Lassignerr;
}
else
{
Parameter *arg0 = Parameter::getNth(f->parameters, 0);
Type *t0 = arg0->type->toBasetype();
Type *tb = sd ? sd->type : cd->type;
if (arg0->type->implicitConvTo(tb) ||
(sd && t0->ty == Tpointer && t0->nextOf()->implicitConvTo(tb))
)
{
if (nparams == 1)
goto Lassignerr;
Parameter *arg1 = Parameter::getNth(f->parameters, 1);
if (arg1->defaultArg)
goto Lassignerr;
}
}
}
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:
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();
return;
Lassignerr:
if (sd)
{
sd->hasIdentityAssign = 1; // don't need to generate it
goto Ldone;
}
error("identity assignment operator overload is illegal");
}
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->incontract);
if (semanticRun >= PASSsemantic3)
return;
semanticRun = PASSsemantic3;
semantic3Errors = 0;
#if IN_LLVM
if (!global.params.useAvailableExternally)
availableExternally = false;
#endif
if (!type || type->ty != Tfunction)
return;
f = (TypeFunction *)(type);
#if 0
// Check the 'throws' clause
if (fthrows)
{
for (int 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 (int 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;
sc2->incontract = 0;
#if !IN_LLVM
sc2->tf = NULL;
#else
sc2->enclosingFinally = NULL;
sc2->enclosingScopeExit = NULL;
#endif
sc2->noctor = 0;
// 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)
{
#if TARGET_NET
varArgs(sc2, f, argptr, _arguments);
#else
Type *t;
#if !IN_GCC && !IN_LLVM
if (global.params.is64bit)
{ // 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(0, 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(0, 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
#ifdef IN_GCC
t = d_gcc_builtin_va_list_d_type;
#else
t = Type::tvoid->pointerTo();
#endif
argptr = new VarDeclaration(0, t, Id::_argptr, NULL);
argptr->semantic(sc2);
sc2->insert(argptr);
argptr->parent = this;
}
#endif
}
#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(0, 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.
// 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;
//if (isPure())
//vtype = vtype->addMod(MODconst);
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(0, 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
InvariantDeclaration *inv = ad->inv;
ClassDeclaration *cd = ad->isClassDeclaration();
while (!inv && cd)
{
cd = cd->baseClass;
if (!cd)
break;
inv = cd->inv;
}
if (inv)
{
e = new DsymbolExp(0, inv);
e = new CallExp(0, e);
e = e->semantic(sc2);
}
}
else
{ // Call invariant virtually
#if IN_LLVM
// We actually need a valid 'var' for codegen.
ThisExp* tv = new ThisExp(0);
tv->var = vthis;
Expression *v = tv;
#else
Expression *v = new ThisExp(0);
#endif
v->type = vthis->type;
#if STRUCTTHISREF
if (ad->isStructDeclaration())
v = v->addressOf(sc);
#endif
Expression *se = new StringExp(0, (char *)"null this");
se = se->semantic(sc);
se->type = Type::tchar->arrayOf();
e = new AssertExp(loc, v, se);
}
if (e)
fpreinv = new ExpStatement(0, e);
}
// Postcondition invariant
Statement *fpostinv = NULL;
if (addPostInvariant())
{
Expression *e = NULL;
if (isCtorDeclaration())
{
// Call invariant directly only if it exists
InvariantDeclaration *inv = ad->inv;
ClassDeclaration *cd = ad->isClassDeclaration();
while (!inv && cd)
{
cd = cd->baseClass;
if (!cd)
break;
inv = cd->inv;
}
if (inv)
{
e = new DsymbolExp(0, inv);
e = new CallExp(0, e);
e = e->semantic(sc2);
}
}
else
{ // Call invariant virtually
#if IN_LLVM
// We actually need a valid 'var' for codegen.
ThisExp* tv = new ThisExp(0);
tv->var = vthis;
Expression *v = tv;
#else
Expression *v = new ThisExp(0);
#endif
v->type = vthis->type;
#if STRUCTTHISREF
if (ad->isStructDeclaration())
v = v->addressOf(sc);
#endif
e = new AssertExp(0, v);
}
if (e)
fpostinv = new ExpStatement(0, 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 = isAggregateMember();
/* 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(0, new Statements());
if (inferRetType)
{ // If no return type inferred yet, then infer a void
if (!type->nextOf())
{
((TypeFunction *)type)->next = Type::tvoid;
//type = type->semantic(loc, sc); // Removed with 6902
}
f = (TypeFunction *)type;
}
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)
{
//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 final field %s", v->toChars());
else if (v->storage_class & STCnodefaultctor)
error("field %s must be initialized in constructor", v->toChars());
}
}
}
if (cd &&
!(sc2->callSuper & CSXany_ctor) &&
cd->baseClass && cd->baseClass->ctor)
{
sc2->callSuper = 0;
// Insert implicit super() at start of fbody
Expression *e1 = new SuperExp(0);
Expression *e = new CallExp(0, e1);
e = e->trySemantic(sc2);
if (!e)
error("no match for implicit super() call in constructor");
else
{
Statement *s = new ExpStatement(0, e);
fbody = new CompoundStatement(0, s, fbody);
}
}
}
else if (fes)
{ // For foreach(){} body, append a return 0;
Expression *e = new IntegerExp(0);
Statement *s = new ReturnStatement(0, e);
fbody = new CompoundStatement(0, 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 ? fbody->blockExit(f->isnothrow) : BEfallthru;
if (f->isnothrow && (global.errors != nothrowErrors) )
error("'%s' is nothrow yet may throw", toChars());
if (flags & FUNCFLAGnothrowInprocess)
{
flags &= ~FUNCFLAGnothrowInprocess;
if (!(blockexit & BEthrow))
f->isnothrow = TRUE;
}
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(0, e, type->nextOf()->defaultInit());
e = e->semantic(sc2);
Statement *s = new ExpStatement(0, e);
fbody = new CompoundStatement(0, 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->incontract++;
// 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
DsymbolTable *labtab_save = labtab;
labtab = NULL; // so in contract can't refer to out/body labels
freq = freq->semantic(sc2);
labtab = labtab_save;
sc2->incontract--;
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->incontract++;
// BUG: need to treat parameters as const
// BUG: need to disallow returns and throws
DsymbolTable *labtab_save = labtab;
labtab = NULL; // so out contract can't refer to in/body labels
fens = fens->semantic(sc2);
labtab = labtab_save;
sc2->incontract--;
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(0, 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)
{ // Initialize _argptr to point to v_argsave
Expression *e1 = new VarExp(0, argptr);
Expression *e = new SymOffExp(0, v_argsave, 6*8 + 8*16);
e->type = argptr->type;
e = new AssignExp(0, e1, e);
e = e->semantic(sc);
a->push(new ExpStatement(0, e));
}
else
{ // Initialize _argptr to point past non-variadic arg
VarDeclaration *p;
unsigned offset = 0;
Expression *e1 = new VarExp(0, argptr);
// Find the last non-ref parameter
if (parameters && parameters->dim)
{
int 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))
{
--lastNonref;
offset += PTRSIZE;
if (lastNonref < 0)
{
p = v_arguments;
break;
}
p = (*parameters)[lastNonref];
}
}
else
p = v_arguments; // last parameter is _arguments[]
if (p->storage_class & STClazy)
// If the last parameter is lazy, it's the size of a delegate
offset += PTRSIZE * 2;
else
offset += p->type->size();
offset = (offset + PTRSIZE - 1) & ~(PTRSIZE - 1); // assume stack aligns on pointer size
Expression *e = new SymOffExp(0, p, offset);
e->type = Type::tvoidptr;
//e = e->semantic(sc);
e = new AssignExp(0, e1, e);
e->type = t;
a->push(new ExpStatement(0, 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(0, v_arguments);
e = new DotIdExp(0, e, Id::elements);
Expression *e1 = new VarExp(0, _arguments);
e = new ConstructExp(0, e1, e);
e = e->semantic(sc2);
a->push(new ExpStatement(0, 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(0, freq, fpreinv);
freq->incontract = 1;
a->push(freq);
}
if (fbody)
a->push(fbody);
if (fens || fpostinv)
{
if (!fens)
fens = fpostinv;
else if (fpostinv)
fens = new CompoundStatement(0, fpostinv, fens);
LabelStatement *ls = new LabelStatement(0, 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(0);
te->type = vthis->type;
te->var = vthis;
e = te;
} else {
e = new VarExp(0, vresult);
}
#else
// Create: return vresult;
Expression *e = new VarExp(0, vresult);
#endif
if (tintro)
{ e = e->implicitCastTo(sc, tintro->nextOf());
e = e->semantic(sc);
}
ReturnStatement *s = new ReturnStatement(0, e);
a->push(s);
}
}
if (isMain() && type->nextOf()->ty == Tvoid)
{ // Add a return 0; statement
Statement *s = new ReturnStatement(0, new IntegerExp(0));
a->push(s);
}
fbody = new CompoundStatement(0, 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 !SARRAYVALUE
/* Don't do this for static arrays, since static
* arrays are called by reference. Remove this
* when we change them to call by value.
*/
if (v->type->toBasetype()->ty == Tsarray)
continue;
#endif
if (v->noscope)
continue;
Expression *e = v->edtor;
if (e)
{ Statement *s = new ExpStatement(0, e);
s = s->semantic(sc2);
if (fbody->blockExit(f->isnothrow) == BEfallthru)
fbody = new CompoundStatement(0, fbody, s);
else
fbody = new TryFinallyStatement(0, fbody, s);
}
}
}
#endif
#if 1
if (isSynchronized())
{ /* Wrap the entire function body in a synchronized statement
*/
AggregateDeclaration *ad = isThis();
ClassDeclaration *cd = ad ? ad->isClassDeclaration() : parent->isClassDeclaration();
if (cd)
{
#if TARGET_WINDOS
if (/*config.flags2 & CFG2seh &&*/ // always on for WINDOS
!isStatic() && !fbody->usesEH())
{
/* The back end uses the "jmonitor" hack for syncing;
* no need to do the sync at this level.
*/
}
else
#endif
{
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());
}
}
#endif
}
sc2->callSuper = 0;
sc2->pop();
}
/* If function survived being marked as impure, then it is pure
*/
if (flags & FUNCFLAGpurityInprocess)
{
flags &= ~FUNCFLAGpurityInprocess;
f->purity = PUREfwdref;
}
if (flags & FUNCFLAGsafetyInprocess)
{
flags &= ~FUNCFLAGsafetyInprocess;
f->trust = TRUSTsafe;
}
// Do semantic type AFTER pure/nothrow inference.
if (inferRetType)
{
type = type->semantic(loc, sc);
}
if (global.gag && global.errors != nerrors)
semanticRun = PASSsemanticdone; // Ensure errors get reported again
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);
}
void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
//printf("FuncDeclaration::toCBuffer() '%s'\n", toChars());
StorageClassDeclaration::stcToCBuffer(buf, storage_class);
type->toCBuffer(buf, ident, hgs);
bodyToCBuffer(buf, hgs);
}
VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad)
{
if (ad)
{ VarDeclaration *v;
{
assert(ad->handle);
Type *thandle = ad->handle;
#if STRUCTTHISREF
thandle = thandle->addMod(type->mod);
thandle = thandle->addStorageClass(storage_class);
//if (isPure())
//thandle = thandle->addMod(MODconst);
#else
if (storage_class & STCconst || type->isConst())
{
assert(0); // BUG: shared not handled
if (thandle->ty == Tclass)
thandle = thandle->constOf();
else
{ assert(thandle->ty == Tpointer);
thandle = thandle->nextOf()->constOf()->pointerTo();
}
}
else if (storage_class & STCimmutable || type->isImmutable())
{
if (thandle->ty == Tclass)
thandle = thandle->invariantOf();
else
{ assert(thandle->ty == Tpointer);
thandle = thandle->nextOf()->invariantOf()->pointerTo();
}
}
else if (storage_class & STCshared || type->isShared())
{
assert(0); // not implemented
}
#endif
v = new ThisDeclaration(loc, thandle);
//v = new ThisDeclaration(loc, isCtorDeclaration() ? ad->handle : thandle);
v->storage_class |= STCparameter;
#if STRUCTTHISREF
if (thandle->ty == Tstruct)
v->storage_class |= STCref;
#endif
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 || hgs->tpltMember || canInline(1,1,1))
)
{ 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();
fbody->toCBuffer(buf, hgs);
buf->writebyte('}');
buf->writenl();
}
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 (int 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 (int 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 *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), params);
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)
{
FuncDeclaration *f;
AliasDeclaration *a;
//printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s->toChars(), toChars());
a = s->isAliasDeclaration();
if (a)
{
if (overnext)
return overnext->overloadInsert(a);
if (!a->aliassym && a->type->ty != Tident && a->type->ty != Tinstance)
{
//printf("\ta = '%s'\n", a->type->toChars());
return FALSE;
}
overnext = a;
//printf("\ttrue: no conflict\n");
return TRUE;
}
f = s->isFuncDeclaration();
if (!f)
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("f->type = %s\n", f->type->toChars());
}
if (type && f->type && // can be NULL for overloaded constructors
f->type->covariant(type) &&
f->type->mod == type->mod &&
!isFuncAliasDeclaration())
{
//printf("\tfalse: conflict %s\n", kind());
return FALSE;
}
#endif
if (overnext)
return overnext->overloadInsert(f);
overnext = f;
//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, Module* from)
{
Param1 p;
p.t = t;
p.f = NULL;
overloadApply(this, &fp1, &p);
return p.f;
}
/********************************************
* Decide which function matches the arguments best.
*/
struct Param2
{
Match *m;
#if DMDV2
Expression *ethis;
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;
MATCH match;
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, don't worry about the right type of ethis. It's a problem
* anyway, because the constructor attribute may not match the ethis attribute,
* but we don't care because the attribute on the ethis doesn't matter until
* after it's constructed.
*/
match = (MATCH) tf->callMatch(f->needThis() && !f->isCtorDeclaration() ? p->ethis : NULL, 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;
}
#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,
Expression *ethis, Expressions *arguments, Module* from)
{
Param2 p;
p.m = m;
p.ethis = ethis;
p.property = 0;
p.arguments = arguments;
overloadApply(fstart, &fp2, &p);
}
FuncDeclaration *FuncDeclaration::overloadResolve(Loc loc, Expression *ethis, Expressions *arguments, int flags, Module* from)
{
TypeFunction *tf;
Match m;
#if 0
printf("FuncDeclaration::overloadResolve('%s')\n", toChars());
if (arguments)
{ int i;
for (i = 0; i < arguments->dim; i++)
{ Expression *arg;
arg = (*arguments)[i];
assert(arg->type);
printf("\t%s: ", arg->toChars());
arg->type->print();
}
}
#endif
memset(&m, 0, sizeof(m));
m.last = MATCHnomatch;
overloadResolveX(&m, this, ethis, arguments, from);
if (m.count == 1) // exactly one match
{
return m.lastf;
}
else
{
OutBuffer buf;
buf.writeByte('(');
if (arguments)
{
HdrGenState hgs;
argExpTypesToCBuffer(&buf, arguments, &hgs);
buf.writeByte(')');
if (ethis)
ethis->type->modToBuffer(&buf);
}
else
buf.writeByte(')');
if (m.last == MATCHnomatch)
{
if (flags & 1) // if do not print error messages
return NULL; // no match
tf = (TypeFunction *)type;
OutBuffer buf2;
tf->modToBuffer(&buf2);
//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),
buf2.toChars(),
buf.toChars());
return m.anyf; // as long as it's not a FuncAliasDeclaration
}
else
{
#if 1
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%s\nand:\n\t%s%s",
buf.toChars(),
m.lastf->toPrettyChars(), Parameter::argsTypesToChars(t1->parameters, t1->varargs),
m.nextf->toPrettyChars(), Parameter::argsTypesToChars(t2->parameters, t2->varargs));
#else
error(loc, "overloads %s and %s both match argument list for %s",
m.lastf->type->toChars(),
m.nextf->type->toChars(),
m.lastf->toChars());
#endif
return m.lastf;
}
}
}
/*************************************
* 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())
{
if (tf->mod != tg->mod)
{
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 (int u = 0; u < nfparams; u++)
{
Parameter *p = Parameter::getNth(tf->parameters, u);
Expression *e;
if (p->storageClass & (STCref | STCout))
{
e = new IdentifierExp(0, p->ident);
e->type = p->type;
}
else
e = p->type->defaultInitLiteral(0);
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.
* sc instantiation scope
* loc instantiation location
* targsi initial list of template arguments
* ethis if !NULL, the 'this' pointer argument
* fargs arguments to function
* flags 1: do not issue error message on no match, just return NULL
*/
FuncDeclaration *resolveFuncCall(Scope *sc, Loc loc, Dsymbol *s,
Objects *tiargs,
Expression *ethis,
Expressions *arguments,
int flags)
{
if (!s)
return NULL; // no match
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
f = f->overloadResolve(loc, ethis, arguments);
else
{ TemplateDeclaration *td = s->isTemplateDeclaration();
assert(td);
f = td->deduceFunctionTemplate(sc, loc, tiargs, NULL, 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(0, 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(0, fbody, s);
}
}
const char *FuncDeclaration::toPrettyChars()
{
if (isMain())
return "D main";
else
return Dsymbol::toPrettyChars();
}
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 && needThis())
{ // The attribute of the 'this' reference affects purity strength
if (type->mod & (MODimmutable | MODwild))
;
else if (type->mod & MODconst && 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;
}
// 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
{
tf = new TypeFunction(args, treturn, 0, LINKc);
fd = new FuncDeclaration(0, 0, id, STCstatic, tf);
fd->protection = PROTpublic;
fd->linkage = LINKc;
st->insert(fd);
}
return fd;
}
const char *FuncDeclaration::kind()
{
return "function";
}
void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
{
//printf("FuncDeclaration::checkNestedReference() %s\n", toChars());
if (parent && parent != sc->parent && this->isNested() &&
this->ident != Id::require && this->ident != Id::ensure)
{
// The function that this function is in
FuncDeclaration *fdv = toParent()->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 (fdv && fdthis && fdv != fdthis)
{
int lv = fdthis->getLevel(loc, sc, fdv);
if (lv == -1)
return; // OK
if (lv == 0)
return; // OK
// 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;
}
}
}
/*******************************
* 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 any function that:
* 1) is a virtual function
* 2) has its address taken
* 3) has a parent that escapes
* -or-
* 4) 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());
for (int i = 0; i < closureVars.dim; i++)
{ VarDeclaration *v = closureVars[i];
assert(v->isVarDeclaration());
//printf("\tv = %s\n", v->toChars());
for (int j = 0; j < v->nestedrefs.dim; j++)
{ FuncDeclaration *f = v->nestedrefs[j];
assert(f != this);
//printf("\t\tf = %s, %d, %p, %d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf);
if (f->isThis() || f->tookAddressOf)
goto Lyes; // assume f escapes this function's scope
// Look to see if any parents of f that are below this escape
for (Dsymbol *s = f->parent; s && s != this; s = s->parent)
{
f = s->isFuncDeclaration();
if (f && (f->isThis() || f->tookAddressOf))
goto Lyes;
}
}
}
/* Look for case (4)
*/
if (closureVars.dim)
{
assert(type->ty == Tfunction);
Type *tret = ((TypeFunction *)type)->next;
assert(tret);
tret = tret->toBasetype();
if (tret->ty == Tclass || tret->ty == Tstruct)
{ Dsymbol *st = tret->toDsymbol(NULL);
for (Dsymbol *s = st->parent; s; s = s->parent)
{
if (s == this)
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;
#if IN_LLVM
importprot = PROTundefined;
#endif
this->hasOverloads = hasOverloads;
if (hasOverloads)
{
if (FuncAliasDeclaration *fad = funcalias->isFuncAliasDeclaration())
this->hasOverloads = fad->hasOverloads;
}
else
{ // for internal use
assert(!funcalias->isFuncAliasDeclaration());
this->hasOverloads = 0;
}
}
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());
}
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)
{
buf->writestring(kind());
buf->writeByte(' ');
type->toCBuffer(buf, NULL, hgs);
bodyToCBuffer(buf, hgs);
}
/********************************* 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;
parent = sc->parent;
Dsymbol *parent = toParent2();
Type *tret;
AggregateDeclaration *ad = parent->isAggregateDeclaration();
if (!ad || parent->isUnionDeclaration())
{
error("constructors are only for class or struct definitions");
fatal();
tret = Type::tvoid;
}
else
{ tret = ad->handle;
assert(tret);
tret = tret->addStorageClass(storage_class | sc->stc);
tret = tret->addMod(type->mod);
}
tf->next = tret;
type = type->semantic(loc, sc);
#if STRUCTTHISREF
if (ad && ad->isStructDeclaration())
{ if (!originalType)
originalType = type->syntaxCopy();
((TypeFunction *)type)->isref = 1;
}
#endif
if (!originalType)
originalType = type;
// Append:
// return this;
// to the function body
if (fbody && semanticRun < PASSsemantic)
{
ThisExp *e = new ThisExp(loc);
if (parent->isClassDeclaration())
e->type = tret;
Statement *s = new ReturnStatement(loc, e);
fbody = new CompoundStatement(loc, fbody, s);
}
FuncDeclaration::semantic(sc);
sc->pop();
// See if it's the default constructor
if (ad && tf->varargs == 0 && Parameter::dim(tf->parameters) == 0)
{
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)
: FuncDeclaration(loc, endloc, Id::_postblit, stc, NULL)
{
}
PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, Identifier *id)
: FuncDeclaration(loc, endloc, id, STCundefined, NULL)
{
}
Dsymbol *PostBlitDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, 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, Identifier *id)
: FuncDeclaration(loc, endloc, id, STCundefined, NULL)
{
}
Dsymbol *DtorDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
DtorDeclaration *dd = new DtorDeclaration(loc, endloc, 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);
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(0, Type::tint32, id, NULL);
v->storage_class = isSharedStaticCtorDeclaration() ? STCstatic : STCtls;
Statements *sa = new Statements();
Statement *s = new ExpStatement(0, v);
sa->push(s);
Expression *e = new IdentifierExp(0, id);
e = new AddAssignExp(0, e, new IntegerExp(1));
e = new EqualExp(TOKnotequal, 0, e, new IntegerExp(1));
s = new IfStatement(0, NULL, e, new ReturnStatement(0, NULL), NULL);
sa->push(s);
if (fbody)
sa->push(fbody);
fbody = new CompoundStatement(0, 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());
#ifdef IN_GCC
m->strictlyneedmoduleinfo = 1;
#endif
}
}
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(0, Type::tint32, id, NULL);
v->storage_class = isSharedStaticDtorDeclaration() ? STCstatic : STCtls;
Statements *sa = new Statements();
Statement *s = new ExpStatement(0, v);
sa->push(s);
Expression *e = new IdentifierExp(0, id);
e = new AddAssignExp(0, e, new IntegerExp((uint64_t)-1));
e = new EqualExp(TOKnotequal, 0, e, new IntegerExp(0));
s = new IfStatement(0, NULL, e, new ReturnStatement(0, NULL), NULL);
sa->push(s);
if (fbody)
sa->push(fbody);
fbody = new CompoundStatement(0, 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());
#ifdef IN_GCC
m->strictlyneedmoduleinfo = 1;
#endif
}
}
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)
: FuncDeclaration(loc, endloc, Id::classInvariant, STCundefined, NULL)
{
}
Dsymbol *InvariantDeclaration::syntaxCopy(Dsymbol *s)
{
InvariantDeclaration *id;
assert(!s);
id = new InvariantDeclaration(loc, endloc);
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;
}
else if (ad->inv && ad->inv != this && semanticRun < PASSsemantic)
{
error("more than one invariant for %s", ad->toChars());
}
ad->inv = this;
if (!type)
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd);
sc = sc->push();
sc->stc &= ~STCstatic; // not a static invariant
sc->stc |= STCconst; // invariant() is always const
sc->incontract++;
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()
{
return Lexer::uniqueId("__unittest");
}
UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc)
: FuncDeclaration(loc, endloc, unitTestId(), STCundefined, NULL)
{
}
Dsymbol *UnitTestDeclaration::syntaxCopy(Dsymbol *s)
{
UnitTestDeclaration *utd;
assert(!s);
utd = new UnitTestDeclaration(loc, endloc);
return FuncDeclaration::syntaxCopy(utd);
}
void UnitTestDeclaration::semantic(Scope *sc)
{
if (scope)
{ sc = scope;
scope = NULL;
}
#if IN_LLVM
if (global.params.useUnitTests && sc->module->isRoot)
#else
if (global.params.useUnitTests)
#endif
{
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