mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-11 18:33:14 +01:00
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:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
27
tests/mini/conststructliteral.d
Normal file
27
tests/mini/conststructliteral.d
Normal 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user