Skip to content

Commit

Permalink
Merge pull request #1585 from 9rnsr/fix_assign
Browse files Browse the repository at this point in the history
Issue 9258 & 9404 & 9416 - fix regressions around opAssign
  • Loading branch information
WalterBright committed Jan 31, 2013
2 parents d617999 + aa33955 commit b94fd3b
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 668 deletions.
2 changes: 2 additions & 0 deletions src/aggregate.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ struct AggregateDeclaration : ScopeDsymbol
void toJson(JsonOut *json);
void toDocBuffer(OutBuffer *buf, Scope *sc);

FuncDeclaration *hasIdentityOpAssign(Scope *sc, Dsymbol *assign);

// For access checking
virtual PROT getAccess(Dsymbol *smember); // determine access to smember
int isFriendOf(AggregateDeclaration *cd);
Expand Down
24 changes: 4 additions & 20 deletions src/class.c
Original file line number Diff line number Diff line change
Expand Up @@ -783,27 +783,11 @@ void ClassDeclaration::semantic(Scope *sc)
dtor = buildDtor(sc);
if (Dsymbol *assign = search_function(this, Id::assign))
{
Expression *e = new NullExp(loc, type); // dummy rvalue
Expressions *arguments = new Expressions();
arguments->push(e);

// check identity opAssign exists
FuncDeclaration *fd = assign->isFuncDeclaration();
if (fd)
{ fd = fd->overloadResolve(loc, e, arguments, 1);
if (fd && !(fd->storage_class & STCdisable))
goto Lassignerr;
}

if (TemplateDeclaration *td = assign->isTemplateDeclaration())
{ fd = td->deduceFunctionTemplate(sc, loc, NULL, e, arguments, 1+2);
if (fd && !(fd->storage_class & STCdisable))
goto Lassignerr;
if (FuncDeclaration *f = hasIdentityOpAssign(sc, assign))
{
if (!(f->storage_class & STCdisable))
error("identity assignment operator overload is illegal");
}

Lassignerr:
if (fd && !(fd->storage_class & STCdisable))
error("identity assignment operator overload is illegal");
}
sc->pop();

Expand Down
138 changes: 95 additions & 43 deletions src/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,62 @@
#include "template.h"


/*******************************************
* Check given opAssign symbol is really identity opAssign or not.
*/

FuncDeclaration *AggregateDeclaration::hasIdentityOpAssign(Scope *sc, Dsymbol *assign)
{
if (assign)
{
assert(assign->ident == Id::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 ar; ar.push(er);
Expressions al; al.push(el);
FuncDeclaration *f = NULL;
if (FuncDeclaration *fd = assign->isFuncDeclaration())
{
f = fd->overloadResolve(loc, er, &ar, 1);
if (!f) f = fd->overloadResolve(loc, er, &al, 1);
}
if (TemplateDeclaration *td = assign->isTemplateDeclaration())
{
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->speculative = true;

f = td->deduceFunctionTemplate(sc2, loc, NULL, er, &ar, 1);
if (!f) f = td->deduceFunctionTemplate(sc2, loc, NULL, er, &al, 1);

sc2->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.
Expand Down Expand Up @@ -90,27 +146,8 @@ FuncDeclaration *StructDeclaration::buildOpAssign(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 ar; ar.push(er);
Expressions al; al.push(el);
if (FuncDeclaration *fd = assign->isFuncDeclaration())
{
FuncDeclaration *f = fd->overloadResolve(loc, er, &ar, 1);
if (f == NULL) f = fd->overloadResolve(loc, er, &al, 1);
if (f)
return (f->storage_class & STCdisable) ? NULL : f;
}
if (TemplateDeclaration *td = assign->isTemplateDeclaration())
{
FuncDeclaration *f = td->deduceFunctionTemplate(sc, loc, NULL, er, &ar, 1);
if (f == NULL) f = td->deduceFunctionTemplate(sc, loc, NULL, er, &al, 1);
if (f)
return (f->storage_class & STCdisable) ? NULL : f;
}
if (FuncDeclaration *f = hasIdentityOpAssign(sc, assign))
return f;
// Even if non-identity opAssign is defined, built-in identity opAssign
// will be defined. (Is this an exception of operator overloading rule?)
}
Expand Down Expand Up @@ -213,14 +250,29 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc)
}
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
}

sc = sc->push();
sc->stc = 0;
sc->linkage = LINKd;
s->semantic(sc);
sc->pop();

//printf("-StructDeclaration::buildOpAssign() %s\n", toChars());
//printf("-StructDeclaration::buildOpAssign() %s %s, errors = %d\n", toChars(), s->kind(), (fop->storage_class & STCdisable) != 0);

return fop;
}
Expand Down Expand Up @@ -410,20 +462,20 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc)
size_t index = members->dim;
members->push(fop);

sc = sc->push();
sc->stc = 0;
sc->linkage = LINKd;
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;

