Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 9258 & 9404 & 9416 - fix regressions around opAssign #1585

Merged
merged 5 commits into from
Jan 31, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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