struct OpApply {
int opApply(scope int delegate(int) dlg) {
return dlg(1);
}
}
struct Foo {
int i;
this(int x) {
foreach(_; OpApply()) {
i = 0; // Error: couldn't find field i of type int in OpApply()
}
}
}
enum x = Foo(1);
(also happens with this.i = 0).
Comment #1 by bugzilla — 2017-05-12T16:33:48Z
The same error:
test.d(10): Error: couldn't find field i of type int in OpApply()
test.d(3): called from here: dlg(1)
test.d(9): called from here: OpApply().opApply(delegate int(int _) => 0)
test.d(14): called from here: Foo(0).this(1)
happens when `enum` is replaced with `auto` so it is not specific to CTFE.
Comment #2 by ag0aep6g — 2017-05-12T18:53:38Z
(In reply to Walter Bright from comment #1)
> The same error:
[...]
> happens when `enum` is replaced with `auto` so it is not specific to CTFE.
I think you're mistaken. If you leave x at the module level, it's still initialized statically, i.e. CTFE is involved. The error goes away when x is a function-local variable.
Comment #3 by bugzilla — 2017-05-12T21:32:04Z
(In reply to ag0aep6g from comment #2)
> I think you're mistaken.
You're right. It is a CTFE problem.
Comment #4 by bugzilla — 2017-05-12T22:07:33Z
The following simpler code reproduces the problem:
------------------------
struct OpApply {
void opApply(void delegate() dlg) {
dlg();
}
}
struct Foo {
int i;
int abc() {
auto o = OpApply();
void dg() { i = 0; } // couldn't find field i of type int in OpApply()
o.opApply(&dg);
return 0;
}
}
void bar() {
enum x = Foo().abc();
}
---------------------------
This code fails in a similar manner, and also works outside of CTFE (replace enum with auto and it compiles):
---------------------------
void opApply(void delegate() dlg) { dlg(); }
struct Foo {
int i;
int abc() {
void dg() { i = 0; } // value of 'this' is not known at compile time
opApply(&dg);
return 0;
}
}
void bar() {
enum x = Foo().abc();
}
-------------------------
This stuff is pretty deep in the bowels of CTFE, which I don't really understand :-(
Comment #5 by uplink.coder — 2017-05-19T09:31:22Z
We generally have no support for closures at ctfe.
Comment #6 by uplink.coder — 2017-05-19T11:38:19Z
I figured out what is happing.
The delegate support code in the ctfe engine takes the parents scope to try and extract a thisPtr.
in the case of walters example
struct Foo {
int i;
int abc() {
void dg() { i = 0; } // value of 'this' is not known at compile time
// dg's direct parent is the function abc, which
// does not have a this (because it's a function)
opApply(&dg);
return 0;
}
}
void bar() {
enum x = Foo().abc();
}
I am working on a solution.
It should work in a couple of days.
Comment #7 by bugzilla — 2017-05-19T17:16:02Z
Don Clugston writes about this problem:
I bet the problem is around dinterpret.d line 4966.
---
else if (ecall.op == TOKdelegate)
{
// Calling a delegate
fd = (cast(DelegateExp)ecall).func;
pthis = (cast(DelegateExp)ecall).e1;
// Special handling for: &nestedfunc --> DelegateExp(VarExp(nestedfunc), nestedfunc)
if (pthis.op == TOKvar && (cast(VarExp)pthis).var == fd)
pthis = null; // context is not necessary for CTFE
---
"Context is not necessary for CTFE".
I doubt very much that that is true.
In the code review I saw that the PR changed the CTFE to use dynamic scoping lookup instead of lexical scoping so after much IRC back and forth I eventually managed to distill the problematic example to:
@safe:
unittest {
struct Foo {
@safe:
int i;
int whoami() { return i; }
int call(int delegate() @safe dlg) {
return dlg();
}
}
int func() {
auto foo1 = Foo(1);
auto foo2 = Foo(2);
assert(1 == foo2.call(&foo1.whoami));
return 0;
}
enum F = func();
}
Comment #10 by eyal — 2017-05-24T00:04:00Z
Sorry, this is the actual example that showed the dynamic scoping lookup in the PR is incorrect:
@safe:
unittest {
enum Caller { OpApply, Other }
struct OpApply {
@safe:
int delegate(Caller) @safe myDlg;
int opApply(int delegate(Caller) @safe dlg) {
myDlg = dlg;
return dlg(Caller.OpApply);
}
}
struct Foo {
@safe:
OpApply o;
int i;
this(int x) {
i = x;
o = OpApply();
foreach(caller; o) {
final switch(caller) {
case Caller.OpApply:
if(i == 1) {
auto foo2 = Foo(2);
assert(2 == foo2.call(o.myDlg));
assert(i == 0);
}
break;
case Caller.Other:
i = 0;
break;
}
}
}
int call(int delegate(Caller) @safe dlg) {
dlg(Caller.Other);
return i;
}
}
Foo(1);
}
The distilled one somehow
Comment #11 by robert.schadek — 2024-12-13T18:48:59Z