unsigned errors = global.startGagging();
fop->semantic(sc);
if (errors == global.gaggedErrors)
{ fop->semantic2(sc);
if (errors == global.gaggedErrors)
{ fop->semantic3(sc);
if (errors == global.gaggedErrors)
fop->addMember(sc, this, 1);
}
}
fop->semantic(sc2);
fop->semantic2(sc2);
fop->semantic3(sc2);

sc2->pop();
global.speculativeGag = oldspec;
if (global.endGagging(errors)) // if errors happened
{
members->remove(index);
Expand All @@ -441,8 +493,8 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc)
}
fop = xerreq;
}

sc->pop();
else
fop->addMember(sc, this, 1);

return fop;
}
Expand Down
2 changes: 1 addition & 1 deletion src/expression.c
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
else if (p->storageClass & STCout)
{
Type *t = arg->type;
if (!t->isMutable() || !t->isAssignable(1)) // check blit assignable
if (!t->isMutable() || !t->isAssignable()) // check blit assignable
arg->error("cannot modify struct %s with immutable members", arg->toChars());
arg = arg->toLvalue(sc, arg);
}
Expand Down
27 changes: 9 additions & 18 deletions src/mtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -1761,13 +1761,14 @@ int Type::isString()
}

/**************************
* When T is mutable,
* Given:
* T a, b;
* Can we assign:
* Can we bitwise assign:
* a = b;
* ?
*/
int Type::isAssignable(int blit)
int Type::isAssignable()
{
return TRUE;
}
Expand Down Expand Up @@ -7525,9 +7526,9 @@ int TypeEnum::isscalar()
return sym->memtype->isscalar();
}

int TypeEnum::isAssignable(int blit)
int TypeEnum::isAssignable()
{
return sym->memtype->isAssignable(blit);
return sym->memtype->isAssignable();
}

int TypeEnum::checkBoolean()
Expand Down Expand Up @@ -7747,9 +7748,9 @@ int TypeTypedef::isscalar()
return sym->basetype->isscalar();
}

int TypeTypedef::isAssignable(int blit)
int TypeTypedef::isAssignable()
{
return sym->basetype->isAssignable(blit);
return sym->basetype->isAssignable();
}

int TypeTypedef::checkBoolean()
Expand Down Expand Up @@ -8289,18 +8290,8 @@ bool TypeStruct::needsNested()
return false;
}

int TypeStruct::isAssignable(int blit)
int TypeStruct::isAssignable()
{
if (!blit)
{
if (sym->hasIdentityAssign)
return TRUE;

// has non-identity opAssign
if (search_function(sym, Id::assign))
return FALSE;
}

int assignable = TRUE;
unsigned offset;

Expand All @@ -8326,7 +8317,7 @@ int TypeStruct::isAssignable(int blit)
if (!assignable)
return FALSE;
}
assignable = v->type->isMutable() && v->type->isAssignable(blit);
assignable = v->type->isMutable() && v->type->isAssignable();
offset = v->offset;
//printf(" -> assignable = %d\n", assignable);
}
Expand Down
8 changes: 4 additions & 4 deletions src/mtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ struct Type : Object
virtual int isunsigned();
virtual int isscope();
virtual int isString();
virtual int isAssignable(int blit = 0);
virtual int isAssignable();
virtual int checkBoolean(); // if can be converted to boolean value
virtual void checkDeprecated(Loc loc, Scope *sc);
int isConst() { return mod & MODconst; }
Expand Down Expand Up @@ -812,7 +812,7 @@ struct TypeStruct : Type
Expression *defaultInitLiteral(Loc loc);
Expression *voidInitLiteral(VarDeclaration *var);
int isZeroInit(Loc loc);
int isAssignable(int blit = 0);
int isAssignable();
int checkBoolean();
int needsDestruction();
bool needsNested();
Expand Down Expand Up @@ -857,7 +857,7 @@ struct TypeEnum : Type
int isscalar();
int isunsigned();
int checkBoolean();
int isAssignable(int blit = 0);
int isAssignable();
int needsDestruction();
bool needsNested();
MATCH implicitConvTo(Type *to);
Expand Down Expand Up @@ -902,7 +902,7 @@ struct TypeTypedef : Type
int isscalar();
int isunsigned();
int checkBoolean();
int isAssignable(int blit = 0);
int isAssignable();
int needsDestruction();
bool needsNested();
Type *toBasetype();
Expand Down
9 changes: 9 additions & 0 deletions src/opover.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,15 @@ Expression *BinExp::op_overload(Scope *sc)
AggregateDeclaration *ad1 = isAggregate(e1->type);
AggregateDeclaration *ad2 = isAggregate(e2->type);

if (op == TOKassign && ad1 == ad2)
{
StructDeclaration *sd = ad1->isStructDeclaration();
if (sd && !sd->hasIdentityAssign)
{ /* This is bitwise struct assignment. */
return NULL;
}
}

Dsymbol *s = NULL;
Dsymbol *s_r = NULL;

Expand Down

0 comments on commit b94fd3b

Please sign in to comment.