Skip to content

Commit

Permalink
Merged pull request #37 from donc/structinit.
Browse files Browse the repository at this point in the history
Fixes for two important CTFE regressions
  • Loading branch information
WalterBright committed Apr 30, 2011
2 parents 01cc218 + d835154 commit 0c9c52c
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 121 deletions.
35 changes: 10 additions & 25 deletions src/dsymbol.c
Expand Up @@ -1171,40 +1171,25 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
* multiple times, it gets set only once.
*/
if (!*pvar) // if not already initialized
{ /* Create variable v and set it to the value of $,
* which will be a constant.
{ /* Create variable v and set it to the value of $
*/
VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL);

if (ce->op == TOKvar)
{ // if ce is const, get its initializer
ce = fromConstInitializer(WANTvalue, ce);
}

if (ce->op == TOKstring)
{ /* It is for a string literal, so the
* length will be a const.
*/
Expression *e = new IntegerExp(0, ((StringExp *)ce)->len, Type::tsize_t);
v->init = new ExpInitializer(0, e);
v->storage_class |= STCstatic | STCconst;
}
else if (ce->op == TOKarrayliteral)
{ /* It is for an array literal, so the
* length will be a const.
*/
Expression *e = new IntegerExp(0, ((ArrayLiteralExp *)ce)->elements->dim, Type::tsize_t);
v->init = new ExpInitializer(0, e);
v->storage_class |= STCstatic | STCconst;
}
else if (ce->op == TOKtuple)
if (ce->op == TOKtuple)
{ /* It is for an expression tuple, so the
* length will be a const.
*/
Expression *e = new IntegerExp(0, ((TupleExp *)ce)->exps->dim, Type::tsize_t);
v->init = new ExpInitializer(0, e);
v->storage_class |= STCstatic | STCconst;
}
else
{ /* For arrays, $ will either be a compile-time constant
* (in which case its value in set during constant-folding),
* or a variable (in which case an expression is created in
* toir.c).
*/
v->init = new VoidInitializer(0);
}
*pvar = v;
}
(*pvar)->semantic(sc);
Expand Down
4 changes: 2 additions & 2 deletions src/inline.c
Expand Up @@ -665,7 +665,7 @@ Expression *IndexExp::doInline(InlineDoState *ids)
ids->from.push(vd);
ids->to.push(vto);

