Skip to content

Commit

Permalink
fix Issue 6216 - Built-in opAssign implicitly defined should call fie…
Browse files Browse the repository at this point in the history
…ld's opAssign

Separate 'top assignable' (see opAssign first) from 'blit assignable' (memberwise), and now they are not related to 'modifiable' directly.
  • Loading branch information
9rnsr committed Oct 6, 2012
1 parent 9ee798f commit b9896d1
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 19 deletions.
2 changes: 0 additions & 2 deletions src/declaration.c
Expand Up @@ -157,8 +157,6 @@ void Declaration::checkModify(Loc loc, Scope *sc, Type *t)
p = "inout";
else if (storage_class & STCmanifest)
p = "enum";
else if (!t->isAssignable())
p = "struct with immutable members";
if (p)
{ error(loc, "cannot modify %s", p);
}
Expand Down
28 changes: 23 additions & 5 deletions src/expression.c
Expand Up @@ -1120,7 +1120,10 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
}
else if (p->storageClass & STCout)
{
arg = arg->modifiableLvalue(sc, arg);
if (arg->type->isAssignable(1)) // check blit assignable
arg = arg->modifiableLvalue(sc, arg);
else
arg->error("cannot modify struct %s with immutable members", arg->toChars());
}
else if (p->storageClass & STClazy)
{ // Convert lazy argument to a delegate
Expand Down Expand Up @@ -1589,7 +1592,7 @@ Expression *Expression::modifiableLvalue(Scope *sc, Expression *e)

// See if this expression is a modifiable lvalue (i.e. not const)
#if DMDV2
if (type && (!type->isMutable() || !type->isAssignable()))
if (type && !type->isMutable())
{ error("%s is not mutable", e->toChars());
return new ErrorExp();
}
Expand Down Expand Up @@ -7148,7 +7151,6 @@ Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e)
if (!t1->isMutable() ||
(t1->ty == Tpointer && !t1->nextOf()->isMutable()) ||
!var->type->isMutable() ||
!var->type->isAssignable() ||
var->storage_class & STCmanifest
)
{
Expand Down Expand Up @@ -9940,7 +9942,7 @@ Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e)
modifiable = 1;
if (e1->op == TOKstring)
error("string literals are immutable");
if (type && (!type->isMutable() || !type->isAssignable()))
if (type && !type->isMutable())
error("%s isn't mutable", e->toChars());
Type *t1 = e1->type->toBasetype();
if (t1->ty == Taarray)
Expand Down Expand Up @@ -10541,8 +10543,15 @@ Expression *AssignExp::semantic(Scope *sc)
e = ae->op_overload(sc);
e2 = new CommaExp(loc, new CommaExp(loc, de, e), ve);
e2 = e2->semantic(sc);

e1 = e1->optimize(WANTvalue);
e1 = e1->modifiableLvalue(sc, e1);
e2 = e2->implicitCastTo(sc, e1->type);
type = e1->type;
assert(type);
e = this;
}
else if (e)
if (e)
return e;
}
else if (op == TOKconstruct && !refinit)
Expand Down Expand Up @@ -10726,6 +10735,15 @@ Expression *AssignExp::semantic(Scope *sc)
{
e2 = e2->implicitCastTo(sc, e1->type);
}
if (e2->op == TOKerror)
return new ErrorExp();

// Check identity assignable (opAssign overloading is already resolved)
if (op == TOKassign && !t1->isAssignable())
{
error("cannot modify struct with immutable members");
return new ErrorExp();
}

/* Look for array operations
*/
Expand Down
24 changes: 17 additions & 7 deletions src/mtype.c
Expand Up @@ -1749,7 +1749,7 @@ int Type::isString()
* a = b;
* ?
*/
int Type::isAssignable()
int Type::isAssignable(int blit)
{
return TRUE;
}
Expand Down Expand Up @@ -7323,9 +7323,9 @@ int TypeEnum::isscalar()
return sym->memtype->isscalar();
}

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

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

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

int TypeTypedef::checkBoolean()
Expand Down Expand Up @@ -8045,8 +8045,18 @@ int TypeStruct::needsDestruction()
return sym->dtor != NULL;
}

