changeset 1499:df11cdec45a2

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.
author Christian Kamm <kamm incasoftware de>
date Sun, 14 Jun 2009 19:49:58 +0200
parents 899a928ac905
children c3c46399bcf1
files dmd/declaration.c dmd/declaration.h dmd/expression.c dmd/expression.h dmd/mtype.c dmd/optimize.c tests/mini/conststructliteral.d
diffstat 7 files changed, 51 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- 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.
--- 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
--- 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();
--- 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);
--- 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)
--- 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;
--- /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);
+}
+