Thread overview
What should delegates with qualifiers mean?
Mar 24, 2021
Q. Schroll
Mar 24, 2021
Paul Backus
Mar 24, 2021
Timon Gehr
Mar 25, 2021
Timon Gehr
Mar 25, 2021
Timon Gehr
Mar 25, 2021
Nicholas Wilson
March 24, 2021
As per the grammar, a delegate can carry member function attributes, including qualifiers (const, immutable) that seemingly apply to the context. The spec does not speak about this.

We should give this meaning!

The best way to approach this mentally is through dg = &obj.method because the version where the delegate is from binding variables is more difficult.

If obj is const or immutable, &obj.method should yield a delegate(...) const or delegate(...) immutable. Intuitively,
* const means that the function pointed to will not change the object.
* immutable means that the context cannot change (implying that that the function pointed to will not change the object).
* mutable, i.e. no annotation, means that the context could change by calling the pointed-to function or through other actions.

While everywhere else, immutable is a requirement and a guarantee, here it is a guarantee only. That is because the context object cannot be reassigned independently of the function pointer. Therefore, a `delegate() immutable` is convertible to a `delegate() const` delegate which in turn is convertible to a `delegate()`.

One benefit of fixing this is that pure immutable delegates are referentially transparent while pure const ones almost aren't and pure mutable ones carry almost no guarantees.

For delegates that come from binding local values, if all those values are immutable, the delegate can be inferred immutable; if all those values are const, the delegate can be inferred const.

I had a conversation with Timon Gehr about this and I think this needs a little more attention.
March 24, 2021
On Wednesday, 24 March 2021 at 19:23:15 UTC, Q. Schroll wrote:
> While everywhere else, immutable is a requirement and a guarantee, here it is a guarantee only. That is because the context object cannot be reassigned independently of the function pointer. Therefore, a `delegate() immutable` is convertible to a `delegate() const` delegate which in turn is convertible to a `delegate()`.

No, this is unsound, because a delegate may return a pointer or reference to its context. Consider:

struct S
{
    int n;
    int* getPtr() { return &n; }
}

void main() @safe
{
    immutable S s = { 123 };
    // implicitly convert from `delegate() immutable` to `delegate()`
    int* delegate() dg = &s.getPtr;
    // undefined behavior
    *dg() = 456;
}
March 24, 2021
On 24.03.21 22:56, Paul Backus wrote:
> struct S
> {
>      int n;
>      int* getPtr() { return &n; }
> }
> 
> void main() @safe
> {
>      immutable S s = { 123 };
>      // implicitly convert from `delegate() immutable` to `delegate()`
>      int* delegate() dg = &s.getPtr;
>      // undefined behavior
>      *dg() = 456;
> }


This is not what was explained in the OP. Removing context qualifiers from delegates is sound. You are calling a mutable method on an immutable object. Not the same thing, and obviously unsound.

Why is removing qualifiers sound? delegate is an existential type:

B delegate(A)q ≡ ∃C. (A×q(C)*)→B

Therefore,

B delegate(A)immutable ≡ ∃C. (A×immutable(C)*)→B ⊆ ∃C'. (A×C'*)→B,

where we have chosen C' as immutable(C).
March 25, 2021
On Wednesday, 24 March 2021 at 19:23:15 UTC, Q. Schroll wrote:
> As per the grammar, a delegate can carry member function attributes, including qualifiers (const, immutable) that seemingly apply to the context. The spec does not speak about this.

don't forget shared! see
https://issues.dlang.org/show_bug.cgi?id=21737
https://issues.dlang.org/show_bug.cgi?id=19984


March 25, 2021
On 24.03.21 23:18, Timon Gehr wrote:
> 
> Why is removing qualifiers sound? delegate is an existential type:
> 
> B delegate(A)q ≡ ∃C. (A×q(C)*)→B

Oops. Typo. This should of course have been:

Why is removing qualifiers sound? delegate is an existential type:

B delegate(A)q ≡ ∃C. (A×q(C)*)×q(C)*→B

Therefore,

B delegate(A)immutable ≡ ∃C. (A×immutable(C)*)×immutable(C)*→B ⊆ ∃C'. (A×C'*)×C'*→B,

where we have chosen C' as immutable(C).
March 25, 2021
On 25.03.21 02:01, Timon Gehr wrote:
> Why is removing qualifiers sound? delegate is an existential type:
> 
> B delegate(A)q ≡ ∃C. (A×q(C)*)×q(C)*→B
> 
> ...
x). This is getting a bit embarrassing. Third try:

Why is removing qualifiers sound? delegate is an existential type:

B delegate(A)q ≡ ∃C. (A×q(C)*→B)×q(C)*

Therefore,

B delegate(A)immutable ≡ ∃C. (A×immutable(C)*→B)×immutable(C)* ⊆ ∃C'. (A×C'*→B)×C'*,

where we have chosen C' as immutable(C).