Bug 3569 – DMD Stack Overflow with a struct member function inside a C-style struct initializer
Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
Other
OS
Windows
Creation time
2009-12-03T13:45:00Z
Last change time
2015-06-09T01:27:02Z
Keywords
ice-on-valid-code, patch
Assigned to
nobody
Creator
sandford
Comments
Comment #0 by sandford — 2009-12-03T13:45:02Z
This is a regression between DMD 2.035 and DMD 2.036. Here is a simplified test case:
struct Foo {
Foo bar() { return this; }
}
struct Bar {
Foo foo;
Bar opCom() {
Bar r = { foo.bar() }; // Error caused by this line
return r;
}
}
The simple work around is to use D style constructors: Bar r = ( foo.bar() ); or to set the fields manually. However, tracking these down is very difficult because there is no line number nor easy find/replace.
Comment #1 by sandford — 2009-12-03T13:54:14Z
The cause of the stack overflow may be due to the C style initializers being incorrectly (correctly?) evaluated at compile time:
i.e.:
struct Foo {
float opMul(float b) { return b; }
}
struct Bar {
Foo foo;
Bar opMul(float b) {
Bar r = {foo*b};
return r;
}
}
generates the errors:
Error: variable b is used before initialization
Error: cannot evaluate this.foo.opMul(b) at compile time
Error: cannot implicitly convert expression (this.foo.opMul(b)) of type float to Foo
Error: cannot evaluate this.foo.opMul(b) at compile time
Comment #2 by clugdbug — 2009-12-29T12:32:09Z
This patch to AssertExp::interpret() prevents the stack overflow, turning it into a simple error message. It doesn't patch the regression.
As Rob notes, the static struct initializers are evaluated at compile time, but they shouldn't be. Nonetheless, this patch is still required to prevent segfaults in the case where they are forcibly evaluated at compile time. Eg, code like the following:
struct Foo {
Foo bar() { return this; }
}
struct Bar {
Foo foo;
int fog() {
enum Bar r = { foo.bar() };
return 3;
}
}
PATCH -----------------------
Index: interpret.c
===================================================================
--- interpret.c (revision 318)
+++ interpret.c (working copy)
@@ -2535,14 +2535,18 @@
if( this->e1->op == TOKaddress)
{ // Special case: deal with compiler-inserted assert(&this, "null this")
AddrExp *ade = (AddrExp *)this->e1;
- if(ade->e1->op == TOKthis && istate->localThis)
- return istate->localThis->interpret(istate);
+ if(ade->e1->op == TOKthis && istate->localThis)
+ if (istate->localThis->op==TOKdotvar
+ && ((DotVarExp *)(istate->localThis))->e1->op==TOKthis)
+ return getVarExp(loc, istate, ((DotVarExp *)(istate->localThis))->var);
+ else
+ return istate->localThis->interpret(istate);
}
-if (this->e1->op == TOKthis)
-{
+ if (this->e1->op == TOKthis)
+ {
if(istate->localThis)
- return istate->localThis->interpret(istate);
-}
+ return istate->localThis->interpret(istate);
+ }
e1 = this->e1->interpret(istate);
if (e1 == EXP_CANT_INTERPRET)
goto Lcant;
Comment #3 by clugdbug — 2010-01-22T00:09:28Z
Svn commit 337 gets rid of the stack overflow, and turns it into a rejects-valid bug.
Comment #4 by clugdbug — 2010-01-31T11:57:01Z
This is downgraded from ice-on-valid-code to rejects-valid in DMD2.040.
Comment #5 by clugdbug — 2010-06-07T06:59:26Z
This is causing a stack overflow again. I don't know why I thought it was fixed.
Comment #6 by clugdbug — 2010-06-07T13:24:35Z
The fact that struct initializers are evaluated at compile time is bug 3809; only the stack overflow is unique to this bug.
Some tough cases for the test suite.
---
template Compileable(int z) { bool OK;}
struct Bug3569 {
int bar() { return 7; }
}
struct Bug3569b {
Bug3569 foo;
void crash() {
static assert(!is(typeof(Compileable!(foo.bar()))));
static assert(!is(typeof(Compileable!((foo = Bug3569.init).bar()))));
}
}
========
PATCH
Index: interpret.c
===================================================================
--- interpret.c (revision 524)
+++ interpret.c (working copy)
@@ -1110,8 +1110,9 @@
Expression *ThisExp::interpret(InterState *istate)
{
- if (istate->localThis)
+ if (istate && istate->localThis)
return istate->localThis->interpret(istate);
+ error("value of 'this' is not known at compile time");
return EXP_CANT_INTERPRET;
}
@@ -2105,6 +2106,11 @@
#endif
Expression *e = EXP_CANT_INTERPRET;
Expression *e1 = this->e1;
+ if (!istate)
+ {
+ error("value of %s is not known at compile time", e1->toChars());
+ return e;
+ }
if (fp)
{