int TypeStruct::isAssignable()
int TypeStruct::isAssignable(int blit)
{
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 @@ -8072,7 +8082,7 @@ int TypeStruct::isAssignable()
if (!assignable)
return FALSE;
}
assignable = v->type->isMutable() && v->type->isAssignable();
assignable = v->type->isMutable() && v->type->isAssignable(blit);
offset = v->offset;
//printf(" -> assignable = %d\n", assignable);
}
Expand Down
8 changes: 4 additions & 4 deletions src/mtype.h
Expand Up @@ -252,7 +252,7 @@ struct Type : Object
virtual int isunsigned();
virtual int isscope();
virtual int isString();
virtual int isAssignable();
virtual int isAssignable(int blit = 0);
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 @@ -770,7 +770,7 @@ struct TypeStruct : Type
Expression *defaultInitLiteral(Loc loc);
Expression *voidInitLiteral(VarDeclaration *var);
int isZeroInit(Loc loc);
int isAssignable();
int isAssignable(int blit = 0);
int checkBoolean();
int needsDestruction();
dt_t **toDt(dt_t **pdt);
Expand Down Expand Up @@ -812,7 +812,7 @@ struct TypeEnum : Type
int isscalar();
int isunsigned();
int checkBoolean();
int isAssignable();
int isAssignable(int blit = 0);
int needsDestruction();
MATCH implicitConvTo(Type *to);
MATCH constConv(Type *to);
Expand Down Expand Up @@ -854,7 +854,7 @@ struct TypeTypedef : Type
int isscalar();
int isunsigned();
int checkBoolean();
int isAssignable();
int isAssignable(int blit = 0);
int needsDestruction();
Type *toBasetype();
MATCH implicitConvTo(Type *to);
Expand Down
124 changes: 123 additions & 1 deletion test/runnable/assignable.d
Expand Up @@ -198,7 +198,7 @@ void test5()
assert(s.mX == 0xA);
s.x ^= 0xF;
assert(s.mX == 0x5);

s.x ^^= 2;
assert(s.mX == 25);

Expand All @@ -210,6 +210,125 @@ void test5()
}

/***************************************************/
// 6216

void test6216a()
{
static class C{}

static struct Xa{ int n; }
static struct Xb{ int[] a; }
static struct Xc{ C c; }
static struct Xd{ void opAssign(typeof(this) rhs){} }
static struct Xe{ void opAssign(T)(T rhs){} }
static struct Xf{ void opAssign(int rhs){} }
static struct Xg{ void opAssign(T)(T rhs)if(!is(T==typeof(this))){} }

// has value type as member
static struct S1 (X){ static if (!is(X==void)) X x; int n; }

// has reference type as member
static struct S2a(X){ static if (!is(X==void)) X x; int[] a; }
static struct S2b(X){ static if (!is(X==void)) X x; C c; }

// has identity opAssign
static struct S3a(X){ static if (!is(X==void)) X x; void opAssign(typeof(this) rhs){} }
static struct S3b(X){ static if (!is(X==void)) X x; void opAssign(T)(T rhs){} }

// has non identity opAssign
static struct S4a(X){ static if (!is(X==void)) X x; void opAssign(int rhs){} }
static struct S4b(X){ static if (!is(X==void)) X x; void opAssign(T)(T rhs)if(!is(T==typeof(this))){} }

enum result = [
/*S1, S2a, S2b, S3a, S3b, S4a, S4b*/
/*- */ [true, true, true, true, true, false, false],
/*Xa*/ [true, true, true, true, true, false, false],
/*Xb*/ [true, true, true, true, true, false, false],
/*Xc*/ [true, true, true, true, true, false, false],
/*Xd*/ [true, true, true, true, true, false, false],
/*Xe*/ [true, true, true, true, true, false, false],
/*Xf*/ [false, false, false, true, true, false, false],
/*Xg*/ [false, false, false, true, true, false, false]
];

pragma(msg, "\\\tS1\tS2a\tS2b\tS3a\tS3b\tS4a\tS4b");
foreach (i, X; TypeTuple!(void,Xa,Xb,Xc,Xd,Xe,Xf,Xg))
{
S1!X s1;
S2a!X s2a;
S2b!X s2b;
S3a!X s3a;
S3b!X s3b;
S4a!X s4a;
S4b!X s4b;

pragma(msg,
is(X==void) ? "-" : X.stringof,
"\t", __traits(compiles, (s1 = s1)),
"\t", __traits(compiles, (s2a = s2a)),
"\t", __traits(compiles, (s2b = s2b)),
"\t", __traits(compiles, (s3a = s3a)),
"\t", __traits(compiles, (s3b = s3b)),
"\t", __traits(compiles, (s4a = s4a)),
"\t", __traits(compiles, (s4b = s4b)) );

static assert(result[i] ==
[ __traits(compiles, (s1 = s1)),
__traits(compiles, (s2a = s2a)),
__traits(compiles, (s2b = s2b)),
__traits(compiles, (s3a = s3a)),
__traits(compiles, (s3b = s3b)),
__traits(compiles, (s4a = s4a)),
__traits(compiles, (s4b = s4b)) ]);
}
}

void test6216b()
{
static int cnt = 0;

static struct X
{
int n;
void opAssign(X rhs){ cnt = 1; }
}
static struct S
{
int n;
X x;
}

S s;
s = s;
assert(cnt == 1);
// Built-in opAssign runs member's opAssign
}

void test6216c()
{
static int cnt = 0;

static struct X
{
int n;
const void opAssign(const X rhs){ cnt = 2; }
}
static struct S
{
int n;
const(X) x;
}

S s;
const(S) cs;
s = s;
s = cs; // cs is copied as mutable and assigned into s
assert(cnt == 2);
// cs = cs; // built-in opAssin is only allowed with mutable object
}

/***************************************************/
// 6286

void test6286()
{
Expand All @@ -231,6 +350,9 @@ int main()
test3();
test4();
test5();
test6216a();
test6216b();
test6216c();
test6286();

printf("Success\n");
Expand Down

0 comments on commit b9896d1

Please sign in to comment.