Uncallable delegates
Dukc
ajieskola at gmail.com
Wed May 13 07:32:49 UTC 2026
On Tuesday, 12 May 2026 at 23:18:30 UTC, Walter Bright wrote:
> The same applies to every other function attribute. This is not
> a design decision, it's an inevitable deriviation from the
> other rules of the language.
So is that some delegates must be uncallable. I'll demonstrate,
starting with function pointers and avoiding bringing
`@safe`/`pure` or templates to table this time. Let's rewrite a
very simple delegate, `void delegate(int)`, to a function pointer
pair.
```D
struct ExampleDel
{ void function(int, void*) fPtr;
void* context;
this(void function(int, void*) fPtr, void* context)
{ this.fPtr = fPtr;
this.context = context;
}
void opCall(int arg) => fPtr(arg, context);
}
struct S
{ string field;
static void memberFun(int arg, void* _this)
{ import std.stdio;
writeln((*cast(typeof(this)*) _this).field, arg);
}
}
void main()
{ auto s = S("val = ");
auto del = ExampleDel(&s.memberFun, &s);
del(24); // val = 24
}
```
Now, what happens if we try to make `del` `const`, making it the
equivalent of `const(void delegate(int))`?
```D
void main()
{ auto s = S("val = ");
const del = ExampleDel(&s.memberFun, &s);
del(24);
}
```
It doesn't compile:
```
Error: mutable method `app.ExampleDel.opCall` is not callable
using a `const` object
del(24);
```
Neither can we qualify `opCall` as `const`:
```
Error: function pointer `this.fPtr` is not callable using
argument types `(int, const(void*))`
void opCall(int arg) const => fPtr(arg, context);
^
cannot pass argument `this.context` of type `const(void*)`
to parameter `void*
```
These errors are not bugs. If you use the call operator of a
`const` struct, of course the struct has to actually support
that. And `context` getting typed as `const(void*)` once `opCall`
has a `const` context is `const` transitivity working as intended.
We can conclude that calling `const(void delegate(int))` is
fundamentally unsound. It can't be made to work without type
system breaking casts.
Note that this does not apply to `const(void delegate(int)
const)`. The rewrite works:
```D
struct ExampleDel
{ void function(int, const(void*)) fPtr;
void* context;
this(void function(int, const(void*)) fPtr, void* context)
{ this.fPtr = fPtr;
this.context = context;
}
void opCall(int arg) const => fPtr(arg, context);
}
struct S
{ string field;
static void memberFun(int arg, const(void*) _this)
{ import std.stdio;
writeln((*cast(typeof(this)*) _this).field, arg);
}
}
void main()
{ auto s = S("val = ");
const del = ExampleDel(&s.memberFun, &s);
del(24); // val = 24
}
```
More information about the dip.development
mailing list