mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-13 03:13:13 +01:00
This commit fundamentally changes the way symbol emission in LDC works: Previously, whenever a declaration was used in some way, the compiler would check whether it actually needs to be defined in the currently processed module, based only on the symbol itself. This lack of contextual information proved to be a major problem in correctly handling emission of templates (see e.g. #454). Now, the DtoResolve…() family of functions and similar only ever declare the symbols, and definition is handled by doing a single pass over Module::members for the root module. This is the same strategy that DMD uses as well, which should also reduce the maintainance burden down the road (which is important as during the last few releases, there was pretty much always a symbol emission related problem slowing us down). Our old approach might have been a bit better tuned w.r.t. avoiding emission of unneeded template instances, but 2.064 will bring improvements here (DMD: FuncDeclaration::toObjFile). Barring such issues, the change shoud also marginally improve compile times because of declarations no longer being emitted when they are not needed. In the future, we should also consider refactoring the code so that it no longer directly accesses Dsymbol::ir but uses wrapper functions that ensure that the appropriate DtoResolve…() function has been called. GitHub: Fixes #454.
4979 lines
151 KiB
C
4979 lines
151 KiB
C
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2012 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// License for redistribution is by either the Artistic License
|
|
// in artistic.txt, or the GNU General Public License in gnu.txt.
|
|
// See the included readme.txt for details.
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
|
|
#include "mars.h"
|
|
#include "init.h"
|
|
#include "declaration.h"
|
|
#include "attrib.h"
|
|
#include "expression.h"
|
|
#include "scope.h"
|
|
#include "mtype.h"
|
|
#include "aggregate.h"
|
|
#include "identifier.h"
|
|
#include "id.h"
|
|
#include "module.h"
|
|
#include "statement.h"
|
|
#include "template.h"
|
|
#include "hdrgen.h"
|
|
#include "target.h"
|
|
|
|
void functionToCBuffer2(TypeFunction *t, OutBuffer *buf, HdrGenState *hgs, int mod, const char *kind);
|
|
|
|
/********************************* FuncDeclaration ****************************/
|
|
|
|
FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type)
|
|
: Declaration(id)
|
|
{
|
|
//printf("FuncDeclaration(id = '%s', type = %p)\n", id->toChars(), type);
|
|
//printf("storage_class = x%x\n", storage_class);
|
|
this->storage_class = storage_class;
|
|
this->type = type;
|
|
if (type)
|
|
this->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR);
|
|
this->loc = loc;
|
|
this->endloc = endloc;
|
|
fthrows = NULL;
|
|
frequire = NULL;
|
|
fdrequire = NULL;
|
|
fdensure = NULL;
|
|
fdrequireParams = NULL;
|
|
fdensureParams = NULL;
|
|
outId = NULL;
|
|
vresult = NULL;
|
|
returnLabel = NULL;
|
|
scout = NULL;
|
|
fensure = NULL;
|
|
fbody = NULL;
|
|
localsymtab = NULL;
|
|
vthis = NULL;
|
|
v_arguments = NULL;
|
|
#ifdef IN_GCC
|
|
v_argptr = NULL;
|
|
v_arguments_var = NULL;
|
|
#endif
|
|
v_argsave = NULL;
|
|
parameters = NULL;
|
|
labtab = NULL;
|
|
overnext = NULL;
|
|
vtblIndex = -1;
|
|
hasReturnExp = 0;
|
|
naked = 0;
|
|
inlineStatusExp = ILSuninitialized;
|
|
inlineStatusStmt = ILSuninitialized;
|
|
inlineNest = 0;
|
|
isArrayOp = 0;
|
|
dArrayOp = NULL;
|
|
semanticRun = PASSinit;
|
|
semantic3Errors = 0;
|
|
#if DMDV1
|
|
nestedFrameRef = 0;
|
|
#endif
|
|
fes = NULL;
|
|
introducing = 0;
|
|
tintro = NULL;
|
|
/* The type given for "infer the return type" is a TypeFunction with
|
|
* NULL for the return type.
|
|
*/
|
|
inferRetType = (type && type->nextOf() == NULL);
|
|
storage_class2 = 0;
|
|
hasReturnExp = 0;
|
|
nrvo_can = 1;
|
|
nrvo_var = NULL;
|
|
#if IN_DMD
|
|
shidden = NULL;
|
|
#endif
|
|
#if DMDV2
|
|
builtin = BUILTINunknown;
|
|
tookAddressOf = 0;
|
|
requiresClosure = false;
|
|
flags = 0;
|
|
#endif
|
|
returns = NULL;
|
|
|
|
#if IN_LLVM
|
|
// LDC
|
|
isArrayOp = false;
|
|
allowInlining = false;
|
|
neverInline = false;
|
|
availableExternally = true; // assume this unless proven otherwise
|
|
#endif
|
|
}
|
|
|
|
Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
FuncDeclaration *f;
|
|
|
|
//printf("FuncDeclaration::syntaxCopy('%s')\n", toChars());
|
|
if (s)
|
|
f = (FuncDeclaration *)s;
|
|
else
|
|
f = new FuncDeclaration(loc, endloc, ident, storage_class, type->syntaxCopy());
|
|
f->outId = outId;
|
|
f->frequire = frequire ? frequire->syntaxCopy() : NULL;
|
|
f->fensure = fensure ? fensure->syntaxCopy() : NULL;
|
|
f->fbody = fbody ? fbody->syntaxCopy() : NULL;
|
|
assert(!fthrows); // deprecated
|
|
|
|
#if IN_LLVM
|
|
f->intrinsicName = intrinsicName;
|
|
#endif
|
|
|
|
return f;
|
|
}
|
|
|
|
#if IN_LLVM
|
|
static int outToRefDg(void *ctx, size_t n, Parameter *p)
|
|
{
|
|
if (p->storageClass & STCout)
|
|
{
|
|
// Cannot just use syntaxCopy() here, because it would cause the
|
|
// parameter type to be semantic()ed again, in the wrong scope. So,
|
|
// just copy the outer layer to modify the storage class.
|
|
void *cpy = malloc(sizeof(Parameter));
|
|
memcpy(cpy, (void *)p, sizeof(Parameter));
|
|
p = (Parameter *)cpy;
|
|
p->storageClass &= ~STCout;
|
|
p->storageClass |= STCref;
|
|
}
|
|
((Parameters *)ctx)->push(p);
|
|
return 0;
|
|
}
|
|
static Parameters *outToRef(Parameters* params)
|
|
{
|
|
Parameters *result = new Parameters();
|
|
Parameter::foreach(params, &outToRefDg, result);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
// Do the semantic analysis on the external interface to the function.
|
|
|
|
void FuncDeclaration::semantic(Scope *sc)
|
|
{ TypeFunction *f;
|
|
AggregateDeclaration *ad;
|
|
StructDeclaration *sd;
|
|
ClassDeclaration *cd;
|
|
InterfaceDeclaration *id;
|
|
Dsymbol *pd;
|
|
bool doesoverride;
|
|
|
|
#if 0
|
|
printf("FuncDeclaration::semantic(sc = %p, this = %p, '%s', linkage = %d)\n", sc, this, toPrettyChars(), sc->linkage);
|
|
if (isFuncLiteralDeclaration())
|
|
printf("\tFuncLiteralDeclaration()\n");
|
|
printf("sc->parent = %s, parent = %s\n", sc->parent->toChars(), parent ? parent->toChars() : "");
|
|
printf("type: %p, %s\n", type, type->toChars());
|
|
#endif
|
|
|
|
if (semanticRun != PASSinit && isFuncLiteralDeclaration())
|
|
{
|
|
/* Member functions that have return types that are
|
|
* forward references can have semantic() run more than
|
|
* once on them.
|
|
* See test\interface2.d, test20
|
|
*/
|
|
return;
|
|
}
|
|
|
|
parent = sc->parent;
|
|
Dsymbol *parent = toParent();
|
|
|
|
if (semanticRun >= PASSsemanticdone)
|
|
{
|
|
if (!parent->isClassDeclaration())
|
|
{
|
|
return;
|
|
}
|
|
// need to re-run semantic() in order to set the class's vtbl[]
|
|
}
|
|
else
|
|
{
|
|
assert(semanticRun <= PASSsemantic);
|
|
semanticRun = PASSsemantic;
|
|
}
|
|
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
|
|
unsigned dprogress_save = Module::dprogress;
|
|
|
|
foverrides.setDim(0); // reset in case semantic() is being retried for this function
|
|
|
|
storage_class |= sc->stc & ~STCref;
|
|
ad = isThis();
|
|
if (ad)
|
|
{
|
|
storage_class |= ad->storage_class & (STC_TYPECTOR | STCsynchronized);
|
|
|
|
if (StructDeclaration *sd = ad->isStructDeclaration())
|
|
sd->makeNested();
|
|
}
|
|
// Remove prefix storage classes silently.
|
|
if ((storage_class & STC_TYPECTOR) && !(ad || isNested()))
|
|
storage_class &= ~STC_TYPECTOR;
|
|
|
|
//printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", storage_class, sc->stc, Declaration::isFinal());
|
|
|
|
FuncLiteralDeclaration *fld = isFuncLiteralDeclaration();
|
|
if (fld && fld->treq)
|
|
linkage = ((TypeFunction *)fld->treq->nextOf())->linkage;
|
|
else
|
|
linkage = sc->linkage;
|
|
protection = sc->protection;
|
|
userAttributes = sc->userAttributes;
|
|
|
|
if (!originalType)
|
|
originalType = type->syntaxCopy();
|
|
if (!type->deco)
|
|
{
|
|
sc = sc->push();
|
|
sc->stc |= storage_class & STCdisable; // forward to function type
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
#if 1
|
|
/* If the parent is @safe, then this function defaults to safe
|
|
* too.
|
|
* If the parent's @safe-ty is inferred, then this function's @safe-ty needs
|
|
* to be inferred first.
|
|
*/
|
|
if (tf->trust == TRUSTdefault &&
|
|
!(//isFuncLiteralDeclaration() ||
|
|
parent->isTemplateInstance() ||
|
|
ad && ad->parent && ad->parent->isTemplateInstance()))
|
|
{
|
|
for (Dsymbol *p = sc->func; p; p = p->toParent2())
|
|
{ FuncDeclaration *fd = p->isFuncDeclaration();
|
|
if (fd)
|
|
{
|
|
if (fd->isSafeBypassingInference())
|
|
tf->trust = TRUSTsafe; // default to @safe
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (tf->isref) sc->stc |= STCref;
|
|
if (tf->isnothrow) sc->stc |= STCnothrow;
|
|
if (tf->isproperty) sc->stc |= STCproperty;
|
|
if (tf->purity == PUREfwdref) sc->stc |= STCpure;
|
|
if (tf->trust == TRUSTsafe) sc->stc |= STCsafe;
|
|
if (tf->trust == TRUSTsystem) sc->stc |= STCsystem;
|
|
if (tf->trust == TRUSTtrusted) sc->stc |= STCtrusted;
|
|
|
|
if (isCtorDeclaration())
|
|
{
|
|
sc->flags |= SCOPEctor;
|
|
|
|
Type *tret;
|
|
if (!ad || parent->isUnionDeclaration())
|
|
{
|
|
error("constructors are only for class or struct definitions");
|
|
tret = Type::tvoid;
|
|
}
|
|
else
|
|
{ tret = ad->handle;
|
|
assert(tret);
|
|
tret = tret->addStorageClass(storage_class | sc->stc);
|
|
tret = tret->addMod(type->mod);
|
|
}
|
|
tf->next = tret;
|
|
|
|
if (ad && ad->isStructDeclaration())
|
|
sc->stc |= STCref;
|
|
}
|
|
|
|
sc->linkage = linkage;
|
|
|
|
if (!tf->isNaked() && !(isThis() || isNested()))
|
|
{
|
|
OutBuffer buf;
|
|
MODtoBuffer(&buf, tf->mod);
|
|
error("without 'this' cannot be %s", buf.toChars());
|
|
tf->mod = 0; // remove qualifiers
|
|
}
|
|
|
|
/* Apply const, immutable, wild and shared storage class
|
|
* to the function type. Do this before type semantic.
|
|
*/
|
|
StorageClass stc = storage_class;
|
|
if (type->isImmutable())
|
|
stc |= STCimmutable;
|
|
if (type->isConst())
|
|
stc |= STCconst;
|
|
if (type->isShared() || storage_class & STCsynchronized)
|
|
stc |= STCshared;
|
|
if (type->isWild())
|
|
stc |= STCwild;
|
|
switch (stc & STC_TYPECTOR)
|
|
{
|
|
case STCimmutable:
|
|
case STCimmutable | STCconst:
|
|
case STCimmutable | STCconst | STCshared:
|
|
case STCimmutable | STCshared:
|
|
case STCimmutable | STCwild:
|
|
case STCimmutable | STCconst | STCwild:
|
|
case STCimmutable | STCconst | STCshared | STCwild:
|
|
case STCimmutable | STCshared | STCwild:
|
|
// Don't use toInvariant(), as that will do a merge()
|
|
type = type->makeInvariant();
|
|
break;
|
|
|
|
case STCconst:
|
|
case STCconst | STCwild:
|
|
type = type->makeConst();
|
|
break;
|
|
|
|
case STCshared | STCconst:
|
|
case STCshared | STCconst | STCwild:
|
|
type = type->makeSharedConst();
|
|
break;
|
|
|
|
case STCshared:
|
|
type = type->makeShared();
|
|
break;
|
|
|
|
case STCwild:
|
|
type = type->makeWild();
|
|
break;
|
|
|
|
case STCshared | STCwild:
|
|
type = type->makeSharedWild();
|
|
break;
|
|
|
|
case 0:
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
type = type->semantic(loc, sc);
|
|
sc = sc->pop();
|
|
}
|
|
|
|
storage_class &= ~STCref;
|
|
if (type->ty != Tfunction)
|
|
{
|
|
error("%s must be a function instead of %s", toChars(), type->toChars());
|
|
return;
|
|
}
|
|
f = (TypeFunction *)type;
|
|
size_t nparams = Parameter::dim(f->parameters);
|
|
|
|
if (storage_class & STCscope)
|
|
error("functions cannot be scope");
|
|
|
|
if (isAbstract() && !isVirtual())
|
|
{
|
|
const char *sfunc;
|
|
if (isStatic())
|
|
sfunc = "static";
|
|
else if (protection == PROTprivate || protection == PROTpackage)
|
|
sfunc = Pprotectionnames[protection];
|
|
else
|
|
sfunc = "non-virtual";
|
|
error("%s functions cannot be abstract", sfunc);
|
|
}
|
|
|
|
if (isOverride() && !isVirtual())
|
|
error("cannot override a non-virtual function");
|
|
|
|
if (isAbstract() && isFinal())
|
|
error("cannot be both final and abstract");
|
|
#if 0
|
|
if (isAbstract() && fbody)
|
|
error("abstract functions cannot have bodies");
|
|
#endif
|
|
|
|
#if 0
|
|
if (isStaticConstructor() || isStaticDestructor())
|
|
{
|
|
if (!isStatic() || type->nextOf()->ty != Tvoid)
|
|
error("static constructors / destructors must be static void");
|
|
if (f->arguments && f->arguments->dim)
|
|
error("static constructors / destructors must have empty parameter list");
|
|
// BUG: check for invalid storage classes
|
|
}
|
|
#endif
|
|
|
|
sd = parent->isStructDeclaration();
|
|
if (sd)
|
|
{
|
|
if (isCtorDeclaration())
|
|
{
|
|
goto Ldone;
|
|
}
|
|
#if 0
|
|
// Verify no constructors, destructors, etc.
|
|
if (isCtorDeclaration()
|
|
//||isDtorDeclaration()
|
|
//|| isInvariantDeclaration()
|
|
//|| isUnitTestDeclaration()
|
|
)
|
|
{
|
|
error("special member functions not allowed for %ss", sd->kind());
|
|
}
|
|
|
|
if (isInvariantDeclaration())
|
|
sd->invs.push(this);
|
|
|
|
if (!sd->aggNew)
|
|
sd->aggNew = isNewDeclaration();
|
|
|
|
if (isDelete())
|
|
{
|
|
if (sd->aggDelete)
|
|
error("multiple delete's for struct %s", sd->toChars());
|
|
sd->aggDelete = (DeleteDeclaration *)(this);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
id = parent->isInterfaceDeclaration();
|
|
if (id)
|
|
{
|
|
storage_class |= STCabstract;
|
|
|
|
if (isCtorDeclaration() ||
|
|
#if DMDV2
|
|
isPostBlitDeclaration() ||
|
|
#endif
|
|
isDtorDeclaration() ||
|
|
isInvariantDeclaration() ||
|
|
isNewDeclaration() || isDelete())
|
|
error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface %s", id->toChars());
|
|
if (fbody && isVirtual())
|
|
error("function body only allowed in final functions in interface %s", id->toChars());
|
|
}
|
|
|
|
/* Contracts can only appear without a body when they are virtual interface functions
|
|
*/
|
|
if (!fbody && (fensure || frequire) && !(id && isVirtual()))
|
|
error("in and out contracts require function body");
|
|
|
|
cd = parent->isClassDeclaration();
|
|
if (cd)
|
|
{ size_t vi;
|
|
CtorDeclaration *ctor;
|
|
DtorDeclaration *dtor;
|
|
|
|
if (isCtorDeclaration())
|
|
{
|
|
// ctor = (CtorDeclaration *)this;
|
|
// if (!cd->ctor)
|
|
// cd->ctor = ctor;
|
|
goto Ldone;
|
|
}
|
|
|
|
#if 0
|
|
dtor = isDtorDeclaration();
|
|
if (dtor)
|
|
{
|
|
if (cd->dtor)
|
|
error("multiple destructors for class %s", cd->toChars());
|
|
cd->dtor = dtor;
|
|
}
|
|
|
|
if (isInvariantDeclaration())
|
|
{
|
|
cd->invs.push(this);
|
|
}
|
|
|
|
if (isNewDeclaration())
|
|
{
|
|
if (!cd->aggNew)
|
|
cd->aggNew = (NewDeclaration *)(this);
|
|
}
|
|
|
|
if (isDelete())
|
|
{
|
|
if (cd->aggDelete)
|
|
error("multiple delete's for class %s", cd->toChars());
|
|
cd->aggDelete = (DeleteDeclaration *)(this);
|
|
}
|
|
#endif
|
|
|
|
if (storage_class & STCabstract)
|
|
cd->isabstract = 1;
|
|
|
|
// if static function, do not put in vtbl[]
|
|
if (!isVirtual())
|
|
{
|
|
//printf("\tnot virtual\n");
|
|
goto Ldone;
|
|
}
|
|
// Suppress further errors if the return type is an error
|
|
if (type->nextOf() == Type::terror)
|
|
goto Ldone;
|
|
|
|
if (cd->baseClass)
|
|
{
|
|
Dsymbol *cbd = cd->baseClass;
|
|
if (cbd->parent && cbd->parent->isTemplateInstance())
|
|
{
|
|
for (size_t i = 0; i < cd->baseClass->vtbl.dim; i++)
|
|
{
|
|
FuncDeclaration *f = cd->baseClass->vtbl[i]->isFuncDeclaration();
|
|
if (f && f->ident == ident && !f->functionSemantic())
|
|
goto Ldone;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find index of existing function in base class's vtbl[] to override
|
|
* (the index will be the same as in cd's current vtbl[])
|
|
*/
|
|
vi = cd->baseClass ? findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, cd->baseClass->vtbl.dim)
|
|
: -1;
|
|
|
|
doesoverride = FALSE;
|
|
switch (vi)
|
|
{
|
|
case -1:
|
|
Lintro:
|
|
/* Didn't find one, so
|
|
* This is an 'introducing' function which gets a new
|
|
* slot in the vtbl[].
|
|
*/
|
|
|
|
// Verify this doesn't override previous final function
|
|
if (cd->baseClass)
|
|
{ Dsymbol *s = cd->baseClass->search(loc, ident, 0);
|
|
if (s)
|
|
{
|
|
FuncDeclaration *f = s->isFuncDeclaration();
|
|
if (f)
|
|
{
|
|
f = f->overloadExactMatch(type);
|
|
if (f && f->isFinal() && f->prot() != PROTprivate)
|
|
error("cannot override final function %s", f->toPrettyChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isFinal())
|
|
{
|
|
// Don't check here, as it may override an interface function
|
|
//if (isOverride())
|
|
//error("is marked as override, but does not override any function");
|
|
cd->vtblFinal.push(this);
|
|
}
|
|
else
|
|
{
|
|
// Append to end of vtbl[]
|
|
//printf("\tintroducing function\n");
|
|
introducing = 1;
|
|
vi = cd->vtbl.dim;
|
|
cd->vtbl.push(this);
|
|
vtblIndex = vi;
|
|
}
|
|
break;
|
|
|
|
case -2: // can't determine because of fwd refs
|
|
cd->sizeok = SIZEOKfwd; // can't finish due to forward reference
|
|
Module::dprogress = dprogress_save;
|
|
return;
|
|
|
|
default:
|
|
{ FuncDeclaration *fdv = cd->baseClass->vtbl[vi]->isFuncDeclaration();
|
|
FuncDeclaration *fdc = cd->vtbl[vi]->isFuncDeclaration();
|
|
// This function is covariant with fdv
|
|
|
|
if (fdc->toParent() == parent)
|
|
{
|
|
//printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n",
|
|
// vi, this, this->toChars(), this->type->toChars(), this->loc.toChars(),
|
|
// fdc, fdc ->toChars(), fdc ->type->toChars(), fdc ->loc.toChars(),
|
|
// fdv, fdv ->toChars(), fdv ->type->toChars(), fdv ->loc.toChars());
|
|
|
|
// fdc overrides fdv exactly, then this introduces new function.
|
|
if (fdc->type->mod == fdv->type->mod && this->type->mod != fdv->type->mod)
|
|
goto Lintro;
|
|
}
|
|
|
|
// This function overrides fdv
|
|
if (fdv->isFinal())
|
|
error("cannot override final function %s", fdv->toPrettyChars());
|
|
|
|
doesoverride = TRUE;
|
|
#if DMDV2
|
|
if (!isOverride())
|
|
::deprecation(loc, "overriding base class function without using override attribute is deprecated (%s overrides %s)", toPrettyChars(), fdv->toPrettyChars());
|
|
#endif
|
|
|
|
if (fdc->toParent() == parent)
|
|
{
|
|
// If both are mixins, or both are not, then error.
|
|
// If either is not, the one that is not overrides the other.
|
|
bool thismixin = this->parent->isClassDeclaration() != NULL;
|
|
bool fdcmixin = fdc->parent->isClassDeclaration() != NULL;
|
|
if (thismixin == fdcmixin)
|
|
{
|
|
error("multiple overrides of same function");
|
|
}
|
|
else if (!thismixin) // fdc overrides fdv
|
|
{ // this doesn't override any function
|
|
break;
|
|
}
|
|
}
|
|
cd->vtbl[vi] = this;
|
|
vtblIndex = vi;
|
|
|
|
/* Remember which functions this overrides
|
|
*/
|
|
foverrides.push(fdv);
|
|
|
|
/* This works by whenever this function is called,
|
|
* it actually returns tintro, which gets dynamically
|
|
* cast to type. But we know that tintro is a base
|
|
* of type, so we could optimize it by not doing a
|
|
* dynamic cast, but just subtracting the isBaseOf()
|
|
* offset if the value is != null.
|
|
*/
|
|
|
|
if (fdv->tintro)
|
|
tintro = fdv->tintro;
|
|
else if (!type->equals(fdv->type))
|
|
{
|
|
/* Only need to have a tintro if the vptr
|
|
* offsets differ
|
|
*/
|
|
int offset;
|
|
if (fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset))
|
|
{
|
|
tintro = fdv->type;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Go through all the interface bases.
|
|
* If this function is covariant with any members of those interface
|
|
* functions, set the tintro.
|
|
*/
|
|
for (size_t i = 0; i < cd->interfaces_dim; i++)
|
|
{
|
|
BaseClass *b = cd->interfaces[i];
|
|
vi = findVtblIndex((Dsymbols *)&b->base->vtbl, b->base->vtbl.dim);
|
|
switch (vi)
|
|
{
|
|
case -1:
|
|
break;
|
|
|
|
case -2:
|
|
cd->sizeok = SIZEOKfwd; // can't finish due to forward reference
|
|
Module::dprogress = dprogress_save;
|
|
return;
|
|
|
|
default:
|
|
{ FuncDeclaration *fdv = (FuncDeclaration *)b->base->vtbl[vi];
|
|
Type *ti = NULL;
|
|
|
|
/* Remember which functions this overrides
|
|
*/
|
|
foverrides.push(fdv);
|
|
|
|
#if DMDV2
|
|
/* Should we really require 'override' when implementing
|
|
* an interface function?
|
|
*/
|
|
//if (!isOverride())
|
|
//warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars());
|
|
#endif
|
|
|
|
if (fdv->tintro)
|
|
ti = fdv->tintro;
|
|
else if (!type->equals(fdv->type))
|
|
{
|
|
/* Only need to have a tintro if the vptr
|
|
* offsets differ
|
|
*/
|
|
unsigned errors = global.errors;
|
|
global.gag++; // suppress printing of error messages
|
|
int offset;
|
|
int baseOf = fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset);
|
|
global.gag--; // suppress printing of error messages
|
|
if (errors != global.errors)
|
|
{
|
|
// any error in isBaseOf() is a forward reference error, so we bail out
|
|
global.errors = errors;
|
|
cd->sizeok = SIZEOKfwd; // can't finish due to forward reference
|
|
Module::dprogress = dprogress_save;
|
|
return;
|
|
}
|
|
if (baseOf)
|
|
{
|
|
ti = fdv->type;
|
|
}
|
|
}
|
|
if (ti)
|
|
{
|
|
if (tintro)
|
|
{
|
|
if (!tintro->nextOf()->equals(ti->nextOf()) &&
|
|
!tintro->nextOf()->isBaseOf(ti->nextOf(), NULL) &&
|
|
!ti->nextOf()->isBaseOf(tintro->nextOf(), NULL))
|
|
{
|
|
error("incompatible covariant types %s and %s", tintro->toChars(), ti->toChars());
|
|
}
|
|
}
|
|
tintro = ti;
|
|
}
|
|
goto L2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!doesoverride && isOverride())
|
|
{
|
|
Dsymbol *s = NULL;
|
|
for (size_t i = 0; i < cd->baseclasses->dim; i++)
|
|
{
|
|
s = (*cd->baseclasses)[i]->base->search_correct(ident);
|
|
if (s) break;
|
|
}
|
|
|
|
if (s)
|
|
error("does not override any function, did you mean to override '%s'?", s->toPrettyChars());
|
|
else
|
|
error("does not override any function");
|
|
}
|
|
|
|
L2: ;
|
|
|
|
/* Go through all the interface bases.
|
|
* Disallow overriding any final functions in the interface(s).
|
|
*/
|
|
for (size_t i = 0; i < cd->interfaces_dim; i++)
|
|
{
|
|
BaseClass *b = cd->interfaces[i];
|
|
if (b->base)
|
|
{
|
|
Dsymbol *s = search_function(b->base, ident);
|
|
if (s)
|
|
{
|
|
FuncDeclaration *f = s->isFuncDeclaration();
|
|
if (f)
|
|
{
|
|
f = f->overloadExactMatch(type);
|
|
if (f && f->isFinal() && f->prot() != PROTprivate)
|
|
error("cannot override final function %s.%s", b->base->toChars(), f->toPrettyChars());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (isOverride() && !parent->isTemplateInstance())
|
|
error("override only applies to class member functions");
|
|
|
|
/* Do not allow template instances to add virtual functions
|
|
* to a class.
|
|
*/
|
|
if (isVirtual())
|
|
{
|
|
TemplateInstance *ti = parent->isTemplateInstance();
|
|
if (ti)
|
|
{
|
|
// Take care of nested templates
|
|
while (1)
|
|
{
|
|
TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance();
|
|
if (!ti2)
|
|
break;
|
|
ti = ti2;
|
|
}
|
|
|
|
// If it's a member template
|
|
ClassDeclaration *cd = ti->tempdecl->isClassMember();
|
|
if (cd)
|
|
{
|
|
error("cannot use template to add virtual function to class '%s'", cd->toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isMain())
|
|
{
|
|
// Check parameters to see if they are either () or (char[][] args)
|
|
switch (nparams)
|
|
{
|
|
case 0:
|
|
break;
|
|
|
|
case 1:
|
|
{
|
|
Parameter *arg0 = Parameter::getNth(f->parameters, 0);
|
|
if (arg0->type->ty != Tarray ||
|
|
arg0->type->nextOf()->ty != Tarray ||
|
|
arg0->type->nextOf()->nextOf()->ty != Tchar ||
|
|
arg0->storageClass & (STCout | STCref | STClazy))
|
|
goto Lmainerr;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
goto Lmainerr;
|
|
}
|
|
|
|
if (!f->nextOf())
|
|
error("must return int or void");
|
|
else if (f->nextOf()->ty != Tint32 && f->nextOf()->ty != Tvoid)
|
|
error("must return int or void, not %s", f->nextOf()->toChars());
|
|
if (f->varargs)
|
|
{
|
|
Lmainerr:
|
|
error("parameters must be main() or main(string[] args)");
|
|
}
|
|
}
|
|
|
|
if (isVirtual() && semanticRun != PASSsemanticdone)
|
|
{
|
|
/* Rewrite contracts as nested functions, then call them.
|
|
* Doing it as nested functions means that overriding functions
|
|
* can call them.
|
|
*/
|
|
if (frequire)
|
|
{
|
|
#if IN_LLVM
|
|
/* In LDC, we can't rely on the codegen hacks DMD has to be able
|
|
* to just magically call the contract function parameterless with
|
|
* the parameters being picked up from the outer stack frame.
|
|
*
|
|
* Thus, we actually pass all the function parameters to the
|
|
* __require call, rewriting out parameters to ref ones because
|
|
* they have already been zeroed in the outer function.
|
|
*
|
|
* Also initialize fdrequireParams here - it will get filled in
|
|
* in semantic3.
|
|
*/
|
|
fdrequireParams = new Expressions();
|
|
Parameters *params = outToRef(((TypeFunction*)type)->parameters);
|
|
Type *tf = new TypeFunction(params, Type::tvoid, 0, LINKd);
|
|
#else
|
|
/* in { ... }
|
|
* becomes:
|
|
* void __require() { ... }
|
|
* __require();
|
|
*/
|
|
TypeFunction *tf = new TypeFunction(NULL, Type::tvoid, 0, LINKd);
|
|
#endif
|
|
Loc loc = frequire->loc;
|
|
FuncDeclaration *fd = new FuncDeclaration(loc, loc,
|
|
Id::require, STCundefined, tf);
|
|
fd->fbody = frequire;
|
|
Statement *s1 = new ExpStatement(loc, fd);
|
|
#if IN_LLVM
|
|
Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), fdrequireParams);
|
|
#else
|
|
Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), (Expressions *)NULL);
|
|
#endif
|
|
Statement *s2 = new ExpStatement(loc, e);
|
|
frequire = new CompoundStatement(loc, s1, s2);
|
|
fdrequire = fd;
|
|
}
|
|
|
|
if (!outId && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid)
|
|
outId = Id::result; // provide a default
|
|
|
|
#if IN_LLVM
|
|
/* We need to initialize fdensureParams here and not in the block below
|
|
* to have the parameter available when calling a base class ensure(),
|
|
* even if this functions doesn't have an out contract.
|
|
*/
|
|
fdensureParams = new Expressions();
|
|
if (outId)
|
|
fdensureParams->push(new IdentifierExp(loc, outId));
|
|
#endif
|
|
|
|
if (fensure)
|
|
{
|
|
#if IN_LLVM
|
|
/* Same as for in contracts, see above. */
|
|
Parameters *arguments = outToRef(((TypeFunction*)type)->parameters);
|
|
#else
|
|
/* out (result) { ... }
|
|
* becomes:
|
|
* tret __ensure(ref tret result) { ... }
|
|
* __ensure(result);
|
|
*/
|
|
Parameters *arguments = new Parameters();
|
|
#endif
|
|
Loc loc = fensure->loc;
|
|
Parameter *a = NULL;
|
|
if (outId)
|
|
{ a = new Parameter(STCref | STCconst, f->nextOf(), outId, NULL);
|
|
arguments->insert(0, a);
|
|
}
|
|
TypeFunction *tf = new TypeFunction(arguments, Type::tvoid, 0, LINKd);
|
|
FuncDeclaration *fd = new FuncDeclaration(loc, loc,
|
|
Id::ensure, STCundefined, tf);
|
|
fd->fbody = fensure;
|
|
Statement *s1 = new ExpStatement(loc, fd);
|
|
#if IN_LLVM
|
|
Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), fdensureParams);
|
|
#else
|
|
Expression *eresult = NULL;
|
|
if (outId)
|
|
eresult = new IdentifierExp(loc, outId);
|
|
Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), eresult);
|
|
#endif
|
|
Statement *s2 = new ExpStatement(loc, e);
|
|
fensure = new CompoundStatement(loc, s1, s2);
|
|
fdensure = fd;
|
|
}
|
|
}
|
|
|
|
Ldone:
|
|
/* Purity and safety can be inferred for some functions by examining
|
|
* the function body.
|
|
*/
|
|
if (fbody &&
|
|
(isFuncLiteralDeclaration() ||
|
|
parent->isTemplateInstance() ||
|
|
ad && ad->parent && ad->parent->isTemplateInstance() && !isVirtualMethod()))
|
|
{
|
|
/* isVirtualMethod() needs setting correct foverrides
|
|
*/
|
|
if (f->purity == PUREimpure) // purity not specified
|
|
flags |= FUNCFLAGpurityInprocess;
|
|
|
|
if (f->trust == TRUSTdefault)
|
|
flags |= FUNCFLAGsafetyInprocess;
|
|
|
|
if (!f->isnothrow)
|
|
flags |= FUNCFLAGnothrowInprocess;
|
|
}
|
|
|
|
Module::dprogress++;
|
|
//LDC relies on semanticRun variable not being reset here
|
|
if(semanticRun < PASSsemanticdone)
|
|
semanticRun = PASSsemanticdone;
|
|
|
|
/* Save scope for possible later use (if we need the
|
|
* function internals)
|
|
*/
|
|
scope = new Scope(*sc);
|
|
scope->setNoFree();
|
|
|
|
static bool printedMain = false; // semantic might run more than once
|
|
if (global.params.verbose && !printedMain)
|
|
{
|
|
const char *type = isMain() ? "main" : isWinMain() ? "winmain" : isDllMain() ? "dllmain" : (const char *)NULL;
|
|
Module *mod = sc->module;
|
|
|
|
if (type && mod)
|
|
{
|
|
printedMain = true;
|
|
const char *name = FileName::searchPath(global.path, mod->srcfile->toChars(), 1);
|
|
printf("%-10s%-10s\t%s\n", "entry", type, name);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void FuncDeclaration::semantic2(Scope *sc)
|
|
{
|
|
}
|
|
|
|
// Do the semantic analysis on the internals of the function.
|
|
|
|
void FuncDeclaration::semantic3(Scope *sc)
|
|
{ TypeFunction *f;
|
|
VarDeclaration *argptr = NULL;
|
|
VarDeclaration *_arguments = NULL;
|
|
int nerrors = global.errors;
|
|
|
|
if (!parent)
|
|
{
|
|
if (global.errors)
|
|
return;
|
|
//printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc);
|
|
assert(0);
|
|
}
|
|
//printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", parent->toChars(), toChars(), this, sc, loc.toChars());
|
|
//fflush(stdout);
|
|
//printf("storage class = x%x %x\n", sc->stc, storage_class);
|
|
//{ static int x; if (++x == 2) *(char*)0=0; }
|
|
//printf("\tlinkage = %d\n", sc->linkage);
|
|
|
|
//printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract));
|
|
if (semanticRun >= PASSsemantic3)
|
|
return;
|
|
semanticRun = PASSsemantic3;
|
|
semantic3Errors = 0;
|
|
|
|
#if IN_LLVM
|
|
if (!global.inExtraInliningSemantic)
|
|
availableExternally = false;
|
|
#endif
|
|
|
|
if (!type || type->ty != Tfunction)
|
|
return;
|
|
f = (TypeFunction *)type;
|
|
if (!inferRetType && f->next->ty == Terror)
|
|
return;
|
|
|
|
#if 0
|
|
// Check the 'throws' clause
|
|
if (fthrows)
|
|
{
|
|
for (size_t i = 0; i < fthrows->dim; i++)
|
|
{
|
|
Type *t = (*fthrows)[i];
|
|
|
|
t = t->semantic(loc, sc);
|
|
if (!t->isClassHandle())
|
|
error("can only throw classes, not %s", t->toChars());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!fbody && inferRetType && !type->nextOf())
|
|
{
|
|
error("has no function body with return type inference");
|
|
return;
|
|
}
|
|
|
|
if (frequire)
|
|
{
|
|
for (size_t i = 0; i < foverrides.dim; i++)
|
|
{
|
|
FuncDeclaration *fdv = foverrides[i];
|
|
|
|
if (fdv->fbody && !fdv->frequire)
|
|
{
|
|
error("cannot have an in contract when overriden function %s does not have an in contract", fdv->toPrettyChars());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
frequire = mergeFrequire(frequire);
|
|
fensure = mergeFensure(fensure);
|
|
|
|
if (fbody || frequire || fensure)
|
|
{
|
|
/* Symbol table into which we place parameters and nested functions,
|
|
* solely to diagnose name collisions.
|
|
*/
|
|
localsymtab = new DsymbolTable();
|
|
|
|
// Establish function scope
|
|
ScopeDsymbol *ss = new ScopeDsymbol();
|
|
ss->parent = sc->scopesym;
|
|
Scope *sc2 = sc->push(ss);
|
|
sc2->func = this;
|
|
sc2->parent = this;
|
|
sc2->callSuper = 0;
|
|
sc2->sbreak = NULL;
|
|
sc2->scontinue = NULL;
|
|
sc2->sw = NULL;
|
|
sc2->fes = fes;
|
|
sc2->linkage = LINKd;
|
|
sc2->stc &= ~(STCauto | STCscope | STCstatic | STCabstract |
|
|
STCdeprecated | STCoverride |
|
|
STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref |
|
|
STCproperty | STCsafe | STCtrusted | STCsystem);
|
|
sc2->protection = PROTpublic;
|
|
sc2->explicitProtection = 0;
|
|
sc2->structalign = STRUCTALIGN_DEFAULT;
|
|
if (this->ident != Id::require && this->ident != Id::ensure)
|
|
sc2->flags = sc->flags & ~SCOPEcontract;
|
|
#if IN_LLVM
|
|
sc2->enclosingFinally = NULL;
|
|
sc2->enclosingScopeExit = NULL;
|
|
#else
|
|
sc2->tf = NULL;
|
|
#endif
|
|
sc2->noctor = 0;
|
|
sc2->speculative = sc->speculative || isSpeculative() != NULL;
|
|
sc2->userAttributes = NULL;
|
|
if (sc2->intypeof == 1) sc2->intypeof = 2;
|
|
|
|
// Declare 'this'
|
|
AggregateDeclaration *ad = isThis();
|
|
if (ad)
|
|
{
|
|
if (isFuncLiteralDeclaration() && isNested() && !sc->intypeof)
|
|
{
|
|
error("function literals cannot be class members");
|
|
return;
|
|
}
|
|
else
|
|
assert(!isNested() || sc->intypeof); // can't be both member and nested
|
|
}
|
|
vthis = declareThis(sc2, ad);
|
|
|
|
// Declare hidden variable _arguments[] and _argptr
|
|
if (f->varargs == 1)
|
|
{
|
|
Type *t;
|
|
|
|
#if !IN_GCC && !IN_LLVM
|
|
if (global.params.is64bit && !global.params.isWindows)
|
|
{ // Declare save area for varargs registers
|
|
Type *t = new TypeIdentifier(loc, Id::va_argsave_t);
|
|
t = t->semantic(loc, sc);
|
|
if (t == Type::terror)
|
|
{
|
|
error("must import core.vararg to use variadic functions");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
v_argsave = new VarDeclaration(loc, t, Id::va_argsave, NULL);
|
|
v_argsave->semantic(sc2);
|
|
sc2->insert(v_argsave);
|
|
v_argsave->parent = this;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (f->linkage == LINKd)
|
|
{ // Declare _arguments[]
|
|
v_arguments = new VarDeclaration(Loc(), Type::typeinfotypelist->type, Id::_arguments_typeinfo, NULL);
|
|
v_arguments->storage_class = STCparameter;
|
|
v_arguments->semantic(sc2);
|
|
sc2->insert(v_arguments);
|
|
v_arguments->parent = this;
|
|
|
|
//t = Type::typeinfo->type->constOf()->arrayOf();
|
|
t = Type::typeinfo->type->arrayOf();
|
|
_arguments = new VarDeclaration(Loc(), t, Id::_arguments, NULL);
|
|
_arguments->semantic(sc2);
|
|
sc2->insert(_arguments);
|
|
_arguments->parent = this;
|
|
}
|
|
if (f->linkage == LINKd || (f->parameters && Parameter::dim(f->parameters)))
|
|
{ // Declare _argptr
|
|
t = Type::tvalist;
|
|
argptr = new VarDeclaration(Loc(), t, Id::_argptr, NULL);
|
|
argptr->semantic(sc2);
|
|
sc2->insert(argptr);
|
|
argptr->parent = this;
|
|
}
|
|
}
|
|
|
|
#if IN_LLVM
|
|
// Make sure semantic analysis has been run on argument types. This is
|
|
// e.g. needed for TypeTuple!(int, int) to be picked up as two int
|
|
// parameters by the Parameter functions.
|
|
if (f->parameters)
|
|
{
|
|
for (size_t i = 0; i < Parameter::dim(f->parameters); i++)
|
|
{ Parameter *arg = (Parameter *)Parameter::getNth(f->parameters, i);
|
|
Type* nw = arg->type->semantic(Loc(), sc);
|
|
if (arg->type != nw) {
|
|
arg->type = nw;
|
|
// Examine this index again.
|
|
// This is important if it turned into a tuple.
|
|
// In particular, the empty tuple should be handled or the
|
|
// next parameter will be skipped.
|
|
// LDC_FIXME: Maybe we only need to do this for tuples,
|
|
// and can add tuple.length after decrement?
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
// Propagate storage class from tuple parameters to their element-parameters.
|
|
if (f->parameters)
|
|
{
|
|
for (size_t i = 0; i < f->parameters->dim; i++)
|
|
{ Parameter *arg = (*f->parameters)[i];
|
|
|
|
//printf("[%d] arg->type->ty = %d %s\n", i, arg->type->ty, arg->type->toChars());
|
|
if (arg->type->ty == Ttuple)
|
|
{ TypeTuple *t = (TypeTuple *)arg->type;
|
|
size_t dim = Parameter::dim(t->arguments);
|
|
for (size_t j = 0; j < dim; j++)
|
|
{ Parameter *narg = Parameter::getNth(t->arguments, j);
|
|
narg->storageClass = arg->storageClass;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Declare all the function parameters as variables
|
|
* and install them in parameters[]
|
|
*/
|
|
size_t nparams = Parameter::dim(f->parameters);
|
|
if (nparams)
|
|
{ /* parameters[] has all the tuples removed, as the back end
|
|
* doesn't know about tuples
|
|
*/
|
|
parameters = new VarDeclarations();
|
|
parameters->reserve(nparams);
|
|
for (size_t i = 0; i < nparams; i++)
|
|
{
|
|
Parameter *arg = Parameter::getNth(f->parameters, i);
|
|
Identifier *id = arg->ident;
|
|
if (!id)
|
|
{
|
|
/* Generate identifier for un-named parameter,
|
|
* because we need it later on.
|
|
*/
|
|
arg->ident = id = Identifier::generateId("_param_", i);
|
|
}
|
|
Type *vtype = arg->type;
|
|
VarDeclaration *v = new VarDeclaration(loc, vtype, id, NULL);
|
|
//printf("declaring parameter %s of type %s\n", v->toChars(), v->type->toChars());
|
|
v->storage_class |= STCparameter;
|
|
if (f->varargs == 2 && i + 1 == nparams)
|
|
v->storage_class |= STCvariadic;
|
|
v->storage_class |= arg->storageClass & (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor);
|
|
v->semantic(sc2);
|
|
if (!sc2->insert(v))
|
|
error("parameter %s.%s is already defined", toChars(), v->toChars());
|
|
else
|
|
parameters->push(v);
|
|
localsymtab->insert(v);
|
|
v->parent = this;
|
|
#if IN_LLVM
|
|
if (fdrequireParams)
|
|
fdrequireParams->push(new VarExp(loc, v));
|
|
if (fdensureParams)
|
|
fdensureParams->push(new VarExp(loc, v));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Declare the tuple symbols and put them in the symbol table,
|
|
// but not in parameters[].
|
|
if (f->parameters)
|
|
{
|
|
for (size_t i = 0; i < f->parameters->dim; i++)
|
|
{ Parameter *arg = (*f->parameters)[i];
|
|
|
|
if (!arg->ident)
|
|
continue; // never used, so ignore
|
|
if (arg->type->ty == Ttuple)
|
|
{ TypeTuple *t = (TypeTuple *)arg->type;
|
|
size_t dim = Parameter::dim(t->arguments);
|
|
Objects *exps = new Objects();
|
|
exps->setDim(dim);
|
|
for (size_t j = 0; j < dim; j++)
|
|
{ Parameter *narg = Parameter::getNth(t->arguments, j);
|
|
assert(narg->ident);
|
|
VarDeclaration *v = sc2->search(Loc(), narg->ident, NULL)->isVarDeclaration();
|
|
assert(v);
|
|
Expression *e = new VarExp(v->loc, v);
|
|
(*exps)[j] = e;
|
|
}
|
|
assert(arg->ident);
|
|
TupleDeclaration *v = new TupleDeclaration(loc, arg->ident, exps);
|
|
//printf("declaring tuple %s\n", v->toChars());
|
|
v->isexp = 1;
|
|
if (!sc2->insert(v))
|
|
error("parameter %s.%s is already defined", toChars(), v->toChars());
|
|
localsymtab->insert(v);
|
|
v->parent = this;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Precondition invariant
|
|
Statement *fpreinv = NULL;
|
|
if (addPreInvariant())
|
|
{
|
|
Expression *e = NULL;
|
|
if (isDtorDeclaration())
|
|
{
|
|
// Call invariant directly only if it exists
|
|
FuncDeclaration *inv = ad->inv;
|
|
ClassDeclaration *cd = ad->isClassDeclaration();
|
|
|
|
while (!inv && cd)
|
|
{
|
|
cd = cd->baseClass;
|
|
if (!cd)
|
|
break;
|
|
inv = cd->inv;
|
|
}
|
|
if (inv)
|
|
{
|
|
e = new DsymbolExp(Loc(), inv);
|
|
e = new CallExp(Loc(), e);
|
|
e = e->semantic(sc2);
|
|
}
|
|
}
|
|
else
|
|
{ // Call invariant virtually
|
|
#if IN_LLVM
|
|
// We actually need a valid 'var' for codegen.
|
|
ThisExp* tv = new ThisExp(Loc());
|
|
tv->var = vthis;
|
|
Expression *v = tv;
|
|
#else
|
|
Expression *v = new ThisExp(Loc());
|
|
#endif
|
|
v->type = vthis->type;
|
|
if (ad->isStructDeclaration())
|
|
v = v->addressOf(sc);
|
|
Expression *se = new StringExp(Loc(), (char *)"null this");
|
|
se = se->semantic(sc);
|
|
se->type = Type::tchar->arrayOf();
|
|
e = new AssertExp(loc, v, se);
|
|
}
|
|
if (e)
|
|
fpreinv = new ExpStatement(Loc(), e);
|
|
}
|
|
|
|
// Postcondition invariant
|
|
Statement *fpostinv = NULL;
|
|
if (addPostInvariant())
|
|
{
|
|
Expression *e = NULL;
|
|
if (isCtorDeclaration())
|
|
{
|
|
// Call invariant directly only if it exists
|
|
FuncDeclaration *inv = ad->inv;
|
|
ClassDeclaration *cd = ad->isClassDeclaration();
|
|
|
|
while (!inv && cd)
|
|
{
|
|
cd = cd->baseClass;
|
|
if (!cd)
|
|
break;
|
|
inv = cd->inv;
|
|
}
|
|
if (inv)
|
|
{
|
|
e = new DsymbolExp(Loc(), inv);
|
|
e = new CallExp(Loc(), e);
|
|
e = e->semantic(sc2);
|
|
}
|
|
}
|
|
else
|
|
{ // Call invariant virtually
|
|
#if IN_LLVM
|
|
// We actually need a valid 'var' for codegen.
|
|
ThisExp* tv = new ThisExp(Loc());
|
|
tv->var = vthis;
|
|
Expression *v = tv;
|
|
#else
|
|
Expression *v = new ThisExp(Loc());
|
|
#endif
|
|
v->type = vthis->type;
|
|
if (ad->isStructDeclaration())
|
|
v = v->addressOf(sc);
|
|
e = new AssertExp(Loc(), v);
|
|
}
|
|
if (e)
|
|
fpostinv = new ExpStatement(Loc(), e);
|
|
}
|
|
|
|
if (fensure || addPostInvariant())
|
|
{
|
|
if ((fensure && global.params.useOut) || fpostinv)
|
|
{ returnLabel = new LabelDsymbol(Id::returnLabel);
|
|
}
|
|
|
|
// scope of out contract (need for vresult->semantic)
|
|
ScopeDsymbol *sym = new ScopeDsymbol();
|
|
sym->parent = sc2->scopesym;
|
|
scout = sc2->push(sym);
|
|
}
|
|
|
|
if (fbody)
|
|
{
|
|
ScopeDsymbol *sym = new ScopeDsymbol();
|
|
sym->parent = sc2->scopesym;
|
|
sc2 = sc2->push(sym);
|
|
|
|
AggregateDeclaration *ad = isAggregateMember2();
|
|
|
|
/* If this is a class constructor
|
|
*/
|
|
if (ad && isCtorDeclaration())
|
|
{
|
|
for (size_t i = 0; i < ad->fields.dim; i++)
|
|
{ VarDeclaration *v = ad->fields[i];
|
|
|
|
v->ctorinit = 0;
|
|
}
|
|
}
|
|
|
|
if (!inferRetType && f->retStyle() != RETstack)
|
|
nrvo_can = 0;
|
|
|
|
fbody = fbody->semantic(sc2);
|
|
if (!fbody)
|
|
fbody = new CompoundStatement(Loc(), new Statements());
|
|
|
|
if (inferRetType)
|
|
{ // If no return type inferred yet, then infer a void
|
|
if (!type->nextOf())
|
|
{
|
|
f->next = Type::tvoid;
|
|
//type = type->semantic(loc, sc); // Removed with 6902
|
|
}
|
|
else if (returns && f->next->ty != Tvoid)
|
|
{
|
|
for (size_t i = 0; i < returns->dim; i++)
|
|
{ Expression *exp = (*returns)[i]->exp;
|
|
if (!f->next->invariantOf()->equals(exp->type->invariantOf()))
|
|
{ exp = exp->castTo(sc2, f->next);
|
|
exp = exp->optimize(WANTvalue);
|
|
(*returns)[i]->exp = exp;
|
|
}
|
|
//printf("[%d] %s %s\n", i, exp->type->toChars(), exp->toChars());
|
|
}
|
|
}
|
|
assert(type == f);
|
|
}
|
|
|
|
if (isStaticCtorDeclaration())
|
|
{ /* It's a static constructor. Ensure that all
|
|
* ctor consts were initialized.
|
|
*/
|
|
|
|
Dsymbol *p = toParent();
|
|
ScopeDsymbol *pd = p->isScopeDsymbol();
|
|
if (!pd)
|
|
{
|
|
error("static constructor can only be member of struct/class/module, not %s %s", p->kind(), p->toChars());
|
|
}
|
|
else
|
|
{
|
|
for (size_t i = 0; i < pd->members->dim; i++)
|
|
{ Dsymbol *s = (*pd->members)[i];
|
|
|
|
s->checkCtorConstInit();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isCtorDeclaration() && ad)
|
|
{
|
|
#if DMDV2
|
|
// Check for errors related to 'nothrow'.
|
|
int nothrowErrors = global.errors;
|
|
int blockexit = fbody->blockExit(f->isnothrow);
|
|
if (f->isnothrow && (global.errors != nothrowErrors) )
|
|
error("'%s' is nothrow yet may throw", toChars());
|
|
if (flags & FUNCFLAGnothrowInprocess)
|
|
f->isnothrow = !(blockexit & BEthrow);
|
|
#endif
|
|
//printf("callSuper = x%x\n", sc2->callSuper);
|
|
|
|
ClassDeclaration *cd = ad->isClassDeclaration();
|
|
|
|
// Verify that all the ctorinit fields got initialized
|
|
if (!(sc2->callSuper & CSXthis_ctor))
|
|
{
|
|
for (size_t i = 0; i < ad->fields.dim; i++)
|
|
{ VarDeclaration *v = ad->fields[i];
|
|
|
|
if (v->ctorinit == 0)
|
|
{
|
|
/* Current bugs in the flow analysis:
|
|
* 1. union members should not produce error messages even if
|
|
* not assigned to
|
|
* 2. structs should recognize delegating opAssign calls as well
|
|
* as delegating calls to other constructors
|
|
*/
|
|
if (v->isCtorinit() && !v->type->isMutable() && cd)
|
|
error("missing initializer for %s field %s", MODtoChars(v->type->mod), v->toChars());
|
|
else if (v->storage_class & STCnodefaultctor)
|
|
error("field %s must be initialized in constructor", v->toChars());
|
|
else if (v->type->needsNested())
|
|
error("field %s must be initialized in constructor, because it is nested struct", v->toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cd &&
|
|
!(sc2->callSuper & CSXany_ctor) &&
|
|
cd->baseClass && cd->baseClass->ctor)
|
|
{
|
|
sc2->callSuper = 0;
|
|
|
|
// Insert implicit super() at start of fbody
|
|
if (!resolveFuncCall(Loc(), sc2, cd->baseClass->ctor, NULL, NULL, NULL, 1))
|
|
{
|
|
error("no match for implicit super() call in constructor");
|
|
}
|
|
else
|
|
{
|
|
Expression *e1 = new SuperExp(Loc());
|
|
Expression *e = new CallExp(Loc(), e1);
|
|
e = e->semantic(sc2);
|
|
|
|
Statement *s = new ExpStatement(Loc(), e);
|
|
fbody = new CompoundStatement(Loc(), s, fbody);
|
|
}
|
|
}
|
|
|
|
/* Append:
|
|
* return this;
|
|
* to function body
|
|
*/
|
|
if (blockexit & BEfallthru)
|
|
{
|
|
Expression *e = new ThisExp(loc);
|
|
if (cd)
|
|
e->type = cd->type;
|
|
Statement *s = new ReturnStatement(loc, e);
|
|
s = s->semantic(sc2);
|
|
fbody = new CompoundStatement(loc, fbody, s);
|
|
}
|
|
}
|
|
else if (fes)
|
|
{ // For foreach(){} body, append a return 0;
|
|
Expression *e = new IntegerExp(0);
|
|
Statement *s = new ReturnStatement(Loc(), e);
|
|
fbody = new CompoundStatement(Loc(), fbody, s);
|
|
assert(!returnLabel);
|
|
}
|
|
else if (!hasReturnExp && type->nextOf()->ty != Tvoid)
|
|
error("has no return statement, but is expected to return a value of type %s", type->nextOf()->toChars());
|
|
else if (hasReturnExp & 8) // if inline asm
|
|
{
|
|
flags &= ~FUNCFLAGnothrowInprocess;
|
|
}
|
|
else
|
|
{
|
|
#if DMDV2
|
|
// Check for errors related to 'nothrow'.
|
|
int nothrowErrors = global.errors;
|
|
int blockexit = fbody->blockExit(f->isnothrow);
|
|
if (f->isnothrow && (global.errors != nothrowErrors) )
|
|
error("'%s' is nothrow yet may throw", toChars());
|
|
if (flags & FUNCFLAGnothrowInprocess)
|
|
{
|
|
if (type == f) f = f->copy();
|
|
f->isnothrow = !(blockexit & BEthrow);
|
|
}
|
|
|
|
int offend = blockexit & BEfallthru;
|
|
#endif
|
|
if (type->nextOf()->ty != Tvoid)
|
|
{
|
|
if (offend)
|
|
{ Expression *e;
|
|
#if DMDV1
|
|
warning(loc, "no return exp; or assert(0); at end of function");
|
|
#else
|
|
error("no return exp; or assert(0); at end of function");
|
|
#endif
|
|
if (global.params.useAssert &&
|
|
!global.params.useInline)
|
|
{ /* Add an assert(0, msg); where the missing return
|
|
* should be.
|
|
*/
|
|
e = new AssertExp(
|
|
endloc,
|
|
new IntegerExp(0),
|
|
new StringExp(loc, (char *)"missing return expression")
|
|
);
|
|
}
|
|
else
|
|
e = new HaltExp(endloc);
|
|
e = new CommaExp(Loc(), e, type->nextOf()->defaultInit());
|
|
e = e->semantic(sc2);
|
|
Statement *s = new ExpStatement(Loc(), e);
|
|
fbody = new CompoundStatement(Loc(), fbody, s);
|
|
}
|
|
}
|
|
}
|
|
|
|
sc2 = sc2->pop();
|
|
}
|
|
|
|
Statement *freq = frequire;
|
|
Statement *fens = fensure;
|
|
|
|
/* Do the semantic analysis on the [in] preconditions and
|
|
* [out] postconditions.
|
|
*/
|
|
if (freq)
|
|
{ /* frequire is composed of the [in] contracts
|
|
*/
|
|
ScopeDsymbol *sym = new ScopeDsymbol();
|
|
sym->parent = sc2->scopesym;
|
|
sc2 = sc2->push(sym);
|
|
sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPErequire;
|
|
|
|
// BUG: need to error if accessing out parameters
|
|
// BUG: need to treat parameters as const
|
|
// BUG: need to disallow returns and throws
|
|
// BUG: verify that all in and ref parameters are read
|
|
freq = freq->semantic(sc2);
|
|
|
|
sc2 = sc2->pop();
|
|
|
|
if (!global.params.useIn)
|
|
freq = NULL;
|
|
}
|
|
|
|
if (fens)
|
|
{ /* fensure is composed of the [out] contracts
|
|
*/
|
|
if (type->nextOf()->ty == Tvoid && outId)
|
|
{
|
|
error("void functions have no result");
|
|
}
|
|
|
|
if (type->nextOf()->ty != Tvoid)
|
|
buildResultVar();
|
|
|
|
sc2 = scout; //push
|
|
sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPEensure;
|
|
|
|
// BUG: need to treat parameters as const
|
|
// BUG: need to disallow returns and throws
|
|
fens = fens->semantic(sc2);
|
|
|
|
sc2 = sc2->pop();
|
|
|
|
if (!global.params.useOut)
|
|
fens = NULL;
|
|
}
|
|
|
|
{
|
|
Statements *a = new Statements();
|
|
|
|
// Merge in initialization of 'out' parameters
|
|
if (parameters)
|
|
{ for (size_t i = 0; i < parameters->dim; i++)
|
|
{
|
|
VarDeclaration *v = (*parameters)[i];
|
|
if (v->storage_class & STCout)
|
|
{
|
|
assert(v->init);
|
|
ExpInitializer *ie = v->init->isExpInitializer();
|
|
assert(ie);
|
|
if (ie->exp->op == TOKconstruct)
|
|
ie->exp->op = TOKassign; // construction occured in parameter processing
|
|
a->push(new ExpStatement(Loc(), ie->exp));
|
|
}
|
|
}
|
|
}
|
|
|
|
// we'll handle variadics ourselves
|
|
#if !IN_LLVM
|
|
if (argptr)
|
|
{ // Initialize _argptr
|
|
#if IN_GCC
|
|
// Handled in FuncDeclaration::toObjFile
|
|
v_argptr = argptr;
|
|
v_argptr->init = new VoidInitializer(loc);
|
|
#else
|
|
Type *t = argptr->type;
|
|
if (global.params.is64bit && !global.params.isWindows)
|
|
{ // Initialize _argptr to point to v_argsave
|
|
Expression *e1 = new VarExp(Loc(), argptr);
|
|
Expression *e = new SymOffExp(Loc(), v_argsave, 6*8 + 8*16);
|
|
e->type = argptr->type;
|
|
e = new AssignExp(Loc(), e1, e);
|
|
e = e->semantic(sc);
|
|
a->push(new ExpStatement(Loc(), e));
|
|
}
|
|
else
|
|
{ // Initialize _argptr to point past non-variadic arg
|
|
VarDeclaration *p;
|
|
unsigned offset = 0;
|
|
Expression *e;
|
|
|
|
Expression *e1 = new VarExp(Loc(), argptr);
|
|
// Find the last non-ref parameter
|
|
if (parameters && parameters->dim)
|
|
{
|
|
size_t lastNonref = parameters->dim -1;
|
|
p = (*parameters)[lastNonref];
|
|
/* The trouble with out and ref parameters is that taking
|
|
* the address of it doesn't work, because later processing
|
|
* adds in an extra level of indirection. So we skip over them.
|
|
*/
|
|
while (p->storage_class & (STCout | STCref))
|
|
{
|
|
offset += Target::ptrsize;
|
|
if (lastNonref-- == 0)
|
|
{
|
|
p = v_arguments;
|
|
break;
|
|
}
|
|
p = (*parameters)[lastNonref];
|
|
}
|
|
}
|
|
else
|
|
p = v_arguments; // last parameter is _arguments[]
|
|
if (global.params.is64bit && global.params.isWindows)
|
|
{ offset += Target::ptrsize;
|
|
if (p->storage_class & STClazy || p->type->size() > Target::ptrsize)
|
|
{
|
|
/* Necessary to offset the extra level of indirection the Win64
|
|
* ABI demands
|
|
*/
|
|
e = new SymOffExp(Loc(),p,0);
|
|
e->type = Type::tvoidptr;
|
|
e = new AddrExp(Loc(), e);
|
|
e->type = Type::tvoidptr;
|
|
e = new AddExp(Loc(), e, new IntegerExp(offset));
|
|
e->type = Type::tvoidptr;
|
|
goto L1;
|
|
}
|
|
}
|
|
else if (p->storage_class & STClazy)
|
|
// If the last parameter is lazy, it's the size of a delegate
|
|
offset += Target::ptrsize * 2;
|
|
else
|
|
offset += p->type->size();
|
|
offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); // assume stack aligns on pointer size
|
|
e = new SymOffExp(Loc(), p, offset);
|
|
e->type = Type::tvoidptr;
|
|
//e = e->semantic(sc);
|
|
L1:
|
|
e = new AssignExp(Loc(), e1, e);
|
|
e->type = t;
|
|
a->push(new ExpStatement(Loc(), e));
|
|
p->isargptr = TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (_arguments)
|
|
{
|
|
#ifdef IN_GCC
|
|
v_arguments_var = _arguments;
|
|
v_arguments_var->init = new VoidInitializer(loc);
|
|
#endif
|
|
/* Advance to elements[] member of TypeInfo_Tuple with:
|
|
* _arguments = v_arguments.elements;
|
|
*/
|
|
Expression *e = new VarExp(Loc(), v_arguments);
|
|
e = new DotIdExp(Loc(), e, Id::elements);
|
|
Expression *e1 = new VarExp(Loc(), _arguments);
|
|
e = new ConstructExp(Loc(), e1, e);
|
|
e = e->semantic(sc2);
|
|
a->push(new ExpStatement(Loc(), e));
|
|
}
|
|
|
|
#endif // !IN_LLVM
|
|
|
|
// Merge contracts together with body into one compound statement
|
|
|
|
if (freq || fpreinv)
|
|
{
|
|
if (!freq)
|
|
freq = fpreinv;
|
|
else if (fpreinv)
|
|
freq = new CompoundStatement(Loc(), freq, fpreinv);
|
|
|
|
a->push(freq);
|
|
}
|
|
|
|
if (fbody)
|
|
a->push(fbody);
|
|
|
|
if (fens || fpostinv)
|
|
{
|
|
if (!fens)
|
|
fens = fpostinv;
|
|
else if (fpostinv)
|
|
fens = new CompoundStatement(Loc(), fpostinv, fens);
|
|
|
|
LabelStatement *ls = new LabelStatement(Loc(), Id::returnLabel, fens);
|
|
returnLabel->statement = ls;
|
|
a->push(returnLabel->statement);
|
|
|
|
if (type->nextOf()->ty != Tvoid && vresult)
|
|
{
|
|
#if IN_LLVM
|
|
Expression *e = 0;
|
|
if (isCtorDeclaration()) {
|
|
ThisExp *te = new ThisExp(Loc());
|
|
te->type = vthis->type;
|
|
te->var = vthis;
|
|
e = te;
|
|
} else {
|
|
e = new VarExp(Loc(), vresult);
|
|
}
|
|
#else
|
|
// Create: return vresult;
|
|
Expression *e = new VarExp(Loc(), vresult);
|
|
#endif
|
|
if (tintro)
|
|
{ e = e->implicitCastTo(sc, tintro->nextOf());
|
|
e = e->semantic(sc);
|
|
}
|
|
ReturnStatement *s = new ReturnStatement(Loc(), e);
|
|
a->push(s);
|
|
}
|
|
}
|
|
if (isMain() && type->nextOf()->ty == Tvoid)
|
|
{ // Add a return 0; statement
|
|
Statement *s = new ReturnStatement(Loc(), new IntegerExp(0));
|
|
a->push(s);
|
|
}
|
|
|
|
fbody = new CompoundStatement(Loc(), a);
|
|
#if DMDV2
|
|
/* Append destructor calls for parameters as finally blocks.
|
|
*/
|
|
if (parameters)
|
|
{ for (size_t i = 0; i < parameters->dim; i++)
|
|
{
|
|
VarDeclaration *v = (*parameters)[i];
|
|
|
|
if (v->storage_class & (STCref | STCout | STClazy))
|
|
continue;
|
|
|
|
if (v->noscope)
|
|
continue;
|
|
|
|
Expression *e = v->edtor;
|
|
if (e)
|
|
{ Statement *s = new ExpStatement(Loc(), e);
|
|
s = s->semantic(sc2);
|
|
int nothrowErrors = global.errors;
|
|
bool isnothrow = f->isnothrow & !(flags & FUNCFLAGnothrowInprocess);
|
|
int blockexit = s->blockExit(isnothrow);
|
|
if (f->isnothrow && (global.errors != nothrowErrors) )
|
|
error("'%s' is nothrow yet may throw", toChars());
|
|
if (flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow)
|
|
f->isnothrow = FALSE;
|
|
if (fbody->blockExit(f->isnothrow) == BEfallthru)
|
|
fbody = new CompoundStatement(Loc(), fbody, s);
|
|
else
|
|
fbody = new TryFinallyStatement(Loc(), fbody, s);
|
|
}
|
|
}
|
|
}
|
|
// from this point on all possible 'throwers' are checked
|
|
flags &= ~FUNCFLAGnothrowInprocess;
|
|
#endif
|
|
|
|
if (isSynchronized())
|
|
{ /* Wrap the entire function body in a synchronized statement
|
|
*/
|
|
AggregateDeclaration *ad = isThis();
|
|
ClassDeclaration *cd = ad ? ad->isClassDeclaration() : parent->isClassDeclaration();
|
|
|
|
if (cd)
|
|
{
|
|
if (!global.params.is64bit &&
|
|
global.params.isWindows &&
|
|
!isStatic() && !fbody->usesEH() && !global.params.trace)
|
|
{
|
|
/* The back end uses the "jmonitor" hack for syncing;
|
|
* no need to do the sync at this level.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
Expression *vsync;
|
|
if (isStatic())
|
|
{ // The monitor is in the ClassInfo
|
|
vsync = new DotIdExp(loc, new DsymbolExp(loc, cd), Id::classinfo);
|
|
}
|
|
else
|
|
{ // 'this' is the monitor
|
|
vsync = new VarExp(loc, vthis);
|
|
}
|
|
fbody = new PeelStatement(fbody); // don't redo semantic()
|
|
fbody = new SynchronizedStatement(loc, vsync, fbody);
|
|
fbody = fbody->semantic(sc2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error("synchronized function %s must be a member of a class", toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
sc2->callSuper = 0;
|
|
sc2->pop();
|
|
}
|
|
|
|
/* If function survived being marked as impure, then it is pure
|
|
*/
|
|
if (flags & FUNCFLAGpurityInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGpurityInprocess;
|
|
if (type == f) f = f->copy();
|
|
f->purity = PUREfwdref;
|
|
}
|
|
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGsafetyInprocess;
|
|
if (type == f) f = f->copy();
|
|
f->trust = TRUSTsafe;
|
|
}
|
|
|
|
// reset deco to apply inference result to mangled name
|
|
if (f != type)
|
|
f->deco = NULL;
|
|
|
|
// Do semantic type AFTER pure/nothrow inference.
|
|
if (!f->deco)
|
|
{
|
|
sc = sc->push();
|
|
sc->stc = 0;
|
|
sc->linkage = linkage; // Bugzilla 8496
|
|
type = f->semantic(loc, sc);
|
|
sc = sc->pop();
|
|
}
|
|
|
|
if (global.gag && global.errors != nerrors)
|
|
{
|
|
/* Errors happened when compiling this function.
|
|
*/
|
|
semanticRun = PASSsemanticdone; // Ensure errors get reported again
|
|
/* Except that re-running semantic3() doesn't always produce errors a second
|
|
* time through.
|
|
* See Bugzilla 8348
|
|
* Need a better way to deal with this than gagging.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
semanticRun = PASSsemantic3done;
|
|
semantic3Errors = global.errors - nerrors;
|
|
}
|
|
//printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars());
|
|
//fflush(stdout);
|
|
}
|
|
|
|
bool FuncDeclaration::functionSemantic()
|
|
{
|
|
if (scope && !originalType) // semantic not yet run
|
|
{
|
|
TemplateInstance *spec = isSpeculative();
|
|
unsigned olderrs = global.errors;
|
|
unsigned oldgag = global.gag;
|
|
if (global.gag && !spec)
|
|
global.gag = 0;
|
|
semantic(scope);
|
|
global.gag = oldgag;
|
|
if (spec && global.errors != olderrs)
|
|
spec->errors = global.errors - olderrs;
|
|
if (olderrs != global.errors) // if errors compiling this function
|
|
return false;
|
|
}
|
|
|
|
// if inferring return type, sematic3 needs to be run
|
|
TemplateInstance *ti;
|
|
AggregateDeclaration *ad;
|
|
if (scope &&
|
|
(inferRetType && type && !type->nextOf() ||
|
|
(ti = parent->isTemplateInstance()) != NULL && !ti->isTemplateMixin() && ti->name == ident ||
|
|
(ad = isThis()) != NULL && ad->parent && ad->parent->isTemplateInstance() && !isVirtualMethod()))
|
|
{
|
|
return functionSemantic3();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FuncDeclaration::functionSemantic3()
|
|
{
|
|
if (semanticRun < PASSsemantic3 && scope)
|
|
{
|
|
/* Forward reference - we need to run semantic3 on this function.
|
|
* If errors are gagged, and it's not part of a speculative
|
|
* template instance, we need to temporarily ungag errors.
|
|
*/
|
|
TemplateInstance *spec = isSpeculative();
|
|
unsigned olderrs = global.errors;
|
|
unsigned oldgag = global.gag;
|
|
if (global.gag && !spec)
|
|
global.gag = 0;
|
|
semantic3(scope);
|
|
global.gag = oldgag;
|
|
|
|
// If it is a speculatively-instantiated template, and errors occur,
|
|
// we need to mark the template as having errors.
|
|
if (spec && global.errors != olderrs)
|
|
spec->errors = global.errors - olderrs;
|
|
if (olderrs != global.errors) // if errors compiling this function
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
//printf("FuncDeclaration::toCBuffer() '%s'\n", toChars());
|
|
|
|
StorageClassDeclaration::stcToCBuffer(buf, storage_class);
|
|
type->toCBuffer(buf, ident, hgs);
|
|
if(hgs->hdrgen == 1)
|
|
{
|
|
if(storage_class & STCauto)
|
|
{
|
|
hgs->autoMember++;
|
|
bodyToCBuffer(buf, hgs);
|
|
hgs->autoMember--;
|
|
}
|
|
else if(hgs->tpltMember == 0 &&
|
|
#if IN_LLVM
|
|
!global.params.hdrKeepAllBodies
|
|
#else
|
|
global.params.useInline == 0
|
|
#endif
|
|
)
|
|
buf->writestring(";");
|
|
else
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
else
|
|
bodyToCBuffer(buf, hgs);
|
|
buf->writenl();
|
|
}
|
|
|
|
VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad)
|
|
{
|
|
if (ad)
|
|
{ VarDeclaration *v;
|
|
|
|
{
|
|
assert(ad->handle);
|
|
Type *thandle = ad->handle;
|
|
thandle = thandle->addMod(type->mod);
|
|
thandle = thandle->addStorageClass(storage_class);
|
|
v = new ThisDeclaration(loc, thandle);
|
|
//v = new ThisDeclaration(loc, isCtorDeclaration() ? ad->handle : thandle);
|
|
v->storage_class |= STCparameter;
|
|
if (thandle->ty == Tstruct)
|
|
v->storage_class |= STCref;
|
|
v->semantic(sc);
|
|
if (!sc->insert(v))
|
|
assert(0);
|
|
v->parent = this;
|
|
return v;
|
|
}
|
|
}
|
|
else if (isNested())
|
|
{
|
|
/* The 'this' for a nested function is the link to the
|
|
* enclosing function's stack frame.
|
|
* Note that nested functions and member functions are disjoint.
|
|
*/
|
|
VarDeclaration *v = new ThisDeclaration(loc, Type::tvoid->pointerTo());
|
|
v->storage_class |= STCparameter;
|
|
v->semantic(sc);
|
|
if (!sc->insert(v))
|
|
assert(0);
|
|
v->parent = this;
|
|
return v;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int FuncDeclaration::equals(Object *o)
|
|
{
|
|
if (this == o)
|
|
return TRUE;
|
|
|
|
Dsymbol *s = isDsymbol(o);
|
|
if (s)
|
|
{
|
|
FuncDeclaration *fd1 = this->toAliasFunc();
|
|
FuncDeclaration *fd2 = s->isFuncDeclaration();
|
|
if (fd2)
|
|
{
|
|
fd2 = fd2->toAliasFunc();
|
|
return fd1->toParent()->equals(fd2->toParent()) &&
|
|
fd1->ident->equals(fd2->ident) && fd1->type->equals(fd2->type);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (fbody && (!hgs->hdrgen ||
|
|
#if IN_LLVM
|
|
global.params.hdrKeepAllBodies ||
|
|
#else
|
|
global.params.useInline ||
|
|
#endif
|
|
hgs->autoMember || hgs->tpltMember))
|
|
{
|
|
int savetlpt = hgs->tpltMember;
|
|
int saveauto = hgs->autoMember;
|
|
hgs->tpltMember = 0;
|
|
hgs->autoMember = 0;
|
|
|
|
buf->writenl();
|
|
|
|
// in{}
|
|
if (frequire)
|
|
{
|
|
buf->writestring("in");
|
|
buf->writenl();
|
|
frequire->toCBuffer(buf, hgs);
|
|
}
|
|
|
|
// out{}
|
|
if (fensure)
|
|
{
|
|
buf->writestring("out");
|
|
if (outId)
|
|
{ buf->writebyte('(');
|
|
buf->writestring(outId->toChars());
|
|
buf->writebyte(')');
|
|
}
|
|
buf->writenl();
|
|
fensure->toCBuffer(buf, hgs);
|
|
}
|
|
|
|
if (frequire || fensure)
|
|
{
|
|
buf->writestring("body");
|
|
buf->writenl();
|
|
}
|
|
|
|
buf->writebyte('{');
|
|
buf->writenl();
|
|
buf->level++;
|
|
fbody->toCBuffer(buf, hgs);
|
|
buf->level--;
|
|
buf->writebyte('}');
|
|
buf->writenl();
|
|
|
|
hgs->tpltMember = savetlpt;
|
|
hgs->autoMember = saveauto;
|
|
}
|
|
else
|
|
{ buf->writeByte(';');
|
|
buf->writenl();
|
|
}
|
|
}
|
|
|
|
/****************************************************
|
|
* Declare result variable lazily.
|
|
*/
|
|
|
|
void FuncDeclaration::buildResultVar()
|
|
{
|
|
if (vresult)
|
|
return;
|
|
|
|
assert(type->nextOf());
|
|
assert(type->nextOf()->toBasetype()->ty != Tvoid);
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
|
|
Loc loc = this->loc;
|
|
|
|
if (fensure)
|
|
loc = fensure->loc;
|
|
|
|
if (!outId)
|
|
outId = Id::result; // provide a default
|
|
|
|
VarDeclaration *v = new VarDeclaration(loc, type->nextOf(), outId, NULL);
|
|
v->noscope = 1;
|
|
v->storage_class |= STCresult;
|
|
#if DMDV2
|
|
if (!isVirtual())
|
|
v->storage_class |= STCconst;
|
|
if (tf->isref)
|
|
{
|
|
v->storage_class |= STCref | STCforeach;
|
|
}
|
|
#endif
|
|
v->semantic(scout);
|
|
if (!scout->insert(v))
|
|
error("out result %s is already defined", v->toChars());
|
|
v->parent = this;
|
|
vresult = v;
|
|
|
|
// vresult gets initialized with the function return value
|
|
// in ReturnStatement::semantic()
|
|
}
|
|
|
|
/****************************************************
|
|
* Merge into this function the 'in' contracts of all it overrides.
|
|
* 'in's are OR'd together, i.e. only one of them needs to pass.
|
|
*/
|
|
|
|
Statement *FuncDeclaration::mergeFrequire(Statement *sf, Expressions *params)
|
|
{
|
|
if (!params)
|
|
params = fdrequireParams;
|
|
|
|
/* If a base function and its override both have an IN contract, then
|
|
* only one of them needs to succeed. This is done by generating:
|
|
*
|
|
* void derived.in() {
|
|
* try {
|
|
* base.in();
|
|
* }
|
|
* catch () {
|
|
* ... body of derived.in() ...
|
|
* }
|
|
* }
|
|
*
|
|
* So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid.
|
|
* If base.in() throws, then derived.in()'s body is executed.
|
|
*/
|
|
|
|
#if IN_LLVM
|
|
/* In LDC, we can't rely on these codegen hacks - we explicitly pass
|
|
* parameters on to the contract functions.
|
|
*/
|
|
#else
|
|
/* Implementing this is done by having the overriding function call
|
|
* nested functions (the fdrequire functions) nested inside the overridden
|
|
* function. This requires that the stack layout of the calling function's
|
|
* parameters and 'this' pointer be in the same place (as the nested
|
|
* function refers to them).
|
|
* This is easy for the parameters, as they are all on the stack in the same
|
|
* place by definition, since it's an overriding function. The problem is
|
|
* getting the 'this' pointer in the same place, since it is a local variable.
|
|
* We did some hacks in the code generator to make this happen:
|
|
* 1. always generate exception handler frame, or at least leave space for it
|
|
* in the frame (Windows 32 SEH only)
|
|
* 2. always generate an EBP style frame
|
|
* 3. since 'this' is passed in a register that is subsequently copied into
|
|
* a stack local, allocate that local immediately following the exception
|
|
* handler block, so it is always at the same offset from EBP.
|
|
*/
|
|
#endif
|
|
for (size_t i = 0; i < foverrides.dim; i++)
|
|
{
|
|
FuncDeclaration *fdv = foverrides[i];
|
|
|
|
/* The semantic pass on the contracts of the overridden functions must
|
|
* be completed before code generation occurs (bug 3602).
|
|
*/
|
|
if (fdv->fdrequire && fdv->fdrequire->semanticRun != PASSsemantic3done)
|
|
{
|
|
assert(fdv->scope);
|
|
Scope *sc = fdv->scope->push();
|
|
sc->stc &= ~STCoverride;
|
|
fdv->semantic3(sc);
|
|
sc->pop();
|
|
}
|
|
|
|
sf = fdv->mergeFrequire(sf, params);
|
|
if (sf && fdv->fdrequire)
|
|
{
|
|
//printf("fdv->frequire: %s\n", fdv->frequire->toChars());
|
|
/* Make the call:
|
|
* try { __require(); }
|
|
* catch { frequire; }
|
|
*/
|
|
Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdrequire, 0), params);
|
|
Statement *s2 = new ExpStatement(loc, e);
|
|
|
|
Catch *c = new Catch(loc, NULL, NULL, sf);
|
|
c->internalCatch = true;
|
|
Catches *catches = new Catches();
|
|
catches->push(c);
|
|
sf = new TryCatchStatement(loc, s2, catches);
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
return sf;
|
|
}
|
|
|
|
/****************************************************
|
|
* Merge into this function the 'out' contracts of all it overrides.
|
|
* 'out's are AND'd together, i.e. all of them need to pass.
|
|
*/
|
|
|
|
Statement *FuncDeclaration::mergeFensure(Statement *sf, Expressions *params)
|
|
{
|
|
if (!params)
|
|
params = fdensureParams;
|
|
|
|
/* Same comments as for mergeFrequire(), except that we take care
|
|
* of generating a consistent reference to the 'result' local by
|
|
* explicitly passing 'result' to the nested function as a reference
|
|
* argument.
|
|
* This won't work for the 'this' parameter as it would require changing
|
|
* the semantic code for the nested function so that it looks on the parameter
|
|
* list for the 'this' pointer, something that would need an unknown amount
|
|
* of tweaking of various parts of the compiler that I'd rather leave alone.
|
|
*/
|
|
for (size_t i = 0; i < foverrides.dim; i++)
|
|
{
|
|
FuncDeclaration *fdv = foverrides[i];
|
|
|
|
/* The semantic pass on the contracts of the overridden functions must
|
|
* be completed before code generation occurs (bug 3602 and 5230).
|
|
*/
|
|
if (fdv->fdensure && fdv->fdensure->semanticRun != PASSsemantic3done)
|
|
{
|
|
assert(fdv->scope);
|
|
Scope *sc = fdv->scope->push();
|
|
sc->stc &= ~STCoverride;
|
|
fdv->semantic3(sc);
|
|
sc->pop();
|
|
}
|
|
|
|
sf = fdv->mergeFensure(sf, params);
|
|
if (fdv->fdensure)
|
|
{
|
|
//printf("fdv->fensure: %s\n", fdv->fensure->toChars());
|
|
// Make the call: __ensure(result)
|
|
Expression *eresult = NULL;
|
|
if (outId)
|
|
{
|
|
#if IN_LLVM
|
|
eresult = (*params)[0];
|
|
#else
|
|
eresult = new IdentifierExp(loc, outId);
|
|
#endif
|
|
Type *t1 = fdv->type->nextOf()->toBasetype();
|
|
#if IN_LLVM
|
|
// We actually check for matching types in CommaExp::toElem,
|
|
// 'testcontract' breaks without this.
|
|
t1 = t1->constOf();
|
|
#endif
|
|
Type *t2 = this->type->nextOf()->toBasetype();
|
|
int offset;
|
|
if (t1->isBaseOf(t2, &offset) && offset != 0)
|
|
{
|
|
/* Making temporary reference variable is necessary
|
|
* to match offset difference in covariant return.
|
|
* See bugzilla 5204.
|
|
*/
|
|
ExpInitializer *ei = new ExpInitializer(Loc(), eresult);
|
|
VarDeclaration *v = new VarDeclaration(Loc(), t1, Lexer::uniqueId("__covres"), ei);
|
|
DeclarationExp *de = new DeclarationExp(Loc(), v);
|
|
VarExp *ve = new VarExp(Loc(), v);
|
|
eresult = new CommaExp(Loc(), de, ve);
|
|
}
|
|
}
|
|
#if IN_LLVM
|
|
if (eresult)
|
|
(*params)[0] = eresult;
|
|
Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), params);
|
|
#else
|
|
Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), eresult);
|
|
#endif
|
|
Statement *s2 = new ExpStatement(loc, e);
|
|
|
|
if (sf)
|
|
{
|
|
sf = new CompoundStatement(fensure->loc, s2, sf);
|
|
}
|
|
else
|
|
sf = s2;
|
|
}
|
|
}
|
|
return sf;
|
|
}
|
|
|
|
/****************************************************
|
|
* Determine if 'this' overrides fd.
|
|
* Return !=0 if it does.
|
|
*/
|
|
|
|
int FuncDeclaration::overrides(FuncDeclaration *fd)
|
|
{ int result = 0;
|
|
|
|
if (fd->ident == ident)
|
|
{
|
|
int cov = type->covariant(fd->type);
|
|
if (cov)
|
|
{ ClassDeclaration *cd1 = toParent()->isClassDeclaration();
|
|
ClassDeclaration *cd2 = fd->toParent()->isClassDeclaration();
|
|
|
|
if (cd1 && cd2 && cd2->isBaseOf(cd1, NULL))
|
|
result = 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*************************************************
|
|
* Find index of function in vtbl[0..dim] that
|
|
* this function overrides.
|
|
* Prefer an exact match to a covariant one.
|
|
* Returns:
|
|
* -1 didn't find one
|
|
* -2 can't determine because of forward references
|
|
*/
|
|
|
|
int FuncDeclaration::findVtblIndex(Dsymbols *vtbl, int dim)
|
|
{
|
|
FuncDeclaration *mismatch = NULL;
|
|
StorageClass mismatchstc = 0;
|
|
int mismatchvi = -1;
|
|
int exactvi = -1;
|
|
int bestvi = -1;
|
|
for (int vi = 0; vi < dim; vi++)
|
|
{
|
|
FuncDeclaration *fdv = (*vtbl)[vi]->isFuncDeclaration();
|
|
if (fdv && fdv->ident == ident)
|
|
{
|
|
if (type->equals(fdv->type)) // if exact match
|
|
{
|
|
if (fdv->parent->isClassDeclaration())
|
|
return vi; // no need to look further
|
|
|
|
if (exactvi >= 0)
|
|
{
|
|
error("cannot determine overridden function");
|
|
return exactvi;
|
|
}
|
|
exactvi = vi;
|
|
|
|
bestvi = vi;
|
|
continue;
|
|
}
|
|
|
|
StorageClass stc = 0;
|
|
int cov = type->covariant(fdv->type, &stc);
|
|
//printf("\tbaseclass cov = %d\n", cov);
|
|
switch (cov)
|
|
{
|
|
case 0: // types are distinct
|
|
break;
|
|
|
|
case 1:
|
|
bestvi = vi; // covariant, but not identical
|
|
break; // keep looking for an exact match
|
|
|
|
case 2:
|
|
mismatchvi = vi;
|
|
mismatchstc = stc;
|
|
mismatch = fdv; // overrides, but is not covariant
|
|
break; // keep looking for an exact match
|
|
|
|
case 3:
|
|
return -2; // forward references
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
if (bestvi == -1 && mismatch)
|
|
{
|
|
//type->print();
|
|
//mismatch->type->print();
|
|
//printf("%s %s\n", type->deco, mismatch->type->deco);
|
|
//printf("stc = %llx\n", mismatchstc);
|
|
if (mismatchstc)
|
|
{ // Fix it by modifying the type to add the storage classes
|
|
type = type->addStorageClass(mismatchstc);
|
|
bestvi = mismatchvi;
|
|
}
|
|
else
|
|
error("of type %s overrides but is not covariant with %s of type %s",
|
|
type->toChars(), mismatch->toPrettyChars(), mismatch->type->toChars());
|
|
}
|
|
return bestvi;
|
|
}
|
|
|
|
/****************************************************
|
|
* Overload this FuncDeclaration with the new one f.
|
|
* Return !=0 if successful; i.e. no conflict.
|
|
*/
|
|
|
|
int FuncDeclaration::overloadInsert(Dsymbol *s)
|
|
{
|
|
//printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s->toChars(), toChars());
|
|
AliasDeclaration *ad = s->isAliasDeclaration();
|
|
if (ad)
|
|
{
|
|
if (overnext)
|
|
return overnext->overloadInsert(ad);
|
|
if (!ad->aliassym && ad->type->ty != Tident && ad->type->ty != Tinstance)
|
|
{
|
|
//printf("\tad = '%s'\n", ad->type->toChars());
|
|
return FALSE;
|
|
}
|
|
overnext = ad;
|
|
//printf("\ttrue: no conflict\n");
|
|
return TRUE;
|
|
}
|
|
FuncDeclaration *fd = s->isFuncDeclaration();
|
|
if (!fd)
|
|
return FALSE;
|
|
|
|
#if 0
|
|
/* Disable this check because:
|
|
* const void foo();
|
|
* semantic() isn't run yet on foo(), so the const hasn't been
|
|
* applied yet.
|
|
*/
|
|
if (type)
|
|
{ printf("type = %s\n", type->toChars());
|
|
printf("fd->type = %s\n", fd->type->toChars());
|
|
}
|
|
if (type && fd->type && // can be NULL for overloaded constructors
|
|
fd->type->covariant(type) &&
|
|
fd->type->mod == type->mod &&
|
|
!isFuncAliasDeclaration())
|
|
{
|
|
//printf("\tfalse: conflict %s\n", kind());
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (overnext)
|
|
return overnext->overloadInsert(fd);
|
|
overnext = fd;
|
|
//printf("\ttrue: no conflict\n");
|
|
return TRUE;
|
|
}
|
|
|
|
/********************************************
|
|
* Find function in overload list that exactly matches t.
|
|
*/
|
|
|
|
/***************************************************
|
|
* Visit each overloaded function in turn, and call
|
|
* (*fp)(param, f) on it.
|
|
* Exit when no more, or (*fp)(param, f) returns 1.
|
|
* Returns:
|
|
* 0 continue
|
|
* 1 done
|
|
*/
|
|
|
|
int overloadApply(FuncDeclaration *fstart,
|
|
int (*fp)(void *, FuncDeclaration *),
|
|
void *param)
|
|
{
|
|
FuncDeclaration *f;
|
|
Declaration *d;
|
|
Declaration *next;
|
|
|
|
for (d = fstart; d; d = next)
|
|
{ FuncAliasDeclaration *fa = d->isFuncAliasDeclaration();
|
|
|
|
if (fa)
|
|
{
|
|
if (fa->hasOverloads)
|
|
{
|
|
if (overloadApply(fa->funcalias, fp, param))
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
f = fa->toAliasFunc();
|
|
if (!f)
|
|
{ d->error("is aliased to a function");
|
|
break;
|
|
}
|
|
if ((*fp)(param, f))
|
|
return 1;
|
|
}
|
|
next = fa->overnext;
|
|
}
|
|
else
|
|
{
|
|
AliasDeclaration *a = d->isAliasDeclaration();
|
|
|
|
if (a)
|
|
{
|
|
Dsymbol *s = a->toAlias();
|
|
next = s->isDeclaration();
|
|
if (next == a)
|
|
break;
|
|
if (next == fstart)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
f = d->isFuncDeclaration();
|
|
if (!f)
|
|
{ d->error("is aliased to a function");
|
|
break; // BUG: should print error message?
|
|
}
|
|
if ((*fp)(param, f))
|
|
return 1;
|
|
|
|
next = f->overnext;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/********************************************
|
|
* If there are no overloads of function f, return that function,
|
|
* otherwise return NULL.
|
|
*/
|
|
|
|
static int fpunique(void *param, FuncDeclaration *f)
|
|
{ FuncDeclaration **pf = (FuncDeclaration **)param;
|
|
|
|
if (*pf)
|
|
{ *pf = NULL;
|
|
return 1; // ambiguous, done
|
|
}
|
|
else
|
|
{ *pf = f;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
FuncDeclaration *FuncDeclaration::isUnique()
|
|
{ FuncDeclaration *result = NULL;
|
|
|
|
overloadApply(this, &fpunique, &result);
|
|
return result;
|
|
}
|
|
|
|
/********************************************
|
|
* Find function in overload list that exactly matches t.
|
|
*/
|
|
|
|
struct Param1
|
|
{
|
|
Type *t; // type to match
|
|
FuncDeclaration *f; // return value
|
|
};
|
|
|
|
int fp1(void *param, FuncDeclaration *f)
|
|
{ Param1 *p = (Param1 *)param;
|
|
Type *t = p->t;
|
|
|
|
if (t->equals(f->type))
|
|
{ p->f = f;
|
|
return 1;
|
|
}
|
|
|
|
#if DMDV2
|
|
/* Allow covariant matches, as long as the return type
|
|
* is just a const conversion.
|
|
* This allows things like pure functions to match with an impure function type.
|
|
*/
|
|
if (t->ty == Tfunction)
|
|
{ TypeFunction *tf = (TypeFunction *)f->type;
|
|
if (tf->covariant(t) == 1 &&
|
|
tf->nextOf()->implicitConvTo(t->nextOf()) >= MATCHconst)
|
|
{
|
|
p->f = f;
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
FuncDeclaration *FuncDeclaration::overloadExactMatch(Type *t)
|
|
{
|
|
Param1 p;
|
|
p.t = t;
|
|
p.f = NULL;
|
|
overloadApply(this, &fp1, &p);
|
|
return p.f;
|
|
}
|
|
|
|
|
|
/********************************************
|
|
* Decide which function matches the arguments best.
|
|
* flags 1: do not issue error message on no match, just return NULL
|
|
* 2: do not issue error on ambiguous matches and need explicit this
|
|
*/
|
|
|
|
struct Param2
|
|
{
|
|
Match *m;
|
|
#if DMDV2
|
|
Type *tthis;
|
|
int property; // 0: unintialized
|
|
// 1: seen @property
|
|
// 2: not @property
|
|
#endif
|
|
Expressions *arguments;
|
|
};
|
|
|
|
int fp2(void *param, FuncDeclaration *f)
|
|
{
|
|
Param2 *p = (Param2 *)param;
|
|
Match *m = p->m;
|
|
Expressions *arguments = p->arguments;
|
|
|
|
if (f != m->lastf) // skip duplicates
|
|
{
|
|
m->anyf = f;
|
|
TypeFunction *tf = (TypeFunction *)f->type;
|
|
|
|
int property = (tf->isproperty) ? 1 : 2;
|
|
if (p->property == 0)
|
|
p->property = property;
|
|
else if (p->property != property)
|
|
error(f->loc, "cannot overload both property and non-property functions");
|
|
|
|
/* For constructors, qualifier check will be opposite direction.
|
|
* Qualified constructor always makes qualified object, then will be checked
|
|
* that it is implicitly convertible to tthis.
|
|
*/
|
|
Type *tthis = f->needThis() ? p->tthis : NULL;
|
|
if (tthis && f->isCtorDeclaration())
|
|
{
|
|
//printf("%s tf->mod = x%x tthis->mod = x%x %d\n", tf->toChars(),
|
|
// tf->mod, tthis->mod, f->isolateReturn());
|
|
if (MODimplicitConv(tf->mod, tthis->mod) ||
|
|
tf->isWild() && tf->isShared() == tthis->isShared() ||
|
|
f->isolateReturn()/* && tf->isShared() == tthis->isShared()*/)
|
|
{ // Uniquely constructed object can ignore shared qualifier.
|
|
// TODO: Is this appropriate?
|
|
tthis = NULL;
|
|
}
|
|
else
|
|
return 0; // MATCHnomatch
|
|
}
|
|
MATCH match = tf->callMatch(tthis, arguments);
|
|
//printf("test1: match = %d\n", match);
|
|
if (match != MATCHnomatch)
|
|
{
|
|
if (match > m->last)
|
|
goto LfIsBetter;
|
|
|
|
if (match < m->last)
|
|
goto LlastIsBetter;
|
|
|
|
/* See if one of the matches overrides the other.
|
|
*/
|
|
if (m->lastf->overrides(f))
|
|
goto LlastIsBetter;
|
|
else if (f->overrides(m->lastf))
|
|
goto LfIsBetter;
|
|
|
|
#if DMDV2
|
|
/* Try to disambiguate using template-style partial ordering rules.
|
|
* In essence, if f() and g() are ambiguous, if f() can call g(),
|
|
* but g() cannot call f(), then pick f().
|
|
* This is because f() is "more specialized."
|
|
*/
|
|
{
|
|
MATCH c1 = f->leastAsSpecialized(m->lastf);
|
|
MATCH c2 = m->lastf->leastAsSpecialized(f);
|
|
//printf("c1 = %d, c2 = %d\n", c1, c2);
|
|
if (c1 > c2)
|
|
goto LfIsBetter;
|
|
if (c1 < c2)
|
|
goto LlastIsBetter;
|
|
}
|
|
|
|
/* If the two functions are the same function, like:
|
|
* int foo(int);
|
|
* int foo(int x) { ... }
|
|
* then pick the one with the body.
|
|
*/
|
|
if (tf->equals(m->lastf->type) &&
|
|
f->storage_class == m->lastf->storage_class &&
|
|
f->parent == m->lastf->parent &&
|
|
f->protection == m->lastf->protection &&
|
|
f->linkage == m->lastf->linkage)
|
|
{
|
|
if (f->fbody && !m->lastf->fbody)
|
|
goto LfIsBetter;
|
|
else if (!f->fbody && m->lastf->fbody)
|
|
goto LlastIsBetter;
|
|
}
|
|
#endif
|
|
Lambiguous:
|
|
m->nextf = f;
|
|
m->count++;
|
|
return 0;
|
|
|
|
LfIsBetter:
|
|
m->last = match;
|
|
m->lastf = f;
|
|
m->count = 1;
|
|
return 0;
|
|
|
|
LlastIsBetter:
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void overloadResolveX(Match *m, FuncDeclaration *fstart,
|
|
Type *tthis, Expressions *arguments)
|
|
{
|
|
Param2 p;
|
|
p.m = m;
|
|
p.tthis = tthis;
|
|
p.property = 0;
|
|
p.arguments = arguments;
|
|
overloadApply(fstart, &fp2, &p);
|
|
}
|
|
|
|
static void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod)
|
|
{
|
|
bool bothMutable = ((lhsMod & rhsMod) == 0);
|
|
bool sharedMismatch = ((lhsMod ^ rhsMod) & MODshared);
|
|
bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODshared);
|
|
|
|
if (lhsMod & MODshared)
|
|
buf->writestring("shared ");
|
|
else if (sharedMismatch && !(lhsMod & MODimmutable))
|
|
buf->writestring("non-shared ");
|
|
|
|
if (bothMutable && sharedMismatchOnly)
|
|
{ }
|
|
else if (lhsMod & MODimmutable)
|
|
buf->writestring("immutable ");
|
|
else if (lhsMod & MODconst)
|
|
buf->writestring("const ");
|
|
else if (lhsMod & MODwild)
|
|
buf->writestring("inout ");
|
|
else
|
|
buf->writestring("mutable ");
|
|
}
|
|
|
|
FuncDeclaration *FuncDeclaration::overloadResolve(Loc loc, Type *tthis, Expressions *arguments, int flags)
|
|
{
|
|
#if 0
|
|
printf("FuncDeclaration::overloadResolve('%s')\n", toChars());
|
|
if (arguments)
|
|
{
|
|
for (size_t i = 0; i < arguments->dim; i++)
|
|
{
|
|
Expression *arg = (*arguments)[i];
|
|
assert(arg->type);
|
|
printf("\t%s: ", arg->toChars());
|
|
arg->type->print();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Match m;
|
|
memset(&m, 0, sizeof(m));
|
|
m.last = MATCHnomatch;
|
|
overloadResolveX(&m, this, tthis, arguments);
|
|
|
|
if (m.count == 1) // exactly one match
|
|
{
|
|
if (!(flags & 1))
|
|
m.lastf->functionSemantic();
|
|
return m.lastf;
|
|
}
|
|
|
|
if (m.last != MATCHnomatch && (flags & 2) && !tthis && m.lastf->needThis())
|
|
{
|
|
return m.lastf;
|
|
}
|
|
|
|
/* Failed to find a best match.
|
|
* Do nothing or print error.
|
|
*/
|
|
if (m.last == MATCHnomatch && (flags & 1))
|
|
{ // if do not print error messages
|
|
}
|
|
else
|
|
{
|
|
OutBuffer buf;
|
|
|
|
buf.writeByte('(');
|
|
if (arguments && arguments->dim)
|
|
{
|
|
HdrGenState hgs;
|
|
argExpTypesToCBuffer(&buf, arguments, &hgs);
|
|
}
|
|
buf.writeByte(')');
|
|
if (tthis)
|
|
tthis->modToBuffer(&buf);
|
|
|
|
if (m.last == MATCHnomatch)
|
|
{
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
if (tthis && !MODimplicitConv(tthis->mod, tf->mod)) // modifier mismatch
|
|
{
|
|
OutBuffer thisBuf, funcBuf;
|
|
MODMatchToBuffer(&thisBuf, tthis->mod, tf->mod);
|
|
MODMatchToBuffer(&funcBuf, tf->mod, tthis->mod);
|
|
::error(loc, "%smethod %s is not callable using a %sobject",
|
|
funcBuf.toChars(), this->toPrettyChars(), thisBuf.toChars());
|
|
}
|
|
else
|
|
{
|
|
//printf("tf = %s, args = %s\n", tf->deco, (*arguments)[0]->type->deco);
|
|
error(loc, "%s%s is not callable using argument types %s",
|
|
Parameter::argsTypesToChars(tf->parameters, tf->varargs),
|
|
tf->modToChars(),
|
|
buf.toChars());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TypeFunction *t1 = (TypeFunction *)m.lastf->type;
|
|
TypeFunction *t2 = (TypeFunction *)m.nextf->type;
|
|
|
|
error(loc, "called with argument types:\n\t(%s)\nmatches both:\n\t%s(%d): %s%s\nand:\n\t%s(%d): %s%s",
|
|
buf.toChars(),
|
|
m.lastf->loc.filename, m.lastf->loc.linnum, m.lastf->toPrettyChars(), Parameter::argsTypesToChars(t1->parameters, t1->varargs),
|
|
m.nextf->loc.filename, m.nextf->loc.linnum, m.nextf->toPrettyChars(), Parameter::argsTypesToChars(t2->parameters, t2->varargs));
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************
|
|
* Determine partial specialization order of 'this' vs g.
|
|
* This is very similar to TemplateDeclaration::leastAsSpecialized().
|
|
* Returns:
|
|
* match 'this' is at least as specialized as g
|
|
* 0 g is more specialized than 'this'
|
|
*/
|
|
|
|
#if DMDV2
|
|
MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g)
|
|
{
|
|
#define LOG_LEASTAS 0
|
|
|
|
#if LOG_LEASTAS
|
|
printf("%s.leastAsSpecialized(%s)\n", toChars(), g->toChars());
|
|
printf("%s, %s\n", type->toChars(), g->type->toChars());
|
|
#endif
|
|
|
|
/* This works by calling g() with f()'s parameters, and
|
|
* if that is possible, then f() is at least as specialized
|
|
* as g() is.
|
|
*/
|
|
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
TypeFunction *tg = (TypeFunction *)g->type;
|
|
size_t nfparams = Parameter::dim(tf->parameters);
|
|
size_t ngparams = Parameter::dim(tg->parameters);
|
|
MATCH match = MATCHexact;
|
|
|
|
/* If both functions have a 'this' pointer, and the mods are not
|
|
* the same and g's is not const, then this is less specialized.
|
|
*/
|
|
if (needThis() && g->needThis() && tf->mod != tg->mod)
|
|
{
|
|
if (isCtorDeclaration())
|
|
{
|
|
if (MODimplicitConv(tg->mod, tf->mod))
|
|
match = MATCHconst;
|
|
else
|
|
return MATCHnomatch;
|
|
}
|
|
else
|
|
{
|
|
if (MODimplicitConv(tf->mod, tg->mod))
|
|
match = MATCHconst;
|
|
else
|
|
return MATCHnomatch;
|
|
}
|
|
}
|
|
|
|
/* Create a dummy array of arguments out of the parameters to f()
|
|
*/
|
|
Expressions args;
|
|
args.setDim(nfparams);
|
|
for (size_t u = 0; u < nfparams; u++)
|
|
{
|
|
Parameter *p = Parameter::getNth(tf->parameters, u);
|
|
Expression *e;
|
|
if (p->storageClass & (STCref | STCout))
|
|
{
|
|
e = new IdentifierExp(Loc(), p->ident);
|
|
e->type = p->type;
|
|
}
|
|
else
|
|
e = p->type->defaultInitLiteral(Loc());
|
|
args[u] = e;
|
|
}
|
|
|
|
MATCH m = (MATCH) tg->callMatch(NULL, &args, 1);
|
|
if (m)
|
|
{
|
|
/* A variadic parameter list is less specialized than a
|
|
* non-variadic one.
|
|
*/
|
|
if (tf->varargs && !tg->varargs)
|
|
goto L1; // less specialized
|
|
|
|
#if LOG_LEASTAS
|
|
printf(" matches %d, so is least as specialized\n", m);
|
|
#endif
|
|
return m;
|
|
}
|
|
L1:
|
|
#if LOG_LEASTAS
|
|
printf(" doesn't match, so is not as specialized\n");
|
|
#endif
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
/*******************************************
|
|
* Given a symbol that could be either a FuncDeclaration or
|
|
* a function template, resolve it to a function symbol.
|
|
* loc instantiation location
|
|
* sc instantiation scope
|
|
* tiargs initial list of template arguments
|
|
* tthis if !NULL, the 'this' pointer argument
|
|
* fargs arguments to function
|
|
* flags 1: do not issue error message on no match, just return NULL
|
|
* 2: overloadResolve only
|
|
*/
|
|
|
|
FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s,
|
|
Objects *tiargs,
|
|
Type *tthis,
|
|
Expressions *arguments,
|
|
int flags)
|
|
{
|
|
if (!s)
|
|
return NULL; // no match
|
|
if (tiargs && arrayObjectIsError(tiargs) ||
|
|
arguments && arrayObjectIsError((Objects *)arguments))
|
|
{
|
|
return NULL;
|
|
}
|
|
FuncDeclaration *f = s->isFuncDeclaration();
|
|
if (f)
|
|
f = f->overloadResolve(loc, tthis, arguments, flags);
|
|
else
|
|
{ TemplateDeclaration *td = s->isTemplateDeclaration();
|
|
assert(td);
|
|
if (!sc) sc = td->scope;
|
|
f = td->deduceFunctionTemplate(loc, sc, tiargs, tthis, arguments, flags);
|
|
}
|
|
return f;
|
|
}
|
|
#endif
|
|
|
|
/********************************
|
|
* Labels are in a separate scope, one per function.
|
|
*/
|
|
|
|
LabelDsymbol *FuncDeclaration::searchLabel(Identifier *ident)
|
|
{ Dsymbol *s;
|
|
|
|
if (!labtab)
|
|
labtab = new DsymbolTable(); // guess we need one
|
|
|
|
s = labtab->lookup(ident);
|
|
if (!s)
|
|
{
|
|
s = new LabelDsymbol(ident);
|
|
labtab->insert(s);
|
|
}
|
|
return (LabelDsymbol *)s;
|
|
}
|
|
|
|
/****************************************
|
|
* If non-static member function that has a 'this' pointer,
|
|
* return the aggregate it is a member of.
|
|
* Otherwise, return NULL.
|
|
*/
|
|
|
|
AggregateDeclaration *FuncDeclaration::isThis()
|
|
{ AggregateDeclaration *ad;
|
|
|
|
//printf("+FuncDeclaration::isThis() '%s'\n", toChars());
|
|
ad = NULL;
|
|
if ((storage_class & STCstatic) == 0)
|
|
{
|
|
ad = isMember2();
|
|
}
|
|
//printf("-FuncDeclaration::isThis() %p\n", ad);
|
|
return ad;
|
|
}
|
|
|
|
AggregateDeclaration *FuncDeclaration::isMember2()
|
|
{ AggregateDeclaration *ad;
|
|
|
|
//printf("+FuncDeclaration::isMember2() '%s'\n", toChars());
|
|
ad = NULL;
|
|
for (Dsymbol *s = this; s; s = s->parent)
|
|
{
|
|
//printf("\ts = '%s', parent = '%s', kind = %s\n", s->toChars(), s->parent->toChars(), s->parent->kind());
|
|
ad = s->isMember();
|
|
if (ad)
|
|
{
|
|
break;
|
|
}
|
|
if (!s->parent ||
|
|
(!s->parent->isTemplateInstance()))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//printf("-FuncDeclaration::isMember2() %p\n", ad);
|
|
return ad;
|
|
}
|
|
|
|
/*****************************************
|
|
* Determine lexical level difference from 'this' to nested function 'fd'.
|
|
* Error if this cannot call fd.
|
|
* Returns:
|
|
* 0 same level
|
|
* -1 increase nesting by 1 (fd is nested within 'this')
|
|
* >0 decrease nesting by number
|
|
*/
|
|
|
|
int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd)
|
|
{ int level;
|
|
Dsymbol *s;
|
|
Dsymbol *fdparent;
|
|
|
|
//printf("FuncDeclaration::getLevel(fd = '%s')\n", fd->toChars());
|
|
fdparent = fd->toParent2();
|
|
if (fdparent == this)
|
|
return -1;
|
|
s = this;
|
|
level = 0;
|
|
while (fd != s && fdparent != s->toParent2())
|
|
{
|
|
//printf("\ts = %s, '%s'\n", s->kind(), s->toChars());
|
|
FuncDeclaration *thisfd = s->isFuncDeclaration();
|
|
if (thisfd)
|
|
{ if (!thisfd->isNested() && !thisfd->vthis && !sc->intypeof)
|
|
goto Lerr;
|
|
}
|
|
else
|
|
{
|
|
AggregateDeclaration *thiscd = s->isAggregateDeclaration();
|
|
if (thiscd)
|
|
{
|
|
/* AggregateDeclaration::isNested returns true only when
|
|
* it has a hidden pointer.
|
|
* But, calling the function belongs unrelated lexical scope
|
|
* is still allowed inside typeof.
|
|
*
|
|
* struct Map(alias fun) {
|
|
* typeof({ return fun(); }) RetType;
|
|
* // No member function makes Map struct 'not nested'.
|
|
* }
|
|
*/
|
|
if (!thiscd->isNested() && !sc->intypeof)
|
|
goto Lerr;
|
|
}
|
|
else
|
|
goto Lerr;
|
|
}
|
|
|
|
s = s->toParent2();
|
|
assert(s);
|
|
level++;
|
|
}
|
|
return level;
|
|
|
|
Lerr:
|
|
// Don't give error if in template constraint
|
|
if (!((sc->flags & SCOPEstaticif) && parent->isTemplateDeclaration()))
|
|
error(loc, "cannot access frame of function %s", fd->toPrettyChars());
|
|
return 1;
|
|
}
|
|
|
|
void FuncDeclaration::appendExp(Expression *e)
|
|
{ Statement *s;
|
|
|
|
s = new ExpStatement(Loc(), e);
|
|
appendState(s);
|
|
}
|
|
|
|
void FuncDeclaration::appendState(Statement *s)
|
|
{
|
|
if (!fbody)
|
|
fbody = s;
|
|
else
|
|
{
|
|
CompoundStatement *cs = fbody->isCompoundStatement();
|
|
if (cs)
|
|
{
|
|
if (!cs->statements)
|
|
fbody = s;
|
|
else
|
|
cs->statements->push(s);
|
|
}
|
|
else
|
|
fbody = new CompoundStatement(Loc(), fbody, s);
|
|
}
|
|
}
|
|
|
|
const char *FuncDeclaration::toPrettyChars()
|
|
{
|
|
if (isMain())
|
|
return "D main";
|
|
else
|
|
return Dsymbol::toPrettyChars();
|
|
}
|
|
|
|
/** for diagnostics, e.g. 'int foo(int x, int y) pure' */
|
|
const char *FuncDeclaration::toFullSignature()
|
|
{
|
|
OutBuffer buf;
|
|
HdrGenState hgs;
|
|
functionToCBuffer2((TypeFunction *)type, &buf, &hgs, 0, toChars());
|
|
buf.writeByte(0);
|
|
return buf.extractData();
|
|
}
|
|
|
|
int FuncDeclaration::isMain()
|
|
{
|
|
return ident == Id::main &&
|
|
linkage != LINKc && !isMember() && !isNested();
|
|
}
|
|
|
|
int FuncDeclaration::isWinMain()
|
|
{
|
|
//printf("FuncDeclaration::isWinMain() %s\n", toChars());
|
|
#if 0
|
|
int x = ident == Id::WinMain &&
|
|
linkage != LINKc && !isMember();
|
|
printf("%s\n", x ? "yes" : "no");
|
|
return x;
|
|
#else
|
|
return ident == Id::WinMain &&
|
|
linkage != LINKc && !isMember();
|
|
#endif
|
|
}
|
|
|
|
int FuncDeclaration::isDllMain()
|
|
{
|
|
return ident == Id::DllMain &&
|
|
linkage != LINKc && !isMember();
|
|
}
|
|
|
|
int FuncDeclaration::isExport()
|
|
{
|
|
return protection == PROTexport;
|
|
}
|
|
|
|
int FuncDeclaration::isImportedSymbol()
|
|
{
|
|
//printf("isImportedSymbol()\n");
|
|
//printf("protection = %d\n", protection);
|
|
return (protection == PROTexport) && !fbody;
|
|
}
|
|
|
|
// Determine if function goes into virtual function pointer table
|
|
|
|
int FuncDeclaration::isVirtual()
|
|
{
|
|
if (toAliasFunc() != this)
|
|
return toAliasFunc()->isVirtual();
|
|
|
|
Dsymbol *p = toParent();
|
|
#if 0
|
|
printf("FuncDeclaration::isVirtual(%s)\n", toChars());
|
|
printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), protection == PROTprivate, isCtorDeclaration(), linkage != LINKd);
|
|
printf("result is %d\n",
|
|
isMember() &&
|
|
!(isStatic() || protection == PROTprivate || protection == PROTpackage) &&
|
|
p->isClassDeclaration() &&
|
|
!(p->isInterfaceDeclaration() && isFinal()));
|
|
#endif
|
|
return isMember() &&
|
|
!(isStatic() || protection == PROTprivate || protection == PROTpackage) &&
|
|
p->isClassDeclaration() &&
|
|
!(p->isInterfaceDeclaration() && isFinal());
|
|
}
|
|
|
|
// Determine if a function is pedantically virtual
|
|
|
|
int FuncDeclaration::isVirtualMethod()
|
|
{
|
|
if (toAliasFunc() != this)
|
|
return toAliasFunc()->isVirtualMethod();
|
|
|
|
//printf("FuncDeclaration::isVirtualMethod() %s\n", toChars());
|
|
if (!isVirtual())
|
|
return 0;
|
|
// If it's a final method, and does not override anything, then it is not virtual
|
|
if (isFinal() && foverrides.dim == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int FuncDeclaration::isFinal()
|
|
{
|
|
if (toAliasFunc() != this)
|
|
return toAliasFunc()->isFinal();
|
|
|
|
ClassDeclaration *cd;
|
|
#if 0
|
|
printf("FuncDeclaration::isFinal(%s), %x\n", toChars(), Declaration::isFinal());
|
|
printf("%p %d %d %d\n", isMember(), isStatic(), Declaration::isFinal(), ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal));
|
|
printf("result is %d\n",
|
|
isMember() &&
|
|
(Declaration::isFinal() ||
|
|
((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal)));
|
|
if (cd)
|
|
printf("\tmember of %s\n", cd->toChars());
|
|
#if 0
|
|
!(isStatic() || protection == PROTprivate || protection == PROTpackage) &&
|
|
(cd = toParent()->isClassDeclaration()) != NULL &&
|
|
cd->storage_class & STCfinal);
|
|
#endif
|
|
#endif
|
|
return isMember() &&
|
|
(Declaration::isFinal() ||
|
|
((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal));
|
|
}
|
|
|
|
int FuncDeclaration::isAbstract()
|
|
{
|
|
return storage_class & STCabstract;
|
|
}
|
|
|
|
int FuncDeclaration::isCodeseg()
|
|
{
|
|
return TRUE; // functions are always in the code segment
|
|
}
|
|
|
|
int FuncDeclaration::isOverloadable()
|
|
{
|
|
return 1; // functions can be overloaded
|
|
}
|
|
|
|
int FuncDeclaration::hasOverloads()
|
|
{
|
|
return overnext != NULL;
|
|
}
|
|
|
|
enum PURE FuncDeclaration::isPure()
|
|
{
|
|
//printf("FuncDeclaration::isPure() '%s'\n", toChars());
|
|
assert(type->ty == Tfunction);
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
if (flags & FUNCFLAGpurityInprocess)
|
|
setImpure();
|
|
if (tf->purity == PUREfwdref)
|
|
tf->purityLevel();
|
|
enum PURE purity = tf->purity;
|
|
if (purity > PUREweak && isNested())
|
|
purity = PUREweak;
|
|
if (purity > PUREweak && needThis())
|
|
{ // The attribute of the 'this' reference affects purity strength
|
|
if (type->mod & MODimmutable)
|
|
;
|
|
else if (type->mod & (MODconst | MODwild) && purity >= PUREconst)
|
|
purity = PUREconst;
|
|
else
|
|
purity = PUREweak;
|
|
}
|
|
tf->purity = purity;
|
|
// ^ This rely on the current situation that every FuncDeclaration has a
|
|
// unique TypeFunction.
|
|
return purity;
|
|
}
|
|
|
|
enum PURE FuncDeclaration::isPureBypassingInference()
|
|
{
|
|
if (flags & FUNCFLAGpurityInprocess)
|
|
return PUREfwdref;
|
|
else
|
|
return isPure();
|
|
}
|
|
|
|
/**************************************
|
|
* The function is doing something impure,
|
|
* so mark it as impure.
|
|
* If there's a purity error, return TRUE.
|
|
*/
|
|
bool FuncDeclaration::setImpure()
|
|
{
|
|
if (flags & FUNCFLAGpurityInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGpurityInprocess;
|
|
}
|
|
else if (isPure())
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
int FuncDeclaration::isSafe()
|
|
{
|
|
assert(type->ty == Tfunction);
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
setUnsafe();
|
|
return ((TypeFunction *)type)->trust == TRUSTsafe;
|
|
}
|
|
|
|
bool FuncDeclaration::isSafeBypassingInference()
|
|
{
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
return false;
|
|
else
|
|
return isSafe();
|
|
}
|
|
|
|
int FuncDeclaration::isTrusted()
|
|
{
|
|
assert(type->ty == Tfunction);
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
setUnsafe();
|
|
return ((TypeFunction *)type)->trust == TRUSTtrusted;
|
|
}
|
|
|
|
/**************************************
|
|
* The function is doing something unsave,
|
|
* so mark it as unsafe.
|
|
* If there's a safe error, return TRUE.
|
|
*/
|
|
bool FuncDeclaration::setUnsafe()
|
|
{
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGsafetyInprocess;
|
|
((TypeFunction *)type)->trust = TRUSTsystem;
|
|
}
|
|
else if (isSafe())
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/**************************************
|
|
* Returns an indirect type one step from t.
|
|
*/
|
|
|
|
Type *getIndirection(Type *t)
|
|
{
|
|
t = t->toBasetype();
|
|
|
|
if (t->ty == Tsarray)
|
|
{ while (t->ty == Tsarray)
|
|
t = t->nextOf()->toBasetype();
|
|
}
|
|
if (t->ty == Tarray || t->ty == Tpointer)
|
|
return t->nextOf()->toBasetype();
|
|
if (t->ty == Taarray || t->ty == Tclass)
|
|
return t;
|
|
if (t->ty == Tstruct)
|
|
return t->hasPointers() ? t : NULL; // TODO
|
|
|
|
// should consider TypeDelegate?
|
|
return NULL;
|
|
}
|
|
|
|
/**************************************
|
|
* Traverse this and t, and then check the indirections convertibility.
|
|
*/
|
|
|
|
int traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool a2b = true)
|
|
{
|
|
if (a2b) // check ta appears in tb
|
|
{
|
|
//printf("\ttraverse(1) %s appears in %s\n", ta->toChars(), tb->toChars());
|
|
if (ta->constConv(tb))
|
|
return 1;
|
|
else if (ta->invariantOf()->equals(tb->invariantOf()))
|
|
return 0;
|
|
else if (tb->ty == Tvoid && MODimplicitConv(ta->mod, tb->mod))
|
|
return 1;
|
|
}
|
|
else // check tb appears in ta
|
|
{
|
|
//printf("\ttraverse(2) %s appears in %s\n", tb->toChars(), ta->toChars());
|
|
if (tb->constConv(ta))
|
|
return 1;
|
|
else if (tb->invariantOf()->equals(ta->invariantOf()))
|
|
return 0;
|
|
else if (ta->ty == Tvoid && MODimplicitConv(tb->mod, ta->mod))
|
|
return 1;
|
|
}
|
|
|
|
// context date to detect circular look up
|
|
struct Ctxt
|
|
{
|
|
Ctxt *prev;
|
|
Type *type;
|
|
};
|
|
Ctxt *ctxt = (Ctxt *)p;
|
|
|
|
Type *tbb = tb->toBasetype();
|
|
if (tbb != tb)
|
|
return traverseIndirections(ta, tbb, ctxt, a2b);
|
|
|
|
if (tb->ty == Tsarray)
|
|
{ while (tb->toBasetype()->ty == Tsarray)
|
|
tb = tb->toBasetype()->nextOf();
|
|
}
|
|
if (tb->ty == Tclass || tb->ty == Tstruct)
|
|
{
|
|
for (Ctxt *c = ctxt; c; c = c->prev)
|
|
if (tb == c->type) return 0;
|
|
Ctxt c;
|
|
c.prev = ctxt;
|
|
c.type = tb;
|
|
|
|
AggregateDeclaration *sym = tb->toDsymbol(NULL)->isAggregateDeclaration();
|
|
for (size_t i = 0; i < sym->fields.dim; i++)
|
|
{
|
|
VarDeclaration *v = sym->fields[i];
|
|
Type *tprmi = v->type->addMod(tb->mod);
|
|
if (!(v->storage_class & STCref))
|
|
tprmi = getIndirection(tprmi);
|
|
if (!tprmi)
|
|
continue;
|
|
|
|
//printf("\ttb = %s, tprmi = %s\n", tb->toChars(), tprmi->toChars());
|
|
if (traverseIndirections(ta, tprmi, &c, a2b))
|
|
return 1;
|
|
}
|
|
}
|
|
else if (tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tpointer)
|
|
{
|
|
Type *tind = tb->nextOf();
|
|
if (traverseIndirections(ta, tind, ctxt, a2b))
|
|
return 1;
|
|
}
|
|
else if (tb->hasPointers())
|
|
{
|
|
// FIXME: function pointer/delegate types should be considered.
|
|
return 1;
|
|
}
|
|
if (a2b)
|
|
return traverseIndirections(tb, ta, ctxt, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/********************************************
|
|
* Returns true if the function return value has no indirection
|
|
* which comes from the parameters.
|
|
*/
|
|
|
|
bool FuncDeclaration::isolateReturn()
|
|
{
|
|
assert(type->ty == Tfunction);
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
assert(tf->next);
|
|
|
|
Type *treti = tf->next;
|
|
treti = tf->isref ? treti : getIndirection(treti);
|
|
if (!treti)
|
|
return true; // target has no mutable indirection
|
|
return parametersIntersect(treti);
|
|
}
|
|
|
|
/********************************************
|
|
* Returns true if an object typed t can have indirections
|
|
* which come from the parameters.
|
|
*/
|
|
|
|
bool FuncDeclaration::parametersIntersect(Type *t)
|
|
{
|
|
assert(t);
|
|
if (!isPureBypassingInference() || isNested())
|
|
return false;
|
|
|
|
assert(type->ty == Tfunction);
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
|
|
//printf("parametersIntersect(%s) t = %s\n", tf->toChars(), t->toChars());
|
|
|
|
size_t dim = Parameter::dim(tf->parameters);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
Parameter *fparam = Parameter::getNth(tf->parameters, i);
|
|
if (!fparam->type)
|
|
continue;
|
|
Type *tprmi = (fparam->storageClass & (STClazy | STCout | STCref))
|
|
? fparam->type : getIndirection(fparam->type);
|
|
if (!tprmi)
|
|
continue; // there is no mutable indirection
|
|
|
|
//printf("\t[%d] tprmi = %d %s\n", i, tprmi->ty, tprmi->toChars());
|
|
if (traverseIndirections(tprmi, t))
|
|
return false;
|
|
}
|
|
if (AggregateDeclaration *ad = isCtorDeclaration() ? NULL : isThis())
|
|
{
|
|
Type *tthis = ad ? ad->getType()->addMod(tf->mod) : NULL;
|
|
//printf("\ttthis = %s\n", tthis->toChars());
|
|
if (traverseIndirections(tthis, t))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Determine if function needs
|
|
// a static frame pointer to its lexically enclosing function
|
|
|
|
int FuncDeclaration::isNested()
|
|
{
|
|
FuncDeclaration *f = toAliasFunc();
|
|
//printf("\ttoParent2() = '%s'\n", f->toParent2()->toChars());
|
|
return ((f->storage_class & STCstatic) == 0) &&
|
|
(f->linkage == LINKd) &&
|
|
(f->toParent2()->isFuncDeclaration() != NULL);
|
|
}
|
|
|
|
int FuncDeclaration::needThis()
|
|
{
|
|
//printf("FuncDeclaration::needThis() '%s'\n", toChars());
|
|
return toAliasFunc()->isThis() != NULL;
|
|
}
|
|
|
|
int FuncDeclaration::addPreInvariant()
|
|
{
|
|
AggregateDeclaration *ad = isThis();
|
|
return (ad &&
|
|
//ad->isClassDeclaration() &&
|
|
global.params.useInvariants &&
|
|
(protection == PROTprotected || protection == PROTpublic || protection == PROTexport) &&
|
|
!naked &&
|
|
ident != Id::cpctor);
|
|
}
|
|
|
|
int FuncDeclaration::addPostInvariant()
|
|
{
|
|
AggregateDeclaration *ad = isThis();
|
|
return (ad &&
|
|
ad->inv &&
|
|
//ad->isClassDeclaration() &&
|
|
global.params.useInvariants &&
|
|
(protection == PROTprotected || protection == PROTpublic || protection == PROTexport) &&
|
|
!naked &&
|
|
ident != Id::cpctor);
|
|
}
|
|
|
|
/**********************************
|
|
* Generate a FuncDeclaration for a runtime library function.
|
|
*/
|
|
|
|
//
|
|
// LDC: Adjusted to give argument info to the runtime function decl.
|
|
//
|
|
|
|
FuncDeclaration *FuncDeclaration::genCfunc(Parameters *args, Type *treturn, const char *name)
|
|
{
|
|
return genCfunc(args, treturn, Lexer::idPool(name));
|
|
}
|
|
|
|
FuncDeclaration *FuncDeclaration::genCfunc(Parameters *args, Type *treturn, Identifier *id)
|
|
{
|
|
FuncDeclaration *fd;
|
|
TypeFunction *tf;
|
|
Dsymbol *s;
|
|
static DsymbolTable *st = NULL;
|
|
|
|
//printf("genCfunc(name = '%s')\n", id->toChars());
|
|
//printf("treturn\n\t"); treturn->print();
|
|
|
|
// See if already in table
|
|
if (!st)
|
|
st = new DsymbolTable();
|
|
s = st->lookup(id);
|
|
if (s)
|
|
{
|
|
fd = s->isFuncDeclaration();
|
|
assert(fd);
|
|
assert(fd->type->nextOf()->equals(treturn));
|
|
}
|
|
else
|
|
{
|
|
#if IN_LLVM
|
|
tf = new TypeFunction(args, treturn, 0, LINKc);
|
|
#else
|
|
tf = new TypeFunction(NULL, treturn, 0, LINKc);
|
|
#endif
|
|
fd = new FuncDeclaration(Loc(), Loc(), id, STCstatic, tf);
|
|
fd->protection = PROTpublic;
|
|
fd->linkage = LINKc;
|
|
|
|
st->insert(fd);
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
const char *FuncDeclaration::kind()
|
|
{
|
|
return "function";
|
|
}
|
|
|
|
/*********************************************
|
|
* In the current function, we are calling 'this' function.
|
|
* 1. Check to see if the current function can call 'this' function, issue error if not.
|
|
* 2. If the current function is not the parent of 'this' function, then add
|
|
* the current function to the list of siblings of 'this' function.
|
|
* 3. If the current function is a literal, and it's accessing an uplevel scope,
|
|
* then mark it as a delegate.
|
|
*/
|
|
|
|
void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
|
|
{
|
|
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
|
|
if (parent && parent != sc->parent && this->isNested() &&
|
|
this->ident != Id::require && this->ident != Id::ensure)
|
|
{
|
|
// The function that this function is in
|
|
FuncDeclaration *fdv2 = toParent2()->isFuncDeclaration();
|
|
|
|
// The current function
|
|
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
|
|
|
|
//printf("this = %s in [%s]\n", this->toChars(), this->loc.toChars());
|
|
//printf("fdv = %s in [%s]\n", fdv->toChars(), fdv->loc.toChars());
|
|
//printf("fdthis = %s in [%s]\n", fdthis->toChars(), fdthis->loc.toChars());
|
|
|
|
if (fdv2 && fdthis && fdv2 != fdthis)
|
|
{
|
|
// Add this function to the list of those which called us
|
|
if (fdthis != this)
|
|
{
|
|
bool found = false;
|
|
for (int i = 0; i < siblingCallers.dim; ++i)
|
|
{ if (siblingCallers[i] == fdthis)
|
|
found = true;
|
|
}
|
|
if (!found)
|
|
{
|
|
//printf("\tadding sibling %s\n", fdthis->toPrettyChars());
|
|
siblingCallers.push(fdthis);
|
|
}
|
|
}
|
|
}
|
|
|
|
FuncDeclaration *fdv = toParent()->isFuncDeclaration();
|
|
fdv = toParent()->isFuncDeclaration();
|
|
if (fdv && fdthis && fdv != fdthis)
|
|
{
|
|
int lv = fdthis->getLevel(loc, sc, fdv);
|
|
if (lv == -1)
|
|
return; // downlevel call
|
|
if (lv == 0)
|
|
return; // same level call
|
|
|
|
// Uplevel call
|
|
|
|
// BUG: may need to walk up outer scopes like Declaration::checkNestedReference() does
|
|
|
|
// function literal has reference to enclosing scope is delegate
|
|
if (FuncLiteralDeclaration *fld = fdthis->isFuncLiteralDeclaration())
|
|
fld->tok = TOKdelegate;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* For all functions between outerFunc and f, mark them as needing
|
|
* a closure.
|
|
*/
|
|
void markAsNeedingClosure(Dsymbol *f, FuncDeclaration *outerFunc)
|
|
{
|
|
for (Dsymbol *sx = f; sx && sx != outerFunc; sx = sx->parent)
|
|
{
|
|
FuncDeclaration *fy = sx->isFuncDeclaration();
|
|
if (fy && fy->closureVars.dim)
|
|
{
|
|
/* fy needs a closure if it has closureVars[],
|
|
* because the frame pointer in the closure will be accessed.
|
|
*/
|
|
fy->requiresClosure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Given a nested function f inside a function outerFunc, check
|
|
* if any sibling callers of f have escaped. If so, mark
|
|
* all the enclosing functions as needing closures.
|
|
* Return true if any closures were detected.
|
|
* This is recursive: we need to check the callers of our siblings.
|
|
* Note that nested functions can only call lexically earlier nested
|
|
* functions, so loops are impossible.
|
|
*/
|
|
bool checkEscapingSiblings(FuncDeclaration *f, FuncDeclaration *outerFunc)
|
|
{
|
|
//printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f->toChars(), outerFunc->toChars());
|
|
bool bAnyClosures = false;
|
|
for (int i = 0; i < f->siblingCallers.dim; ++i)
|
|
{
|
|
FuncDeclaration *g = f->siblingCallers[i];
|
|
if (g->isThis() || g->tookAddressOf)
|
|
{
|
|
markAsNeedingClosure(g, outerFunc);
|
|
bAnyClosures = true;
|
|
}
|
|
bAnyClosures |= checkEscapingSiblings(g, outerFunc);
|
|
}
|
|
//printf("\t%d\n", bAnyClosures);
|
|
return bAnyClosures;
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* Look at all the variables in this function that are referenced
|
|
* by nested functions, and determine if a closure needs to be
|
|
* created for them.
|
|
*/
|
|
|
|
#if DMDV2
|
|
int FuncDeclaration::needsClosure()
|
|
{
|
|
/* Need a closure for all the closureVars[] if any of the
|
|
* closureVars[] are accessed by a
|
|
* function that escapes the scope of this function.
|
|
* We take the conservative approach and decide that a function needs
|
|
* a closure if it:
|
|
* 1) is a virtual function
|
|
* 2) has its address taken
|
|
* 3) has a parent that escapes
|
|
* 4) calls another nested function that needs a closure
|
|
* -or-
|
|
* 5) this function returns a local struct/class
|
|
*
|
|
* Note that since a non-virtual function can be called by
|
|
* a virtual one, if that non-virtual function accesses a closure
|
|
* var, the closure still has to be taken. Hence, we check for isThis()
|
|
* instead of isVirtual(). (thanks to David Friedman)
|
|
*/
|
|
|
|
//printf("FuncDeclaration::needsClosure() %s\n", toChars());
|
|
|
|
if (requiresClosure)
|
|
goto Lyes;
|
|
|
|
for (size_t i = 0; i < closureVars.dim; i++)
|
|
{ VarDeclaration *v = closureVars[i];
|
|
assert(v->isVarDeclaration());
|
|
//printf("\tv = %s\n", v->toChars());
|
|
|
|
for (size_t j = 0; j < v->nestedrefs.dim; j++)
|
|
{ FuncDeclaration *f = v->nestedrefs[j];
|
|
assert(f != this);
|
|
|
|
//printf("\t\tf = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf);
|
|
|
|
/* Look to see if f escapes. We consider all parents of f within
|
|
* this, and also all siblings which call f; if any of them escape,
|
|
* so does f.
|
|
* Mark all affected functions as requiring closures.
|
|
*/
|
|
for (Dsymbol *s = f; s && s != this; s = s->parent)
|
|
{
|
|
FuncDeclaration *fx = s->isFuncDeclaration();
|
|
if (fx && (fx->isThis() || fx->tookAddressOf))
|
|
{
|
|
//printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx->toChars(), fx->isVirtual(), fx->isThis(), fx->tookAddressOf);
|
|
|
|
/* Mark as needing closure any functions between this and f
|
|
*/
|
|
markAsNeedingClosure( (fx == f) ? fx->parent : fx, this);
|
|
|
|
goto Lyes;
|
|
}
|
|
|
|
/* We also need to check if any sibling functions that
|
|
* called us, have escaped. This is recursive: we need
|
|
* to check the callers of our siblings.
|
|
*/
|
|
if (fx && checkEscapingSiblings(fx, this))
|
|
goto Lyes;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Look for case (5)
|
|
*/
|
|
if (closureVars.dim)
|
|
{
|
|
assert(type->ty == Tfunction);
|
|
Type *tret = ((TypeFunction *)type)->next;
|
|
assert(tret);
|
|
tret = tret->toBasetype();
|
|
//printf("\t\treturning %s\n", tret->toChars());
|
|
if (tret->ty == Tclass || tret->ty == Tstruct)
|
|
{ Dsymbol *st = tret->toDsymbol(NULL);
|
|
//printf("\t\treturning class/struct %s\n", tret->toChars());
|
|
for (Dsymbol *s = st->parent; s; s = s->parent)
|
|
{
|
|
//printf("\t\t\tparent = %s %s\n", s->kind(), s->toChars());
|
|
if (s == this)
|
|
{ //printf("\t\treturning local %s\n", st->toChars());
|
|
goto Lyes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
Lyes:
|
|
//printf("\tneeds closure\n");
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/***********************************************
|
|
* Determine if function's variables are referenced by a function
|
|
* nested within it.
|
|
*/
|
|
|
|
int FuncDeclaration::hasNestedFrameRefs()
|
|
{
|
|
#if DMDV2
|
|
if (closureVars.dim)
|
|
#else
|
|
if (nestedFrameRef)
|
|
#endif
|
|
return 1;
|
|
|
|
/* If a virtual method has contracts, assume its variables are referenced
|
|
* by those contracts, even if they aren't. Because they might be referenced
|
|
* by the overridden or overriding function's contracts.
|
|
* This can happen because frequire and fensure are implemented as nested functions,
|
|
* and they can be called directly by an overriding function and the overriding function's
|
|
* context had better match, or Bugzilla 7337 will bite.
|
|
*/
|
|
if ((fdrequire || fdensure) && isVirtualMethod())
|
|
return 1;
|
|
|
|
if (foverrides.dim && isVirtualMethod())
|
|
{
|
|
for (size_t i = 0; i < foverrides.dim; i++)
|
|
{
|
|
FuncDeclaration *fdv = foverrides[i];
|
|
if (fdv->hasNestedFrameRefs())
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************
|
|
* Return the function's parameter list, and whether
|
|
* it is variadic or not.
|
|
*/
|
|
|
|
Parameters *FuncDeclaration::getParameters(int *pvarargs)
|
|
{ Parameters *fparameters;
|
|
int fvarargs;
|
|
|
|
if (type)
|
|
{
|
|
assert(type->ty == Tfunction);
|
|
TypeFunction *fdtype = (TypeFunction *)type;
|
|
fparameters = fdtype->parameters;
|
|
fvarargs = fdtype->varargs;
|
|
}
|
|
if (pvarargs)
|
|
*pvarargs = fvarargs;
|
|
return fparameters;
|
|
}
|
|
|
|
|
|
/****************************** FuncAliasDeclaration ************************/
|
|
|
|
// Used as a way to import a set of functions from another scope into this one.
|
|
|
|
FuncAliasDeclaration::FuncAliasDeclaration(FuncDeclaration *funcalias, int hasOverloads)
|
|
: FuncDeclaration(funcalias->loc, funcalias->endloc, funcalias->ident,
|
|
funcalias->storage_class, funcalias->type)
|
|
{
|
|
assert(funcalias != this);
|
|
this->funcalias = funcalias;
|
|
|
|
this->hasOverloads = hasOverloads;
|
|
if (hasOverloads)
|
|
{
|
|
if (FuncAliasDeclaration *fad = funcalias->isFuncAliasDeclaration())
|
|
this->hasOverloads = fad->hasOverloads;
|
|
}
|
|
else
|
|
{ // for internal use
|
|
assert(!funcalias->isFuncAliasDeclaration());
|
|
this->hasOverloads = 0;
|
|
}
|
|
userAttributes = funcalias->userAttributes;
|
|
}
|
|
|
|
const char *FuncAliasDeclaration::kind()
|
|
{
|
|
return "function alias";
|
|
}
|
|
|
|
FuncDeclaration *FuncAliasDeclaration::toAliasFunc()
|
|
{
|
|
return funcalias->toAliasFunc();
|
|
}
|
|
|
|
|
|
/****************************** FuncLiteralDeclaration ************************/
|
|
|
|
FuncLiteralDeclaration::FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type,
|
|
enum TOK tok, ForeachStatement *fes)
|
|
: FuncDeclaration(loc, endloc, NULL, STCundefined, type)
|
|
{
|
|
const char *id;
|
|
|
|
if (fes)
|
|
id = "__foreachbody";
|
|
else if (tok == TOKreserved)
|
|
id = "__lambda";
|
|
else if (tok == TOKdelegate)
|
|
id = "__dgliteral";
|
|
else
|
|
id = "__funcliteral";
|
|
this->ident = Lexer::uniqueId(id);
|
|
this->tok = tok;
|
|
this->fes = fes;
|
|
this->treq = NULL;
|
|
//printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this->ident->toChars(), type->toChars());
|
|
|
|
#if IN_LLVM
|
|
this->owningTemplate = NULL;
|
|
#endif
|
|
}
|
|
|
|
Dsymbol *FuncLiteralDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
FuncLiteralDeclaration *f;
|
|
|
|
//printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars());
|
|
if (s)
|
|
f = (FuncLiteralDeclaration *)s;
|
|
else
|
|
{ f = new FuncLiteralDeclaration(loc, endloc, type->syntaxCopy(), tok, fes);
|
|
f->ident = ident; // keep old identifier
|
|
f->treq = treq; // don't need to copy
|
|
}
|
|
FuncDeclaration::syntaxCopy(f);
|
|
return f;
|
|
}
|
|
|
|
int FuncLiteralDeclaration::isNested()
|
|
{
|
|
//printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars());
|
|
return (tok != TOKfunction);
|
|
}
|
|
|
|
int FuncLiteralDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
const char *FuncLiteralDeclaration::kind()
|
|
{
|
|
// GCC requires the (char*) casts
|
|
return (tok != TOKfunction) ? (char*)"delegate" : (char*)"function";
|
|
}
|
|
|
|
void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (tok != TOKreserved)
|
|
{
|
|
buf->writestring(kind());
|
|
buf->writeByte(' ');
|
|
}
|
|
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
// Don't print tf->mod, tf->trust, and tf->linkage
|
|
if (tf->next)
|
|
tf->next->toCBuffer2(buf, hgs, 0);
|
|
Parameter::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs);
|
|
|
|
ReturnStatement *ret = !fbody->isCompoundStatement() ?
|
|
fbody->isReturnStatement() : NULL;
|
|
if (ret && ret->exp)
|
|
{
|
|
buf->writestring(" => ");
|
|
ret->exp->toCBuffer(buf, hgs);
|
|
}
|
|
else
|
|
{
|
|
hgs->tpltMember++;
|
|
bodyToCBuffer(buf, hgs);
|
|
hgs->tpltMember--;
|
|
}
|
|
}
|
|
|
|
|
|
/********************************* CtorDeclaration ****************************/
|
|
|
|
CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type)
|
|
: FuncDeclaration(loc, endloc, Id::ctor, stc, type)
|
|
{
|
|
//printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars());
|
|
}
|
|
|
|
Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
CtorDeclaration *f = new CtorDeclaration(loc, endloc, storage_class, type->syntaxCopy());
|
|
|
|
f->outId = outId;
|
|
f->frequire = frequire ? frequire->syntaxCopy() : NULL;
|
|
f->fensure = fensure ? fensure->syntaxCopy() : NULL;
|
|
f->fbody = fbody ? fbody->syntaxCopy() : NULL;
|
|
assert(!fthrows); // deprecated
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
void CtorDeclaration::semantic(Scope *sc)
|
|
{
|
|
//printf("CtorDeclaration::semantic() %s\n", toChars());
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
assert(tf && tf->ty == Tfunction);
|
|
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
|
|
sc = sc->push();
|
|
sc->stc &= ~STCstatic; // not a static constructor
|
|
sc->flags |= SCOPEctor;
|
|
|
|
FuncDeclaration::semantic(sc);
|
|
|
|
sc->pop();
|
|
|
|
Dsymbol *parent = toParent2();
|
|
AggregateDeclaration *ad = parent->isAggregateDeclaration();
|
|
|
|
/* See if it's the default constructor
|
|
* But, template constructor should not become a default constructor.
|
|
*/
|
|
if (ad && tf->varargs == 0 && Parameter::dim(tf->parameters) == 0
|
|
&& (!this->parent->isTemplateInstance() || this->parent->isTemplateMixin()))
|
|
{
|
|
StructDeclaration *sd = ad->isStructDeclaration();
|
|
if (sd)
|
|
{
|
|
if (fbody || !(storage_class & STCdisable))
|
|
{ error("default constructor for structs only allowed with @disable and no body");
|
|
storage_class |= STCdisable;
|
|
fbody = NULL;
|
|
}
|
|
sd->noDefaultCtor = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ad->defaultCtor = this;
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *CtorDeclaration::kind()
|
|
{
|
|
return "constructor";
|
|
}
|
|
|
|
char *CtorDeclaration::toChars()
|
|
{
|
|
return (char *)"this";
|
|
}
|
|
|
|
int CtorDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int CtorDeclaration::addPreInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int CtorDeclaration::addPostInvariant()
|
|
{
|
|
return (isThis() && vthis && global.params.useInvariants);
|
|
}
|
|
|
|
|
|
/********************************* PostBlitDeclaration ****************************/
|
|
|
|
#if DMDV2
|
|
PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
|
|
: FuncDeclaration(loc, endloc, id, stc, NULL)
|
|
{
|
|
}
|
|
|
|
Dsymbol *PostBlitDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
assert(!s);
|
|
PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, storage_class, ident);
|
|
return FuncDeclaration::syntaxCopy(dd);
|
|
}
|
|
|
|
|
|
void PostBlitDeclaration::semantic(Scope *sc)
|
|
{
|
|
//printf("PostBlitDeclaration::semantic() %s\n", toChars());
|
|
//printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor);
|
|
//printf("stc = x%llx\n", sc->stc);
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
parent = sc->parent;
|
|
Dsymbol *parent = toParent();
|
|
StructDeclaration *ad = parent->isStructDeclaration();
|
|
if (!ad)
|
|
{
|
|
error("post blits are only for struct/union definitions, not %s %s", parent->kind(), parent->toChars());
|
|
}
|
|
else if (ident == Id::_postblit && semanticRun < PASSsemantic)
|
|
ad->postblits.push(this);
|
|
|
|
if (!type)
|
|
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd, storage_class);
|
|
|
|
sc = sc->push();
|
|
sc->stc &= ~STCstatic; // not static
|
|
sc->linkage = LINKd;
|
|
|
|
FuncDeclaration::semantic(sc);
|
|
|
|
sc->pop();
|
|
}
|
|
|
|
int PostBlitDeclaration::overloadInsert(Dsymbol *s)
|
|
{
|
|
return FALSE; // cannot overload postblits
|
|
}
|
|
|
|
int PostBlitDeclaration::addPreInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int PostBlitDeclaration::addPostInvariant()
|
|
{
|
|
return (isThis() && vthis && global.params.useInvariants);
|
|
}
|
|
|
|
int PostBlitDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void PostBlitDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("this(this)");
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
#endif
|
|
|
|
/********************************* DtorDeclaration ****************************/
|
|
|
|
DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc)
|
|
: FuncDeclaration(loc, endloc, Id::dtor, STCundefined, NULL)
|
|
{
|
|
}
|
|
|
|
DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
|
|
: FuncDeclaration(loc, endloc, id, stc, NULL)
|
|
{
|
|
}
|
|
|
|
Dsymbol *DtorDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
assert(!s);
|
|
DtorDeclaration *dd = new DtorDeclaration(loc, endloc, storage_class, ident);
|
|
return FuncDeclaration::syntaxCopy(dd);
|
|
}
|
|
|
|
|
|
void DtorDeclaration::semantic(Scope *sc)
|
|
{
|
|
//printf("DtorDeclaration::semantic() %s\n", toChars());
|
|
//printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor);
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
parent = sc->parent;
|
|
Dsymbol *parent = toParent();
|
|
AggregateDeclaration *ad = parent->isAggregateDeclaration();
|
|
if (!ad)
|
|
{
|
|
error("destructors are only for class/struct/union definitions, not %s %s", parent->kind(), parent->toChars());
|
|
fatal();
|
|
}
|
|
else if (ident == Id::dtor && semanticRun < PASSsemantic)
|
|
ad->dtors.push(this);
|
|
|
|
if (!type)
|
|
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd, storage_class);
|
|
|
|
sc = sc->push();
|
|
sc->stc &= ~STCstatic; // not a static destructor
|
|
sc->linkage = LINKd;
|
|
|
|
FuncDeclaration::semantic(sc);
|
|
|
|
sc->pop();
|
|
}
|
|
|
|
int DtorDeclaration::overloadInsert(Dsymbol *s)
|
|
{
|
|
return FALSE; // cannot overload destructors
|
|
}
|
|
|
|
int DtorDeclaration::addPreInvariant()
|
|
{
|
|
return (isThis() && vthis && global.params.useInvariants);
|
|
}
|
|
|
|
int DtorDeclaration::addPostInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
const char *DtorDeclaration::kind()
|
|
{
|
|
return "destructor";
|
|
}
|
|
|
|
char *DtorDeclaration::toChars()
|
|
{
|
|
return (char *)"~this";
|
|
}
|
|
|
|
int DtorDeclaration::isVirtual()
|
|
{
|
|
// FALSE so that dtor's don't get put into the vtbl[]
|
|
return FALSE;
|
|
}
|
|
|
|
void DtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("~this()");
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
|
|
/********************************* StaticCtorDeclaration ****************************/
|
|
|
|
StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc)
|
|
: FuncDeclaration(loc, endloc,
|
|
Identifier::generateId("_staticCtor"), STCstatic, NULL)
|
|
{
|
|
}
|
|
|
|
StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, const char *name)
|
|
: FuncDeclaration(loc, endloc,
|
|
Identifier::generateId(name), STCstatic, NULL)
|
|
{
|
|
}
|
|
|
|
Dsymbol *StaticCtorDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
assert(!s);
|
|
StaticCtorDeclaration *scd = new StaticCtorDeclaration(loc, endloc);
|
|
return FuncDeclaration::syntaxCopy(scd);
|
|
}
|
|
|
|
|
|
void StaticCtorDeclaration::semantic(Scope *sc)
|
|
{
|
|
//printf("StaticCtorDeclaration::semantic()\n");
|
|
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
|
|
if (!type)
|
|
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd);
|
|
|
|
/* If the static ctor appears within a template instantiation,
|
|
* it could get called multiple times by the module constructors
|
|
* for different modules. Thus, protect it with a gate.
|
|
*/
|
|
if (inTemplateInstance() && semanticRun < PASSsemantic)
|
|
{
|
|
/* Add this prefix to the function:
|
|
* static int gate;
|
|
* if (++gate != 1) return;
|
|
* Note that this is not thread safe; should not have threads
|
|
* during static construction.
|
|
*/
|
|
Identifier *id = Lexer::idPool("__gate");
|
|
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL);
|
|
v->storage_class = isSharedStaticCtorDeclaration() ? STCstatic : STCtls;
|
|
Statements *sa = new Statements();
|
|
Statement *s = new ExpStatement(Loc(), v);
|
|
sa->push(s);
|
|
Expression *e = new IdentifierExp(Loc(), id);
|
|
e = new AddAssignExp(Loc(), e, new IntegerExp(1));
|
|
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(1));
|
|
s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL);
|
|
sa->push(s);
|
|
if (fbody)
|
|
sa->push(fbody);
|
|
fbody = new CompoundStatement(Loc(), sa);
|
|
}
|
|
|
|
FuncDeclaration::semantic(sc);
|
|
|
|
// We're going to need ModuleInfo
|
|
Module *m = getModule();
|
|
if (!m)
|
|
m = sc->module;
|
|
if (m)
|
|
{ m->needmoduleinfo = 1;
|
|
//printf("module1 %s needs moduleinfo\n", m->toChars());
|
|
}
|
|
}
|
|
|
|
AggregateDeclaration *StaticCtorDeclaration::isThis()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int StaticCtorDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
bool StaticCtorDeclaration::hasStaticCtorOrDtor()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int StaticCtorDeclaration::addPreInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int StaticCtorDeclaration::addPostInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void StaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (hgs->hdrgen && !hgs->tpltMember)
|
|
{
|
|
buf->writestring("static this();");
|
|
buf->writenl();
|
|
return;
|
|
}
|
|
buf->writestring("static this()");
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
|
|
/********************************* SharedStaticCtorDeclaration ****************************/
|
|
|
|
SharedStaticCtorDeclaration::SharedStaticCtorDeclaration(Loc loc, Loc endloc)
|
|
: StaticCtorDeclaration(loc, endloc, "_sharedStaticCtor")
|
|
{
|
|
}
|
|
|
|
Dsymbol *SharedStaticCtorDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
assert(!s);
|
|
SharedStaticCtorDeclaration *scd = new SharedStaticCtorDeclaration(loc, endloc);
|
|
return FuncDeclaration::syntaxCopy(scd);
|
|
}
|
|
|
|
void SharedStaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("shared ");
|
|
StaticCtorDeclaration::toCBuffer(buf, hgs);
|
|
}
|
|
|
|
/********************************* StaticDtorDeclaration ****************************/
|
|
|
|
StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc)
|
|
: FuncDeclaration(loc, endloc,
|
|
Identifier::generateId("_staticDtor"), STCstatic, NULL)
|
|
{
|
|
vgate = NULL;
|
|
}
|
|
|
|
StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc, const char *name)
|
|
: FuncDeclaration(loc, endloc,
|
|
Identifier::generateId(name), STCstatic, NULL)
|
|
{
|
|
vgate = NULL;
|
|
}
|
|
|
|
Dsymbol *StaticDtorDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
assert(!s);
|
|
StaticDtorDeclaration *sdd = new StaticDtorDeclaration(loc, endloc);
|
|
return FuncDeclaration::syntaxCopy(sdd);
|
|
}
|
|
|
|
|
|
void StaticDtorDeclaration::semantic(Scope *sc)
|
|
{
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
|
|
ClassDeclaration *cd = sc->scopesym->isClassDeclaration();
|
|
|
|
if (!type)
|
|
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd);
|
|
|
|
/* If the static ctor appears within a template instantiation,
|
|
* it could get called multiple times by the module constructors
|
|
* for different modules. Thus, protect it with a gate.
|
|
*/
|
|
if (inTemplateInstance() && semanticRun < PASSsemantic)
|
|
{
|
|
/* Add this prefix to the function:
|
|
* static int gate;
|
|
* if (--gate != 0) return;
|
|
* Increment gate during constructor execution.
|
|
* Note that this is not thread safe; should not have threads
|
|
* during static destruction.
|
|
*/
|
|
Identifier *id = Lexer::idPool("__gate");
|
|
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL);
|
|
v->storage_class = isSharedStaticDtorDeclaration() ? STCstatic : STCtls;
|
|
Statements *sa = new Statements();
|
|
Statement *s = new ExpStatement(Loc(), v);
|
|
sa->push(s);
|
|
Expression *e = new IdentifierExp(Loc(), id);
|
|
e = new AddAssignExp(Loc(), e, new IntegerExp(-1)); // LDC_FIXME: Previously had (uint64_t)-1, double-check this.
|
|
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0));
|
|
s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL);
|
|
sa->push(s);
|
|
if (fbody)
|
|
sa->push(fbody);
|
|
fbody = new CompoundStatement(Loc(), sa);
|
|
vgate = v;
|
|
}
|
|
|
|
FuncDeclaration::semantic(sc);
|
|
|
|
// We're going to need ModuleInfo
|
|
Module *m = getModule();
|
|
if (!m)
|
|
m = sc->module;
|
|
if (m)
|
|
{ m->needmoduleinfo = 1;
|
|
//printf("module2 %s needs moduleinfo\n", m->toChars());
|
|
}
|
|
}
|
|
|
|
AggregateDeclaration *StaticDtorDeclaration::isThis()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int StaticDtorDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
bool StaticDtorDeclaration::hasStaticCtorOrDtor()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int StaticDtorDeclaration::addPreInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int StaticDtorDeclaration::addPostInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void StaticDtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (hgs->hdrgen)
|
|
return;
|
|
buf->writestring("static ~this()");
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
|
|
/********************************* SharedStaticDtorDeclaration ****************************/
|
|
|
|
SharedStaticDtorDeclaration::SharedStaticDtorDeclaration(Loc loc, Loc endloc)
|
|
: StaticDtorDeclaration(loc, endloc, "_sharedStaticDtor")
|
|
{
|
|
}
|
|
|
|
Dsymbol *SharedStaticDtorDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
assert(!s);
|
|
SharedStaticDtorDeclaration *sdd = new SharedStaticDtorDeclaration(loc, endloc);
|
|
return FuncDeclaration::syntaxCopy(sdd);
|
|
}
|
|
|
|
void SharedStaticDtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (!hgs->hdrgen)
|
|
{
|
|
buf->writestring("shared ");
|
|
StaticDtorDeclaration::toCBuffer(buf, hgs);
|
|
}
|
|
}
|
|
|
|
|
|
/********************************* InvariantDeclaration ****************************/
|
|
|
|
InvariantDeclaration::InvariantDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
|
|
: FuncDeclaration(loc, endloc,
|
|
id ? id : Identifier::generateId("__invariant"),
|
|
stc, NULL)
|
|
{
|
|
}
|
|
|
|
Dsymbol *InvariantDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
InvariantDeclaration *id;
|
|
|
|
assert(!s);
|
|
id = new InvariantDeclaration(loc, endloc, storage_class);
|
|
FuncDeclaration::syntaxCopy(id);
|
|
return id;
|
|
}
|
|
|
|
|
|
void InvariantDeclaration::semantic(Scope *sc)
|
|
{
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
parent = sc->parent;
|
|
Dsymbol *parent = toParent();
|
|
AggregateDeclaration *ad = parent->isAggregateDeclaration();
|
|
if (!ad)
|
|
{
|
|
error("invariants are only for struct/union/class definitions");
|
|
return;
|
|
}
|
|
if (ident != Id::classInvariant && semanticRun < PASSsemantic)
|
|
{
|
|
ad->invs.push(this);
|
|
}
|
|
if (!type)
|
|
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd, storage_class);
|
|
|
|
sc = sc->push();
|
|
sc->stc &= ~STCstatic; // not a static invariant
|
|
sc->stc |= STCconst; // invariant() is always const
|
|
sc->flags = (sc->flags & ~SCOPEcontract) | SCOPEinvariant;
|
|
sc->linkage = LINKd;
|
|
|
|
FuncDeclaration::semantic(sc);
|
|
|
|
sc->pop();
|
|
}
|
|
|
|
int InvariantDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int InvariantDeclaration::addPreInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int InvariantDeclaration::addPostInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void InvariantDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (hgs->hdrgen)
|
|
return;
|
|
buf->writestring("invariant");
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
|
|
|
|
/********************************* UnitTestDeclaration ****************************/
|
|
|
|
/*******************************
|
|
* Generate unique unittest function Id so we can have multiple
|
|
* instances per module.
|
|
*/
|
|
|
|
static Identifier *unitTestId(Loc loc)
|
|
{
|
|
char name[24];
|
|
#if __DMC__ || _MSC_VER
|
|
_snprintf(name, 24, "__unittestL%u_", loc.linnum);
|
|
#else
|
|
snprintf(name, 24, "__unittestL%u_", loc.linnum);
|
|
#endif
|
|
return Lexer::uniqueId(name);
|
|
}
|
|
|
|
UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc, char *codedoc)
|
|
: FuncDeclaration(loc, endloc, unitTestId(loc), STCundefined, NULL)
|
|
{
|
|
this->codedoc = codedoc;
|
|
}
|
|
|
|
Dsymbol *UnitTestDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
UnitTestDeclaration *utd;
|
|
|
|
assert(!s);
|
|
utd = new UnitTestDeclaration(loc, endloc, codedoc);
|
|
return FuncDeclaration::syntaxCopy(utd);
|
|
}
|
|
|
|
|
|
void UnitTestDeclaration::semantic(Scope *sc)
|
|
{
|
|
protection = sc->protection;
|
|
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
if (global.params.useUnitTests)
|
|
{
|
|
if (!type)
|
|
type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd);
|
|
Scope *sc2 = sc->push();
|
|
// It makes no sense for unit tests to be pure or nothrow.
|
|
sc2->stc &= ~(STCnothrow | STCpure);
|
|
sc2->linkage = LINKd;
|
|
FuncDeclaration::semantic(sc2);
|
|
sc2->pop();
|
|
}
|
|
|
|
#if 0
|
|
// We're going to need ModuleInfo even if the unit tests are not
|
|
// compiled in, because other modules may import this module and refer
|
|
// to this ModuleInfo.
|
|
// (This doesn't make sense to me?)
|
|
Module *m = getModule();
|
|
if (!m)
|
|
m = sc->module;
|
|
if (m)
|
|
{
|
|
//printf("module3 %s needs moduleinfo\n", m->toChars());
|
|
m->needmoduleinfo = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
AggregateDeclaration *UnitTestDeclaration::isThis()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int UnitTestDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int UnitTestDeclaration::addPreInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int UnitTestDeclaration::addPostInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void UnitTestDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (hgs->hdrgen)
|
|
return;
|
|
buf->writestring("unittest");
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
|
|
/********************************* NewDeclaration ****************************/
|
|
|
|
NewDeclaration::NewDeclaration(Loc loc, Loc endloc, Parameters *arguments, int varargs)
|
|
: FuncDeclaration(loc, endloc, Id::classNew, STCstatic, NULL)
|
|
{
|
|
this->arguments = arguments;
|
|
this->varargs = varargs;
|
|
}
|
|
|
|
Dsymbol *NewDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
NewDeclaration *f;
|
|
|
|
f = new NewDeclaration(loc, endloc, NULL, varargs);
|
|
|
|
FuncDeclaration::syntaxCopy(f);
|
|
|
|
f->arguments = Parameter::arraySyntaxCopy(arguments);
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
void NewDeclaration::semantic(Scope *sc)
|
|
{
|
|
//printf("NewDeclaration::semantic()\n");
|
|
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
|
|
parent = sc->parent;
|
|
Dsymbol *parent = toParent();
|
|
ClassDeclaration *cd = parent->isClassDeclaration();
|
|
if (!cd && !parent->isStructDeclaration())
|
|
{
|
|
error("new allocators only are for class or struct definitions");
|
|
}
|
|
Type *tret = Type::tvoid->pointerTo();
|
|
if (!type)
|
|
type = new TypeFunction(arguments, tret, varargs, LINKd);
|
|
|
|
type = type->semantic(loc, sc);
|
|
assert(type->ty == Tfunction);
|
|
|
|
// Check that there is at least one argument of type size_t
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
if (Parameter::dim(tf->parameters) < 1)
|
|
{
|
|
error("at least one argument of type size_t expected");
|
|
}
|
|
else
|
|
{
|
|
Parameter *a = Parameter::getNth(tf->parameters, 0);
|
|
if (!a->type->equals(Type::tsize_t))
|
|
error("first argument must be type size_t, not %s", a->type->toChars());
|
|
}
|
|
|
|
FuncDeclaration::semantic(sc);
|
|
}
|
|
|
|
const char *NewDeclaration::kind()
|
|
{
|
|
return "allocator";
|
|
}
|
|
|
|
int NewDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int NewDeclaration::addPreInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int NewDeclaration::addPostInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void NewDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("new");
|
|
Parameter::argsToCBuffer(buf, hgs, arguments, varargs);
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
|
|
|
|
/********************************* DeleteDeclaration ****************************/
|
|
|
|
DeleteDeclaration::DeleteDeclaration(Loc loc, Loc endloc, Parameters *arguments)
|
|
: FuncDeclaration(loc, endloc, Id::classDelete, STCstatic, NULL)
|
|
{
|
|
this->arguments = arguments;
|
|
}
|
|
|
|
Dsymbol *DeleteDeclaration::syntaxCopy(Dsymbol *s)
|
|
{
|
|
DeleteDeclaration *f;
|
|
|
|
f = new DeleteDeclaration(loc, endloc, NULL);
|
|
|
|
FuncDeclaration::syntaxCopy(f);
|
|
|
|
f->arguments = Parameter::arraySyntaxCopy(arguments);
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
void DeleteDeclaration::semantic(Scope *sc)
|
|
{
|
|
//printf("DeleteDeclaration::semantic()\n");
|
|
|
|
if (scope)
|
|
{ sc = scope;
|
|
scope = NULL;
|
|
}
|
|
|
|
parent = sc->parent;
|
|
Dsymbol *parent = toParent();
|
|
ClassDeclaration *cd = parent->isClassDeclaration();
|
|
if (!cd && !parent->isStructDeclaration())
|
|
{
|
|
error("new allocators only are for class or struct definitions");
|
|
}
|
|
if (!type)
|
|
type = new TypeFunction(arguments, Type::tvoid, 0, LINKd);
|
|
|
|
type = type->semantic(loc, sc);
|
|
assert(type->ty == Tfunction);
|
|
|
|
// Check that there is only one argument of type void*
|
|
TypeFunction *tf = (TypeFunction *)type;
|
|
if (Parameter::dim(tf->parameters) != 1)
|
|
{
|
|
error("one argument of type void* expected");
|
|
}
|
|
else
|
|
{
|
|
Parameter *a = Parameter::getNth(tf->parameters, 0);
|
|
if (!a->type->equals(Type::tvoid->pointerTo()))
|
|
error("one argument of type void* expected, not %s", a->type->toChars());
|
|
}
|
|
|
|
FuncDeclaration::semantic(sc);
|
|
}
|
|
|
|
const char *DeleteDeclaration::kind()
|
|
{
|
|
return "deallocator";
|
|
}
|
|
|
|
int DeleteDeclaration::isDelete()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int DeleteDeclaration::isVirtual()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int DeleteDeclaration::addPreInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int DeleteDeclaration::addPostInvariant()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void DeleteDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("delete");
|
|
Parameter::argsToCBuffer(buf, hgs, arguments, 0);
|
|
bodyToCBuffer(buf, hgs);
|
|
}
|
|
|
|
|
|
|
|
|