# HG changeset patch # User Christian Kamm # Date 1245001798 -7200 # Node ID df11cdec45a24e429eddb5d0fd06d16c2d327d8a # Parent 899a928ac9056953bfec91ff370f8179696ebd85 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. diff -r 899a928ac905 -r df11cdec45a2 dmd/declaration.c --- a/dmd/declaration.c Sun Jun 14 14:28:11 2009 +0200 +++ b/dmd/declaration.c Sun Jun 14 19:49:58 2009 +0200 @@ -1213,6 +1213,14 @@ 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. diff -r 899a928ac905 -r df11cdec45a2 dmd/declaration.h --- a/dmd/declaration.h Sun Jun 14 14:28:11 2009 +0200 +++ b/dmd/declaration.h Sun Jun 14 19:49:58 2009 +0200 @@ -135,6 +135,8 @@ 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 @@ void checkCtorConstInit(); void checkNestedReference(Scope *sc, Loc loc); Dsymbol *toAlias(); + + virtual int isSameAsInitializer(); #if IN_DMD void toObjFile(int multiobj); // compile to .obj file diff -r 899a928ac905 -r df11cdec45a2 dmd/expression.c --- a/dmd/expression.c Sun Jun 14 14:28:11 2009 +0200 +++ b/dmd/expression.c Sun Jun 14 19:49:58 2009 +0200 @@ -2113,7 +2113,7 @@ type = Type::terror; } } - if (v->isConst() && type->toBasetype()->ty != Tsarray) + if (v->isSameAsInitializer() && type->toBasetype()->ty != Tsarray) { if (v->init) { @@ -3242,13 +3242,10 @@ } #endif -/* -Removed in LDC. See declaration. Expression *StructLiteralExp::toLvalue(Scope *sc, Expression *e) { return this; } -*/ int StructLiteralExp::checkSideEffect(int flag) @@ -3949,7 +3946,7 @@ 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 @@ return this; } type = v->type; - if (v->isConst()) + if (v->isSameAsInitializer()) { if (v->init) { @@ -5614,7 +5611,7 @@ 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(); diff -r 899a928ac905 -r df11cdec45a2 dmd/expression.h --- a/dmd/expression.h Sun Jun 14 14:28:11 2009 +0200 +++ b/dmd/expression.h Sun Jun 14 19:49:58 2009 +0200 @@ -509,9 +509,7 @@ 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); diff -r 899a928ac905 -r df11cdec45a2 dmd/mtype.c --- a/dmd/mtype.c Sun Jun 14 14:28:11 2009 +0200 +++ b/dmd/mtype.c Sun Jun 14 19:49:58 2009 +0200 @@ -3409,7 +3409,7 @@ 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 @@ 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 @@ 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 @@ 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) diff -r 899a928ac905 -r df11cdec45a2 dmd/optimize.c --- a/dmd/optimize.c Sun Jun 14 14:28:11 2009 +0200 +++ b/dmd/optimize.c Sun Jun 14 19:49:58 2009 +0200 @@ -194,7 +194,9 @@ { 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; diff -r 899a928ac905 -r df11cdec45a2 tests/mini/conststructliteral.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/mini/conststructliteral.d Sun Jun 14 19:49:58 2009 +0200 @@ -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); +} +