Another shot at fixing the issues with (constant) struct literals and their addresses. See DMD2682, #218, #324.

The idea is to separate the notion of const from 'this variable can always be
replaced with its initializer' in the frontend. To do that, I introduced
Declaration::isSameAsInitializer, which is overridden in VarDeclaration to
return false for constants that have a struct literal initializer.

So
{{{
const S s = S(5);
void foo() { auto ps = &s; }
// is no longer replaced by
void foo() { auto ps = &(S(5)); }
}}}

To make taking the address of a struct constant with a struct-initializer
outside of function scope possible, I made sure that AddrExp::optimize doesn't
try to run the argument's optimization with WANTinterpret - that'd again
replace the constant with a struct literal temporary.
This commit is contained in:
Christian Kamm
2009-06-14 19:49:58 +02:00
parent 8357777440
commit 723cfef519
7 changed files with 51 additions and 15 deletions

View File

@@ -1213,6 +1213,14 @@ int VarDeclaration::hasPointers()
return (!isDataseg() && type->hasPointers());
}
int VarDeclaration::isSameAsInitializer()
{
if (init && init->isExpInitializer() &&
init->isExpInitializer()->exp->op == TOKstructliteral)
return 0;
return isConst();
}
/******************************************
* If a variable has an auto destructor call, return call for it.
* Otherwise, return NULL.

View File

@@ -135,6 +135,8 @@ struct Declaration : Dsymbol
int isParameter() { return storage_class & STCparameter; }
int isDeprecated() { return storage_class & STCdeprecated; }
int isOverride() { return storage_class & STCoverride; }
virtual int isSameAsInitializer() { return isConst(); };
int isIn() { return storage_class & STCin; }
int isOut() { return storage_class & STCout; }
@@ -282,6 +284,8 @@ struct VarDeclaration : Declaration
void checkCtorConstInit();
void checkNestedReference(Scope *sc, Loc loc);
Dsymbol *toAlias();
virtual int isSameAsInitializer();
#if IN_DMD
void toObjFile(int multiobj); // compile to .obj file

View File

@@ -2113,7 +2113,7 @@ Lagain:
type = Type::terror;
}
}
if (v->isConst() && type->toBasetype()->ty != Tsarray)
if (v->isSameAsInitializer() && type->toBasetype()->ty != Tsarray)
{
if (v->init)
{
@@ -3242,13 +3242,10 @@ int StructLiteralExp::isLvalue()
}
#endif
/*
Removed in LDC. See declaration.
Expression *StructLiteralExp::toLvalue(Scope *sc, Expression *e)
{
return this;
}
*/
int StructLiteralExp::checkSideEffect(int flag)
@@ -3949,7 +3946,7 @@ Expression *VarExp::semantic(Scope *sc)
VarDeclaration *v = var->isVarDeclaration();
if (v)
{
if (v->isConst() && type->toBasetype()->ty != Tsarray && v->init)
if (v->isSameAsInitializer() && type->toBasetype()->ty != Tsarray && v->init)
{
ExpInitializer *ei = v->init->isExpInitializer();
if (ei)
@@ -5355,7 +5352,7 @@ Expression *DotIdExp::semantic(Scope *sc)
return this;
}
type = v->type;
if (v->isConst())
if (v->isSameAsInitializer())
{
if (v->init)
{
@@ -5614,7 +5611,7 @@ Expression *DotVarExp::semantic(Scope *sc)
accessCheck(loc, sc, e1, var);
VarDeclaration *v = var->isVarDeclaration();
if (v && v->isConst())
if (v && v->isSameAsInitializer())
{ ExpInitializer *ei = v->getExpInitializer();
if (ei)
{ Expression *e = ei->exp->copy();

View File

@@ -509,9 +509,7 @@ struct StructLiteralExp : Expression
void scanForNestedRef(Scope *sc);
Expression *optimize(int result);
Expression *interpret(InterState *istate);
// LDC: struct literals aren't lvalues! Taking their address can lead to
// incorrect behavior, see LDC#218, DMD#2682
// Expression *toLvalue(Scope *sc, Expression *e);
Expression *toLvalue(Scope *sc, Expression *e);
int inlineCost(InlineCostState *ics);
Expression *doInline(InlineDoState *ids);

View File

@@ -3409,7 +3409,7 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc,
v = s->isVarDeclaration();
if (v && id == Id::length)
{
if (v->isConst() && v->getExpInitializer())
if (v->isSameAsInitializer() && v->getExpInitializer())
{ e = v->getExpInitializer()->exp;
}
else
@@ -3456,7 +3456,7 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc,
if (v)
{
// It's not a type, it's an expression
if (v->isConst() && v->getExpInitializer())
if (v->isSameAsInitializer() && v->getExpInitializer())
{
ExpInitializer *ei = v->getExpInitializer();
assert(ei);
@@ -4520,7 +4520,7 @@ L1:
s = s->toAlias();
v = s->isVarDeclaration();
if (v && v->isConst() && v->type->toBasetype()->ty != Tsarray)
if (v && v->isSameAsInitializer() && v->type->toBasetype()->ty != Tsarray)
{ ExpInitializer *ei = v->getExpInitializer();
if (ei)
@@ -4932,7 +4932,7 @@ L1:
s->checkDeprecated(e->loc, sc);
s = s->toAlias();
v = s->isVarDeclaration();
if (v && v->isConst() && v->type->toBasetype()->ty != Tsarray)
if (v && v->isSameAsInitializer() && v->type->toBasetype()->ty != Tsarray)
{ ExpInitializer *ei = v->getExpInitializer();
if (ei)

View File

@@ -194,7 +194,9 @@ Expression *AddrExp::optimize(int result)
{ Expression *e;
//printf("AddrExp::optimize(result = %d) %s\n", result, toChars());
e1 = e1->optimize(result);
// never try to interpret: it could change the semantics by turning
// const p = &s; into an something like const p = &(Struct());
e1 = e1->optimize(result & ~WANTinterpret);
// Convert &*ex to ex
if (e1->op == TOKstar)
{ Expression *ex;

View File

@@ -0,0 +1,27 @@
struct S { int i; }
const S s1;
static this() { s1 = S(5); }
const S s2 = { 5 };
const S s3 = S(5);
S foo() { S t; t.i = 5; return t; }
const S s4 = foo();
const ps1 = &s1;
const ps2 = &s2;
//const ps3 = &s3; // these could be made to work
//const ps4 = &s4;
extern(C) int printf(char*,...);
void main() {
printf("%p %p\n", ps1, ps2);
printf("%p %p %p %p\n", &s1, &s2, &s3, &s4);
assert(ps1 == ps1);
assert(ps2 == ps2);
assert(&s1 == &s1);
assert(&s2 == &s2);
assert(&s3 == &s3);
assert(&s4 == &s4);
}