From 723cfef51962ad45812f37998ca86b26f57e2d19 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Sun, 14 Jun 2009 19:49:58 +0200 Subject: [PATCH] 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. --- dmd/declaration.c | 8 ++++++++ dmd/declaration.h | 4 ++++ dmd/expression.c | 11 ++++------- dmd/expression.h | 4 +--- dmd/mtype.c | 8 ++++---- dmd/optimize.c | 4 +++- tests/mini/conststructliteral.d | 27 +++++++++++++++++++++++++++ 7 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 tests/mini/conststructliteral.d diff --git a/dmd/declaration.c b/dmd/declaration.c index 09754bb4..5b1e9d40 100644 --- a/dmd/declaration.c +++ b/dmd/declaration.c @@ -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. diff --git a/dmd/declaration.h b/dmd/declaration.h index 495d9924..731f4080 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -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 diff --git a/dmd/expression.c b/dmd/expression.c index 0f9cca97..dc9ccbbd 100644 --- a/dmd/expression.c +++ b/dmd/expression.c @@ -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(); diff --git a/dmd/expression.h b/dmd/expression.h index ac6d129f..8fa6b7a8 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -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); diff --git a/dmd/mtype.c b/dmd/mtype.c index 124bd479..dc048ec6 100644 --- a/dmd/mtype.c +++ b/dmd/mtype.c @@ -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) diff --git a/dmd/optimize.c b/dmd/optimize.c index e7791eb4..6ae9315c 100644 --- a/dmd/optimize.c +++ b/dmd/optimize.c @@ -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; diff --git a/tests/mini/conststructliteral.d b/tests/mini/conststructliteral.d new file mode 100644 index 00000000..353f9042 --- /dev/null +++ b/tests/mini/conststructliteral.d @@ -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); +} +