Files
ldc/dmd2/clone.c
David Nadlinger ee7fe16e40 Low-hanging DMD diff reduction fruit.
The trailing whitespace isn't pretty, but removes the
files completly from the diff.
2013-10-05 21:19:35 +02:00

871 lines
27 KiB
C

// Compiler implementation of the D programming language
// Copyright (c) 1999-2011 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 "root.h"
#include "aggregate.h"
#include "scope.h"
#include "mtype.h"
#include "declaration.h"
#include "module.h"
#include "id.h"
#include "expression.h"
#include "statement.h"
#include "init.h"
#include "template.h"
/*******************************************
* Merge function attributes pure, nothrow, @safe, and @disable
*/
StorageClass mergeFuncAttrs(StorageClass s1, StorageClass s2)
{
StorageClass stc = 0;
StorageClass sa = s1 & s2;
StorageClass so = s1 | s2;
if (so & STCsystem)
stc |= STCsystem;
else if (sa & STCtrusted)
stc |= STCtrusted;
else if ((so & (STCtrusted | STCsafe)) == (STCtrusted | STCsafe))
stc |= STCtrusted;
else if (sa & STCsafe)
stc |= STCsafe;
if (sa & STCpure)
stc |= STCpure;
if (sa & STCnothrow)
stc |= STCnothrow;
if (so & STCdisable)
stc |= STCdisable;
return stc;
}
/*******************************************
* Check given opAssign symbol is really identity opAssign or not.
*/
FuncDeclaration *AggregateDeclaration::hasIdentityOpAssign(Scope *sc)
{
Dsymbol *assign = search_function(this, Id::assign);
if (assign)
{
/* check identity opAssign exists
*/
Expression *er = new NullExp(loc, type); // dummy rvalue
Expression *el = new IdentifierExp(loc, Id::p); // dummy lvalue
el->type = type;
Expressions *a = new Expressions();
a->setDim(1);
FuncDeclaration *f = NULL;
unsigned errors = global.startGagging(); // Do not report errors, even if the
unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it.
global.speculativeGag = global.gag;
sc = sc->push();
sc->speculative = true;
for (size_t i = 0; i < 2; i++)
{
(*a)[0] = (i == 0 ? er : el);
f = resolveFuncCall(loc, sc, assign, NULL, type, a, 1);
if (f)
break;
}
sc = sc->pop();
global.speculativeGag = oldspec;
global.endGagging(errors);
if (f)
{
int varargs;
Parameters *fparams = f->getParameters(&varargs);
if (fparams->dim >= 1)
{
Parameter *arg0 = Parameter::getNth(fparams, 0);
if (arg0->type->toDsymbol(NULL) != this)
f = NULL;
}
}
// BUGS: This detection mechanism cannot find some opAssign-s like follows:
// struct S { void opAssign(ref immutable S) const; }
return f;
}
return NULL;
}
/*******************************************
* We need an opAssign for the struct if
* it has a destructor or a postblit.
* We need to generate one if a user-specified one does not exist.
*/
int StructDeclaration::needOpAssign()
{
#define X 0
if (X) printf("StructDeclaration::needOpAssign() %s\n", toChars());
if (hasIdentityAssign)
goto Lneed; // because has identity==elaborate opAssign
if (dtor || postblit)
goto Lneed;
/* If any of the fields need an opAssign, then we
* need it too.
*/
for (size_t i = 0; i < fields.dim; i++)
{
Dsymbol *s = fields[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v && v->isField());
if (v->storage_class & STCref)
continue;
Type *tv = v->type->toBasetype();
while (tv->ty == Tsarray)
{ TypeSArray *ta = (TypeSArray *)tv;
tv = tv->nextOf()->toBasetype();
}
if (tv->ty == Tstruct)
{ TypeStruct *ts = (TypeStruct *)tv;
StructDeclaration *sd = ts->sym;
if (sd->needOpAssign())
goto Lneed;
}
}
Ldontneed:
if (X) printf("\tdontneed\n");
return 0;
Lneed:
if (X) printf("\tneed\n");
return 1;
#undef X
}
/******************************************
* Build opAssign for struct.
* ref S opAssign(S s) { ... }
*
* Note that s will be constructed onto the stack, and probably
* copy-constructed in caller site.
*
* If S has copy copy construction and/or destructor,
* the body will make bit-wise object swap:
* S __tmp = this; // bit copy
* this = s; // bit copy
* __tmp.dtor();
* Instead of running the destructor on s, run it on tmp instead.
*
* Otherwise, the body will make member-wise assignments:
* Then, the body is:
* this.field1 = s.field1;
* this.field2 = s.field2;
* ...;
*/
FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc)
{
if (FuncDeclaration *f = hasIdentityOpAssign(sc))
{
hasIdentityAssign = 1;
return f;
}
// Even if non-identity opAssign is defined, built-in identity opAssign
// will be defined.
if (!needOpAssign())
return NULL;
//printf("StructDeclaration::buildOpAssign() %s\n", toChars());
StorageClass stc = STCundefined;
Loc declLoc = this->loc;
Loc loc = Loc(); // internal code should have no loc to prevent coverage
Parameters *fparams = new Parameters;
fparams->push(new Parameter(STCnodtor, type, Id::p, NULL));
Type *ftype = new TypeFunction(fparams, handle, FALSE, LINKd);
((TypeFunction *)ftype)->isref = 1;
FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, ftype);
Expression *e = NULL;
if (dtor || postblit)
{
/* Do swap this and rhs
* tmp = this; this = s; tmp.dtor();
*/
//printf("\tswap copy\n");
Identifier *idtmp = Lexer::uniqueId("__tmp");
VarDeclaration *tmp;
AssignExp *ec = NULL;
if (dtor)
{
tmp = new VarDeclaration(loc, type, idtmp, new VoidInitializer(loc));
tmp->noscope = 1;
tmp->storage_class |= STCctfe;
e = new DeclarationExp(loc, tmp);
ec = new AssignExp(loc,
new VarExp(loc, tmp),
new ThisExp(loc)
);
ec->op = TOKblit;
e = Expression::combine(e, ec);
}
ec = new AssignExp(loc,
new ThisExp(loc),
new IdentifierExp(loc, Id::p));
ec->op = TOKblit;
e = Expression::combine(e, ec);
if (dtor)
{
/* Instead of running the destructor on s, run it
* on tmp. This avoids needing to copy tmp back in to s.
*/
Expression *ec2 = new DotVarExp(loc, new VarExp(loc, tmp), dtor, 0);
ec2 = new CallExp(loc, ec2);
e = Expression::combine(e, ec2);
}
}
else
{
/* Do memberwise copy
*/
//printf("\tmemberwise copy\n");
for (size_t i = 0; i < fields.dim; i++)
{
Dsymbol *s = fields[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v && v->isField());
// this.v = s.v;
AssignExp *ec = new AssignExp(loc,
new DotVarExp(loc, new ThisExp(loc), v, 0),
new DotVarExp(loc, new IdentifierExp(loc, Id::p), v, 0));
e = Expression::combine(e, ec);
}
}
Statement *s1 = new ExpStatement(loc, e);
/* Add:
* return this;
*/
e = new ThisExp(loc);
Statement *s2 = new ReturnStatement(loc, e);
fop->fbody = new CompoundStatement(loc, s1, s2);
Dsymbol *s = fop;
#if 1 // workaround until fixing issue 1528
Dsymbol *assign = search_function(this, Id::assign);
if (assign && assign->isTemplateDeclaration())
{
// Wrap a template around the function declaration
TemplateParameters *tpl = new TemplateParameters();
Dsymbols *decldefs = new Dsymbols();
decldefs->push(s);
TemplateDeclaration *tempdecl =
new TemplateDeclaration(assign->loc, fop->ident, tpl, NULL, decldefs, 0);
s = tempdecl;
}
#endif
members->push(s);
s->addMember(sc, this, 1);
this->hasIdentityAssign = 1; // temporary mark identity assignable
unsigned errors = global.startGagging(); // Do not report errors, even if the
unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it.
global.speculativeGag = global.gag;
Scope *sc2 = sc->push();
sc2->stc = 0;
sc2->linkage = LINKd;
sc2->speculative = true;
s->semantic(sc2);
s->semantic2(sc2);
s->semantic3(sc2);
sc2->pop();
global.speculativeGag = oldspec;
if (global.endGagging(errors)) // if errors happened
{ // Disable generated opAssign, because some members forbid identity assignment.
fop->storage_class |= STCdisable;
fop->fbody = NULL; // remove fbody which contains the error
}
//printf("-StructDeclaration::buildOpAssign() %s %s, errors = %d\n", toChars(), s->kind(), (fop->storage_class & STCdisable) != 0);
return fop;
}
/*******************************************
* We need an opEquals for the struct if
* any fields has an opEquals.
* Generate one if a user-specified one does not exist.
*/
int StructDeclaration::needOpEquals()
{
#define X 0
if (X) printf("StructDeclaration::needOpEquals() %s\n", toChars());
if (hasIdentityEquals)
goto Lneed;
if (isUnionDeclaration())
goto Ldontneed;
/* If any of the fields has an opEquals, then we
* need it too.
*/
for (size_t i = 0; i < fields.dim; i++)
{
Dsymbol *s = fields[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v && v->isField());
if (v->storage_class & STCref)
continue;
Type *tv = v->type->toBasetype();
if (tv->isfloating())
goto Lneed;
if (tv->ty == Tarray)
goto Lneed;
if (tv->ty == Taarray)
goto Lneed;
if (tv->ty == Tclass)
goto Lneed;
while (tv->ty == Tsarray)
{ TypeSArray *ta = (TypeSArray *)tv;
tv = tv->nextOf()->toBasetype();
}
if (tv->ty == Tstruct)
{ TypeStruct *ts = (TypeStruct *)tv;
StructDeclaration *sd = ts->sym;
if (sd->needOpEquals())
goto Lneed;
}
}
Ldontneed:
if (X) printf("\tdontneed\n");
return 0;
Lneed:
if (X) printf("\tneed\n");
return 1;
#undef X
}
FuncDeclaration *AggregateDeclaration::hasIdentityOpEquals(Scope *sc)
{
Dsymbol *eq = search_function(this, Id::eq);
if (eq)
{
/* check identity opEquals exists
*/
Expression *er = new NullExp(loc, NULL); // dummy rvalue
Expression *el = new IdentifierExp(loc, Id::p); // dummy lvalue
Expressions *a = new Expressions();
a->setDim(1);
for (size_t i = 0; ; i++)
{
Type *tthis;
if (i == 0) tthis = type;
if (i == 1) tthis = type->constOf();
if (i == 2) tthis = type->invariantOf();
if (i == 3) tthis = type->sharedOf();
if (i == 4) tthis = type->sharedConstOf();
if (i == 5) break;
FuncDeclaration *f = NULL;
unsigned errors = global.startGagging(); // Do not report errors, even if the
unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it.
global.speculativeGag = global.gag;
sc = sc->push();
sc->speculative = true;
for (size_t j = 0; j < 2; j++)
{
(*a)[0] = (j == 0 ? er : el);
(*a)[0]->type = tthis;
f = resolveFuncCall(loc, sc, eq, NULL, tthis, a, 1);
if (f)
break;
}
sc = sc->pop();
global.speculativeGag = oldspec;
global.endGagging(errors);
if (f)
return f;
}
}
return NULL;
}
/******************************************
* Build opEquals for struct.
* const bool opEquals(const S s) { ... }
*
* By fixing bugzilla 3789, opEquals is changed to be never implicitly generated.
* Now, struct objects comparison s1 == s2 is translated to:
* s1.tupleof == s2.tupleof
* to calculate structural equality. See EqualExp::semantic.
*/
FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc)
{
if (FuncDeclaration *f = hasIdentityOpEquals(sc))
{
hasIdentityEquals = 1;
}
return NULL;
}
/******************************************
* Build __xopEquals for TypeInfo_Struct
* static bool __xopEquals(ref const S p, ref const S q)
* {
* return p == q;
* }
*
* This is called by TypeInfo.equals(p1, p2). If the struct does not support
* const objects comparison, it will throw "not implemented" Error in runtime.
*/
FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc)
{
if (!needOpEquals())
return NULL;
//printf("StructDeclaration::buildXopEquals() %s\n", toChars());
Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly
Loc loc = Loc(); // loc is unnecessary so errors are gagged
Parameters *parameters = new Parameters;
parameters->push(new Parameter(STCref | STCconst, type, Id::p, NULL));
parameters->push(new Parameter(STCref | STCconst, type, Id::q, NULL));
TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd);
tf = (TypeFunction *)tf->semantic(loc, sc);
Identifier *id = Lexer::idPool("__xopEquals");
FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf);
Expression *e1 = new IdentifierExp(loc, Id::p);
Expression *e2 = new IdentifierExp(loc, Id::q);
Expression *e = new EqualExp(TOKequal, loc, e1, e2);
fop->fbody = new ReturnStatement(loc, e);
size_t index = members->dim;
members->push(fop);
unsigned errors = global.startGagging(); // Do not report errors, even if the
unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it.
global.speculativeGag = global.gag;
Scope *sc2 = sc->push();
sc2->stc = 0;
sc2->linkage = LINKd;
sc2->speculative = true;
fop->semantic(sc2);
fop->semantic2(sc2);
fop->semantic3(sc2);
sc2->pop();
global.speculativeGag = oldspec;
if (global.endGagging(errors)) // if errors happened
{
members->remove(index);
if (!xerreq)
{
Expression *e = new IdentifierExp(loc, Id::empty);
e = new DotIdExp(loc, e, Id::object);
e = new DotIdExp(loc, e, Lexer::idPool("_xopEquals"));
e = e->semantic(sc);
Dsymbol *s = getDsymbol(e);
FuncDeclaration *fd = s->isFuncDeclaration();
xerreq = fd;
}
fop = xerreq;
}
else
fop->addMember(sc, this, 1);
return fop;
}
/*******************************************
* Build copy constructor for struct.
* void __cpctpr(ref const S s) const [pure nothrow @trusted]
* {
* (*cast(S*)&this) = *cast(S*)s;
* (*cast(S*)&this).postBlit();
* }
*
* Copy constructors are compiler generated only, and are only
* callable from the compiler. They are not user accessible.
*
* This is done so:
* - postBlit() never sees uninitialized data
* - memcpy can be much more efficient than memberwise copy
* - no fields are overlooked
*/
FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc)
{
/* Copy constructor is only necessary if there is a postblit function,
* otherwise the code generator will just do a bit copy.
*/
if (!postblit)
return NULL;
//printf("StructDeclaration::buildCpCtor() %s\n", toChars());
StorageClass stc = STCsafe | STCnothrow | STCpure;
Loc declLoc = postblit->loc;
Loc loc = Loc(); // internal code should have no loc to prevent coverage
stc = mergeFuncAttrs(stc, postblit->storage_class);
if (stc & STCsafe) // change to @trusted for unsafe casts
stc = stc & ~STCsafe | STCtrusted;
Parameters *fparams = new Parameters;
fparams->push(new Parameter(STCref, type->constOf(), Id::p, NULL));
Type *ftype = new TypeFunction(fparams, Type::tvoid, 0, LINKd, stc);
ftype->mod = MODconst;
FuncDeclaration *fcp = new FuncDeclaration(declLoc, Loc(), Id::cpctor, stc, ftype);
if (!(stc & STCdisable))
{
// Build *this = p;
Expression *e = new ThisExp(loc);
AssignExp *ea = new AssignExp(loc,
new PtrExp(loc, new CastExp(loc, new AddrExp(loc, e), type->mutableOf()->pointerTo())),
new PtrExp(loc, new CastExp(loc, new AddrExp(loc, new IdentifierExp(loc, Id::p)), type->mutableOf()->pointerTo()))
);
ea->op = TOKblit;
Statement *s = new ExpStatement(loc, ea);
// Build postBlit();
e = new ThisExp(loc);
e = new PtrExp(loc, new CastExp(loc, new AddrExp(loc, e), type->mutableOf()->pointerTo()));
e = new DotVarExp(loc, e, postblit, 0);
e = new CallExp(loc, e);
s = new CompoundStatement(loc, s, new ExpStatement(loc, e));
fcp->fbody = s;
}
members->push(fcp);
sc = sc->push();
sc->stc = 0;
sc->linkage = LINKd;
fcp->semantic(sc);
sc->pop();
return fcp;
}
/*****************************************
* Create inclusive postblit for struct by aggregating
* all the postblits in postblits[] with the postblits for
* all the members.
* Note the close similarity with AggregateDeclaration::buildDtor(),
* and the ordering changes (runs forward instead of backwards).
*/
#if DMDV2
FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc)
{
//printf("StructDeclaration::buildPostBlit() %s\n", toChars());
StorageClass stc = STCsafe | STCnothrow | STCpure;
Loc declLoc = postblits.dim ? postblits[0]->loc : this->loc;
Loc loc = Loc(); // internal code should have no loc to prevent coverage
Expression *e = NULL;
for (size_t i = 0; i < fields.dim; i++)
{
Dsymbol *s = fields[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v && v->isField());
if (v->storage_class & STCref)
continue;
Type *tv = v->type->toBasetype();
dinteger_t dim = 1;
while (tv->ty == Tsarray)
{ TypeSArray *ta = (TypeSArray *)tv;
dim *= ((TypeSArray *)tv)->dim->toInteger();
tv = tv->nextOf()->toBasetype();
}
if (tv->ty == Tstruct)
{ TypeStruct *ts = (TypeStruct *)tv;
StructDeclaration *sd = ts->sym;
if (sd->postblit && dim)
{
stc = mergeFuncAttrs(stc, sd->postblit->storage_class);
if (stc & STCdisable)
{
e = NULL;
break;
}
// this.v
Expression *ex = new ThisExp(loc);
ex = new DotVarExp(loc, ex, v, 0);
if (v->type->toBasetype()->ty == Tstruct)
{ // this.v.postblit()
ex = new DotVarExp(loc, ex, sd->postblit, 0);
ex = new CallExp(loc, ex);
}
else
{
// Typeinfo.postblit(cast(void*)&this.v);
Expression *ea = new AddrExp(loc, ex);
ea = new CastExp(loc, ea, Type::tvoid->pointerTo());
Expression *et = v->type->getTypeInfo(sc);
et = new DotIdExp(loc, et, Id::postblit);
ex = new CallExp(loc, et, ea);
}
e = Expression::combine(e, ex); // combine in forward order
}
}
}
/* Build our own "postblit" which executes e
*/
if (e || (stc & STCdisable))
{ //printf("Building __fieldPostBlit()\n");
PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Lexer::idPool("__fieldPostBlit"));
dd->fbody = new ExpStatement(loc, e);
postblits.shift(dd);
members->push(dd);
dd->semantic(sc);
}
switch (postblits.dim)
{
case 0:
return NULL;
case 1:
return postblits[0];
default:
e = NULL;
stc = STCsafe | STCnothrow | STCpure;
for (size_t i = 0; i < postblits.dim; i++)
{
FuncDeclaration *fd = postblits[i];
stc = mergeFuncAttrs(stc, fd->storage_class);
if (stc & STCdisable)
{
e = NULL;
break;
}
Expression *ex = new ThisExp(loc);
ex = new DotVarExp(loc, ex, fd, 0);
ex = new CallExp(loc, ex);
e = Expression::combine(e, ex);
}
PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Lexer::idPool("__aggrPostBlit"));
dd->fbody = new ExpStatement(loc, e);
members->push(dd);
dd->semantic(sc);
return dd;
}
}
#endif
/*****************************************
* Create inclusive destructor for struct/class by aggregating
* all the destructors in dtors[] with the destructors for
* all the members.
* Note the close similarity with StructDeclaration::buildPostBlit(),
* and the ordering changes (runs backward instead of forwards).
*/
FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc)
{
//printf("AggregateDeclaration::buildDtor() %s\n", toChars());
StorageClass stc = STCsafe | STCnothrow | STCpure;
Loc declLoc = dtors.dim ? dtors[0]->loc : this->loc;
Loc loc = Loc(); // internal code should have no loc to prevent coverage
Expression *e = NULL;
#if DMDV2
for (size_t i = 0; i < fields.dim; i++)
{
Dsymbol *s = fields[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v && v->isField());
if (v->storage_class & STCref)
continue;
Type *tv = v->type->toBasetype();
dinteger_t dim = 1;
while (tv->ty == Tsarray)
{ TypeSArray *ta = (TypeSArray *)tv;
dim *= ((TypeSArray *)tv)->dim->toInteger();
tv = tv->nextOf()->toBasetype();
}
if (tv->ty == Tstruct)
{ TypeStruct *ts = (TypeStruct *)tv;
StructDeclaration *sd = ts->sym;
if (sd->dtor && dim)
{
stc = mergeFuncAttrs(stc, sd->dtor->storage_class);
if (stc & STCdisable)
{
e = NULL;
break;
}
// this.v
Expression *ex = new ThisExp(loc);
ex = new DotVarExp(loc, ex, v, 0);
if (v->type->toBasetype()->ty == Tstruct)
{ // this.v.dtor()
ex = new DotVarExp(loc, ex, sd->dtor, 0);
ex = new CallExp(loc, ex);
}
else
{
// Typeinfo.destroy(cast(void*)&this.v);
Expression *ea = new AddrExp(loc, ex);
ea = new CastExp(loc, ea, Type::tvoid->pointerTo());
Expression *et = v->type->getTypeInfo(sc);
et = new DotIdExp(loc, et, Id::destroy);
ex = new CallExp(loc, et, ea);
}
e = Expression::combine(ex, e); // combine in reverse order
}
}
}
/* Build our own "destructor" which executes e
*/
if (e || (stc & STCdisable))
{ //printf("Building __fieldDtor()\n");
DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Lexer::idPool("__fieldDtor"));
dd->fbody = new ExpStatement(loc, e);
dtors.shift(dd);
members->push(dd);
dd->semantic(sc);
}
#endif
switch (dtors.dim)
{
case 0:
return NULL;
case 1:
return dtors[0];
default:
e = NULL;
stc = STCsafe | STCnothrow | STCpure;
for (size_t i = 0; i < dtors.dim; i++)
{
FuncDeclaration *fd = dtors[i];
stc = mergeFuncAttrs(stc, fd->storage_class);
if (stc & STCdisable)
{
e = NULL;
break;
}
Expression *ex = new ThisExp(loc);
ex = new DotVarExp(loc, ex, fd, 0);
ex = new CallExp(loc, ex);
e = Expression::combine(ex, e);
}
DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Lexer::idPool("__aggrDtor"));
dd->fbody = new ExpStatement(loc, e);
members->push(dd);
dd->semantic(sc);
return dd;
}
}
/******************************************
* Create inclusive invariant for struct/class by aggregating
* all the invariants in invs[].
* void __invariant() const [pure nothrow @trusted]
* {
* invs[0](), invs[1](), ...;
* }
*/
FuncDeclaration *AggregateDeclaration::buildInv(Scope *sc)
{
StorageClass stc = STCsafe | STCnothrow | STCpure;
Loc declLoc = this->loc;
Loc loc = Loc(); // internal code should have no loc to prevent coverage
switch (invs.dim)
{
case 0:
return NULL;
case 1:
// Don't return invs[0] so it has uniquely generated name.
/* fall through */
default:
Expression *e = NULL;
StorageClass stcx = 0;
for (size_t i = 0; i < invs.dim; i++)
{
stc = mergeFuncAttrs(stc, invs[i]->storage_class);
if (stc & STCdisable)
{
// What should do?
}
StorageClass stcy = invs[i]->storage_class & (STCshared | STCsynchronized);
if (i == 0)
stcx = stcy;
else if (stcx ^ stcy)
{
#if 1 // currently rejects
error(invs[i]->loc, "mixing invariants with shared/synchronized differene is not supported");
e = NULL;
break;
#endif
}
e = Expression::combine(e, new CallExp(loc, new VarExp(loc, invs[i])));
}
InvariantDeclaration *inv;
inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id::classInvariant);
inv->fbody = new ExpStatement(loc, e);
members->push(inv);
inv->semantic(sc);
return inv;
}
}