CTFE: wrong code with delegates, recursion
Probably two symptoms of the same cause:
A) infinite loop
---
cat > test_a.d <<code
void main() {
assert(go() == 1); // passes
enum e = go(); // infinite loop
}
int go() {
int result;
S().into((s) {result = s;});
return result;
}
struct S {
void into(void delegate(int) sink) {
put(sink, R(true));
}
}
struct R {
bool set;
void into(void delegate(int) sink) {
if(set) put(sink, R(false));
else sink(1);
}
}
// a lengthy way to write r.into(sink)
void put(void delegate(int) sink, R r) {
r.into((s) {sink(s);});
}
code
dmd test_a.d
---
test_a.d(24): Error: delegate test_a.put.__lambda4!(int).__lambda4 CTFE recursion limit exceeded
test_a.d(24): called from here: sink(s)
test_a.d(24): 994 recursive calls to function __lambda4
test_a.d(19): called from here: sink(1)
test_a.d(24): called from here: r.into(delegate void(int s)
{
sink(s);
}
)
test_a.d(18): called from here: put(sink, R(false))
test_a.d(24): called from here: r.into(delegate void(int s)
{
sink(s);
}
)
test_a.d(12): called from here: put(sink, R(true))
test_a.d(7): called from here: S().into(delegate void(int s)
{
result = s;
}
)
test_a.d(3): called from here: go()
---
B) variable of enclosing scope doesn't get set
---
cat > test_b.d <<code
void main() {
assert(go() == 1); // passes
enum e = go();
}
int go() {
int result;
R(true).into((s) {result = s;});
return result;
}
struct R {
bool set;
void into(void delegate(int) sink) {
int result = -1;
if(set) {
R(false).into((s) {
result = s;
assert(result == 1); // passes
});
assert(result == 1); // fails in CTFE
sink(result);
} else sink(1);
}
}
code
dmd test_b.d
---
test_b.d(19): Error: assert(result == 1) failed
test_b.d(7): called from here: R(true).into(delegate void(int s)
{
result = s;
}
)
test_b.d(3): called from here: go()
---
Comment #1 by nilsbossung — 2013-02-20T16:03:31Z
Yup, they both boil down to this:
---
cat > test.d <<code
enum e = (){
into(null, 42);
return true;
}();
void into(void delegate() sink, int set) {
if(set == 42) into(() {assert(set != 13);}, 13);
else sink();
}
code
dmd -c test.d
---
test.d(6): Error: assert(set != 13) failed
---
(output truncated)
Apparently, the delegate sees the wrong variables.
Comment #2 by clugdbug — 2013-02-27T00:45:20Z
A slight reduction, which removes the delegate literal with a nested function.
==================
bool into(void delegate() sink, int set) {
void xxx() {
assert(set != 13);
}
if(set == 42)
into(&xxx, 13);
else
sink();
return true;
}
static assert(into(null, 42));
==================
This may be quite difficult to fix, since it has exposed a design flaw in the current CTFE implementation.
Comment #3 by robert.schadek — 2024-12-13T18:04:10Z