if (vd->init)
if (vd->init && !vd->init->isVoidInitializer())
{
ie = vd->init->isExpInitializer();
assert(ie);
Expand Down Expand Up @@ -702,7 +702,7 @@ Expression *SliceExp::doInline(InlineDoState *ids)
ids->from.push(vd);
ids->to.push(vto);

if (vd->init)
if (vd->init && !vd->init->isVoidInitializer())
{
ie = vd->init->isExpInitializer();
assert(ie);
Expand Down
213 changes: 120 additions & 93 deletions src/interpret.c
Expand Up @@ -2550,10 +2550,6 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
// ---------------------------------------
// Deal with reference assignment
// ---------------------------------------
// If the destination is an array literal or string literal, it is non-null here.
ArrayLiteralExp *dest_ae = NULL;
// AssocArrayLiteralExp *dest_aae = NULL;
StringExp *dest_se = NULL;
if (wantRef)
{
if (this->e2->op == TOKvar)
Expand All @@ -2564,7 +2560,13 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_

/* Set the $ variable
*/
Expression *dollar = ArrayLength(Type::tsize_t, sexp->e1->interpret(istate));
Expression *e1val = sexp->e1->interpret(istate);
Expression *dollar;
if (e1val->op == TOKnull)
dollar = new IntegerExp(0, 0, Type::tsize_t);
else
dollar = ArrayLength(Type::tsize_t, e1val);

if (dollar != EXP_CANT_INTERPRET && sexp->lengthVar)
{
sexp->lengthVar->createStackValue(dollar);
Expand All @@ -2589,107 +2591,126 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
newval = this->e2->interpret(istate);
if (newval == EXP_CANT_INTERPRET)
return newval;
if (e1->op == TOKvar)

if (newval->op == TOKarrayliteral || (newval->op == TOKassocarrayliteral)
|| newval->op == TOKstring)
{
VarExp *ve = (VarExp *)e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (!destinationIsReference)
addVarToInterstate(istate, v);
// It's a reference type. The old value gets lost.
if (newval->op == TOKarrayliteral || (newval->op == TOKassocarrayliteral)
|| newval->op == TOKstring)
{
if (needToCopyLiteral(this->e2))
newval = copyLiteral(newval);
v->setValueNull();
v->createRefValue(newval);
}
else if (newval->op == TOKnull)
{
v->setValueNull();
v->createRefValue(newval);
}
else if (newval->op == TOKvar)
{
VarExp *vv = (VarExp *)newval;
if (needToCopyLiteral(this->e2))
newval = copyLiteral(newval);
}
else if (newval->op == TOKnull)
{ // do nothing
}
else if (newval->op == TOKvar)
{
VarExp *vv = (VarExp *)newval;

VarDeclaration *v2 = vv->var->isVarDeclaration();
assert(v2 && v2->getValue());
newval = v2->getValue();
}
else if ((e1->op == TOKdotvar || e1->op == TOKvar) && newval->op == TOKslice)
{
// This one is interesting because it could be a slice of itself
SliceExp * sexp = (SliceExp *)newval;
Expression *agg = sexp->e1;
dinteger_t newlo = sexp->lwr->toInteger();
dinteger_t newup = sexp->upr->toInteger();
if (agg->op == TOKvar)
{
VarExp *vv = (VarExp *)agg;
VarDeclaration *v2 = vv->var->isVarDeclaration();
assert(v2 && v2->getValue());
assert((v2->getValue()->op == TOKarrayliteral ||
v2->getValue()->op == TOKassocarrayliteral ||
v2->getValue()->op == TOKstring ||
v2->getValue()->op == TOKslice ||
v2->getValue()->op == TOKnull) );
v->setValueNull();
v->createRefValue(v2->getValue());
}
else if (newval->op == TOKslice)
{
// This one is interesting because it could be a slice of itself
SliceExp * sexp = (SliceExp *)newval;
Expression *agg = sexp->e1;
dinteger_t newlo = sexp->lwr->toInteger();
dinteger_t newup = sexp->upr->toInteger();
if (agg->op == TOKvar)
if (v2->getValue()->op == TOKarrayliteral
|| v2->getValue()->op == TOKstring)
{
VarExp *vv = (VarExp *)agg;
VarDeclaration *v2 = vv->var->isVarDeclaration();
assert(v2 && v2->getValue());
if (v2->getValue()->op == TOKarrayliteral || v2->getValue()->op == TOKstring)
{
Expression *dollar = ArrayLength(Type::tsize_t, v2->getValue());
if ((newup < newlo) || (newup > dollar->toInteger()))
{
error("slice [%jd..%jd] exceeds array bounds [0..%jd]",
newlo, newup, dollar->toInteger());
return EXP_CANT_INTERPRET;
}
sexp->e1 = v2->getValue();
v->setValueNull();
v->createRefValue(sexp);
}
else if (v2->getValue()->op == TOKslice)
Expression *dollar = ArrayLength(Type::tsize_t, v2->getValue());
if ((newup < newlo) || (newup > dollar->toInteger()))
{
SliceExp *sexpold = (SliceExp *)v2->getValue();
sexp->e1 = sexpold->e1;
dinteger_t hi = newup + sexpold->lwr->toInteger();
dinteger_t lo = newlo + sexpold->lwr->toInteger();
if ((newup < newlo) || (hi > sexpold->upr->toInteger()))
{
error("slice [%jd..%jd] exceeds array bounds [0..%jd]",
newlo, newup, sexpold->upr->toInteger()-sexpold->lwr->toInteger());
return EXP_CANT_INTERPRET;
}
sexp->lwr = new IntegerExp(loc, lo, Type::tsize_t);
sexp->upr = new IntegerExp(loc, hi, Type::tsize_t);
v->setValueNull();
v->createRefValue(sexp);
error("slice [%jd..%jd] exceeds array bounds [0..%jd]",
newlo, newup, dollar->toInteger());
return EXP_CANT_INTERPRET;
}
else
sexp->e1 = v2->getValue();
newval = sexp;
}
else if (v2->getValue()->op == TOKslice)
{
SliceExp *sexpold = (SliceExp *)v2->getValue();
sexp->e1 = sexpold->e1;
dinteger_t hi = newup + sexpold->lwr->toInteger();
dinteger_t lo = newlo + sexpold->lwr->toInteger();
if ((newup < newlo) || (hi > sexpold->upr->toInteger()))
{
newval = newval->interpret(istate);
if (newval == EXP_CANT_INTERPRET)
return newval;
if (!v->getValue())
v->createRefValue(newval);
else v->setRefValue(newval);
error("slice [%jd..%jd] exceeds array bounds [0..%jd]",
newlo, newup, sexpold->upr->toInteger()-sexpold->lwr->toInteger());
return EXP_CANT_INTERPRET;
}
sexp->lwr = new IntegerExp(loc, lo, Type::tsize_t);
sexp->upr = new IntegerExp(loc, hi, Type::tsize_t);
newval = sexp;
}
else
{
newval = newval->interpret(istate);
if (newval == EXP_CANT_INTERPRET)
return newval;
if (!v->getValue())
v->createRefValue(newval);
else v->setRefValue(newval);
}
}
else
{
v->setValueNull();
v->createStackValue(newval);
newval = newval->interpret(istate);
if (newval == EXP_CANT_INTERPRET)
return newval;
}
}
if (e1->op == TOKvar || e1->op == TOKdotvar)
{

assert((newval->op == TOKarrayliteral ||
newval->op == TOKassocarrayliteral ||
newval->op == TOKstring ||
newval->op == TOKslice ||
newval->op == TOKnull) );
if (newval->op == TOKslice)
{
Expression *sss = ((SliceExp *)newval)->e1;
assert(sss->op == TOKarrayliteral || sss->op == TOKstring);
}
}

if (e1->op == TOKdotvar)
{
Expression *exx = ((DotVarExp *)e1)->e1->interpret(istate);
if (exx == EXP_CANT_INTERPRET)
return exx;
if (exx->op != TOKstructliteral)
{
error("CTFE internal error: Dotvar assignment");
return EXP_CANT_INTERPRET;
}
StructLiteralExp *se3 = (StructLiteralExp *)exx;
VarDeclaration *vv = ((DotVarExp *)e1)->var->isVarDeclaration();
if (!vv)
{
error("CTFE internal error: Dotvar assignment");
return EXP_CANT_INTERPRET;
}
int se_indx = se3->getFieldIndex(e1->type, vv->offset);
se3->elements->data[se_indx] = newval;
// Mark the parent variable as modified
if (!destinationIsReference)
addVarToInterstate(istate, ultimateVar);
return newval;
}
else if (e1->op == TOKvar)
{
VarExp *ve = (VarExp *)e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (!destinationIsReference)
addVarToInterstate(istate, v);
v->setValueNull();
v->createRefValue(newval);
return newval;
}
e = newval;
Expand Down Expand Up @@ -3585,12 +3606,6 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal)
if (e1 == EXP_CANT_INTERPRET)
goto Lcant;

if (e1->op == TOKnull)
{
error("cannot slice null array %s", this->e1->toChars());
return EXP_CANT_INTERPRET;
}

if (!this->lwr)
{
if (goal == ctfeNeedLvalue)
Expand All @@ -3601,7 +3616,10 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal)

/* Set the $ variable
*/
e = ArrayLength(Type::tsize_t, e1);
if (e1->op == TOKnull)
e = new IntegerExp(0, 0, Type::tsize_t);
else
e = ArrayLength(Type::tsize_t, e1);
if (e == EXP_CANT_INTERPRET)
{
error("Cannot determine length of %s at compile time\n", e1->toChars());
Expand All @@ -3627,6 +3645,15 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal)
e->type = type;
return e;
}
uinteger_t ilwr = lwr->toInteger();
uinteger_t iupr = upr->toInteger();
if (e1->op == TOKnull)
{
if (ilwr== 0 && iupr == 0)
return e1;
e1->error("slice [%ju..%ju] is out of bounds", ilwr, iupr);
return EXP_CANT_INTERPRET;
}
e = Slice(type, e1, lwr, upr);
if (e == EXP_CANT_INTERPRET)
error("%s cannot be interpreted at compile time", toChars());
Expand Down
4 changes: 3 additions & 1 deletion src/optimize.c
Expand Up @@ -924,8 +924,10 @@ Expression *IdentityExp::optimize(int result)
*/
void setLengthVarIfKnown(VarDeclaration *lengthVar, Expression *arr)
{
if (!lengthVar || lengthVar->init)
if (!lengthVar)
return;
if (lengthVar->init && !lengthVar->init->isVoidInitializer())
return; // we have previously calculated the length
size_t len;
if (arr->op == TOKstring)
len = ((StringExp *)arr)->len;
Expand Down
39 changes: 39 additions & 0 deletions test/compilable/interpret3.d
Expand Up @@ -690,3 +690,42 @@ static assert(bug5852("abc")==3);
*******************************************/

static assert( ['a', 'b'] ~ "c" == "abc" );

/*******************************************
Bug 5685
*******************************************/

string bug5685() {
return "xxx";
}
struct Bug5865 {
void test1(){
enum file2 = (bug5685())[0..$] ;
}
}

/*******************************************
Bug 5840
*******************************************/

struct Bug5840 {
string g;
int w;
}

int bug5840(int u)
{ // check for clobbering
Bug5840 x = void;
x.w = 4;
x.g = "3gs";
if (u==1) bug5840(2);
if (u==2) {
x.g = "abc";
x.w = 3465;
} else {
assert(x.g == "3gs");
assert(x.w == 4);
}
return 56;
}
static assert(bug5840(1)==56);

0 comments on commit 0c9c52c

Please sign in to comment.