There appears to be a weird regression introduced in DMD 2.042 with the compile time handling of ref parameters (runtime versions of the function are unaffected). It appears that when a function taking a ref parameter passes it as a ref parameter to another function. If the first function modifies the parameter then modifications made by the second function won't be observed.
Below is a very simple test case:
import std.stdio: writeln;
void foo2(ref size_t offset) {
offset = 8;
}
void foo(ref size_t offset) {
auto okay = offset; // Reading from the ref param is okay
offset += 0; // comment this line out and it works
foo2(offset);
}
size_t bar() {
size_t offset = 0;
foo(offset);
return offset;
}
template foobar() { immutable foobar = bar; }
void main(string[] args) {
writeln(foobar!(),'\t', bar ); // prints 0 8 instead of 8 8
}
Comment #1 by clugdbug — 2010-03-26T12:09:15Z
Reduced test case for test suite.
--------
void bug4004a(ref int a) {
assert(a==7);
a+=3;
}
void bug4004b(ref int b) {
b= 7;
bug4004a(b);
}
int bug4004c() {
int offset = 5;
bug4004b(offset);
return offset;
}
static assert(bug4004c()==10);
-------------------------
PATCH:
Index: interpret.c
===================================================================
--- interpret.c (revision 420)
+++ interpret.c (working copy)
@@ -53,7 +53,7 @@
Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclaration *fd);
ArrayLiteralExp *createBlockDuplicatedArrayLiteral(Type *type, Expression *elem, size_t dim);
-Expression * resolveReferences(Expression *e, Expression *thisval);
+Expression * resolveReferences(Expression *e, Expression *thisval, bool *isReference = NULL);
/*************************************
* Attempt to interpret a function given the arguments.
@@ -1107,8 +1107,11 @@
// -------------------------------------------------------------
// The variable used in a dotvar, index, or slice expression,
// after 'out', 'ref', and 'this' have been removed.
-Expression * resolveReferences(Expression *e, Expression *thisval)
+// *isReference will be set to true if a reference was removed.
+Expression * resolveReferences(Expression *e, Expression *thisval, bool *isReference /*=NULL */)
{
+ if (isReference)
+ *isReference = false;
for(;;)
{
if (e->op == TOKthis)
@@ -1131,6 +1134,8 @@
VarExp *ve2 = (VarExp *)v->value;
if (!ve2->var->isSymbolDeclaration())
{
+ if (isReference)
+ *isReference = true;
e = v->value;
continue;
}
@@ -2087,7 +2092,8 @@
v->value = e2;
return e2;
}
- e1 = resolveReferences(e1, istate->localThis);
+ bool destinationIsReference = false;
+ e1 = resolveReferences(e1, istate->localThis, &destinationIsReference);
// Unless we have a simple var assignment, we're
// only modifying part of the variable.
@@ -2167,7 +2174,8 @@
{
VarExp *ve = (VarExp *)e1;
VarDeclaration *v = ve->var->isVarDeclaration();
- addVarToInterstate(istate, v);
+ if (!destinationIsReference)
+ addVarToInterstate(istate, v);
v->value = newval;
}
else if (e1->op == TOKindex)