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