Uncallable delegates

Walter Bright newshound2 at digitalmars.com
Tue May 12 04:29:12 UTC 2026


On 4/1/2026 7:29 AM, Dukc wrote:
> It can be found 
> [here](https://gist.github.com/dukc/0ede11a0fcb77b2ff92654ceaa7805e2), and is 
> awaiting your feedback.

Thank you for your hard work on this. It's an important thing to get right.

The difficulty I have with it is I do not understand it. It has to be broken 
down into simpler constructs, then built back up into the delegate. How 
delegates behave is inevitable, it is something that we either get right or get 
severely wrong.

** Implicit Conversions

Let's start with implicit conversions:

```
int* p;
const(int)* q;
p = q; // invalid
q = p; // valid (contravariance)
```
What is important here is all of the type qualifiers add a *restriction*. None 
of the the type qualifiers *loosen* it. So the implicit conversions only go one 
way, like a diode.

** Class Inheritance

The next concept is covariance:

```
class A {
     void foo(int*);
     void bar(const(int)*);
}
class B : A {
     void foo(const(int)*); // ok - added restriction, covariant
     void bar(int*); // error, removed restriction
}
```
Since B.foo overrides A.foo, it must adhere to the (int*) constraint, and since 
nothing says A.foo must modify through the parameter, overriding it with B.foo 
that promises to not change it, still fulfills the (int*).B.bar() is an error 
because it does not fulfill the promise of A.bar() that the argument won't be 
modified. This is called covariance.

Next, contravariance:
```
class A {
     int* foo();
     const(int)* bar();
}
class B {
     const(int)* foo(); // error, as the return value gets interpreted as mutable
     int* bar(); // ok, as the return result can be implicitly cast to const
}
```
This property is called contra-variance.

** Function Pointers

```
int* function() foop;
const(int)* function() barp;
barp = foop; // ok, contravariant, foop()'s return is cast to const(int)*
foop = barp; // error, barp's return is cast to mutable
```


** Delegates

Delegates are a function pointer with an additional (hidden) parameter.

```
int* delegate() foodg;
```
For illustration purposes, it can be rewritten as a function pointer with the 
additional (hidden) parameter explicit, let's call it `this`:
```
int* function(const(int)* this) foodg;
```

Apply the rules for return types for function pointers, and the covariant rules 
for the `this` parameter, and the behavior of delegates is completely derived 
from the earlier rules. We are not designing anything new.

*** Type Qualifiers for Delegates

```
const T* delegate() dg; // the const applies to the function pointer
const(T)* delegate() dg; // the const(T)* is the type of the return value
void delegate() const dg; // the const is applied to the `this` parameter
```

It is not necessary for the DIP to address any storage classes or attributes 
other than simply use `pure` for storage class and `const` for attribute. The 
behavior of the other storage classes (like nothrow, @nogc) is the same. The 
same for other attributes (immutable, @safe, nothrow, etc.)

Feel free to use any or all of this in your DIP.


More information about the dip.development mailing list