February 10, 2021
https://issues.dlang.org/show_bug.cgi?id=1983

--- Comment #28 from Bolpat <qs.il.paperinik@gmail.com> ---
(In reply to timon.gehr from comment #27)
It took me some time to think about and come to something like conclusion. Some
parts may look like we [Timon Gehr and me] agree, but I'm not entirely sure we
are. My mental model of a delegate is

    struct DG(R, Args...)
    {
        alias Context = void*;
        alias FP = R function(Context, Args);
        FP fp;
        Context context;

        this(FP fp, Context context)
        {
            this.fp = fp;
            this.context = context;
        }

        R opCall(Args args)
        {
            return fp(context, args);
        }
    }

too. However, I make myself aware that this is an implementation detail and in principle, other implementations are possible where the context pointer is not actually part of the delegate object but externalized to a global associative array. One would be this:

    struct DG(R, Args...)
    {
        alias Context = void*;
        alias FP = R function(Context, Args);

        FP fp;
        static Context[FP] contexts;

        this(FP fp, Context context)
        {
            this.fp = fp;
            contexts[fp] = context;
        }
        ~this()
        {
            // contexts.remove(fp);
        }

        R opCall(Args args)
        {
            return fp(contexts[fp], args);
        }

As someone in the forums in the discussion of `const` and `immutable` pointed out, stuff that is conceptually part of an object need not literally be part of that object. If `DG` is a delegate type, having `const(DG)` mean the context must not be mutated by a call to that delegate is a perception that comes from seeing a delegate as a functionPtr-context pair.

Thanks to your [Timon Gehr's] explanations (the formal stuff wasn't so easy to comprehend, to be honest), I now do not see annotations for how the delegate can do things with its context as a problem now. I see immutable contexts as an absolute win: A pure delegate makes almost no practically useful guarantees, but a pure immutable one does.

Now I feel the need to invent terminology:
A const/immutable/... [annotated] delegate has those restrictions on how its
context pointer is affected by it.
A const/immutable/... (type|qualified) delegate cannot be assigned.

I will use "type" or "qualified" always, but "annotated" only for emphasis. The
annotations are of the same sort as `pure` and `nothrow` and describe the
behavior of the function pointer with respect to the value. Type qualifiers
describe the variable.
As an example, an `immutable` annotated delegate is

    alias DGa = void delegate(int) immutable

and a ´const` qualified delegate is

    alias DGb = const(void delegate(int))

And those annotations have nothing to do with each other. They mean orthogonal things. A DGa object can be reassigned and calling a DGb object can mutate stuff through its context.

Following the example class, I don't think a `const` method should be barred from calling a mutable annotated delegate on the same reasoning it isn't barred from calling mutable methods of other classes. Most likely, the delegate's context has nothing to do with the class. (We made it to prove a point.) Barring a `const` method of a class to only call a `const` annotated member delegate is a major restriction.

In the example, we had a `pure` constructor that assigns the address of a mutable method to a mutable delegate member. This is valid, obviously, it's an exact type match. Using the second model of handling contexts with an external AA, it proves that a reference is leaked. That leaked reference invalidates uniqueness. That means the implicit cast to immutable is invalid, too. However, the result is unique when the delegate were annotated `immutable` (or `const`?). Then, the assignment of a MUTABLE member function wouldn't be valid.

If we tried to replace the pure constructor by two (or three) ones for explicit mutable and immutable (and const) construction, it becomes more obvious that if an aggregate type contains a mutable annotated (i.e. not `const` or `immutable` or `inout` annotated) delegate, even the result of a `pure` constructor cannot be assumed to yield unique values.

I don't think that an `immutable` (or `const`) delegate type variable should
extend the qualification down to its context as it would be a breaking change.
It would have to if your model is the function-ptr--context pair, but I think
this model is misleading. The qualification of the delegate object should only
affect assignment of the function ptr and the context, but not references
reachable through that context. If it were, a delegate with a context that
binds non-`immutable` values cannot be assigned to an `immutable` qualified
delegate type variable.
The annotation of a delegate should only affect the signature of the function
pointer: The context parameter is mutable, const or immutable depending on the
annotation. It tells what the function pointer does, not what the context is.

--
February 10, 2021
https://issues.dlang.org/show_bug.cgi?id=1983

--- Comment #29 from ZombineDev <petar.p.kirov@gmail.com> ---
alias Context = void*;
................^^^^^

This is where the problem begins and why I think many fail to recognize the type system holes. `void*` is just an implementation detail. It really shouldn't be part of anyone's mental model **as far the type system is concerned**. Most importantly, the front-end shouldn't even know that the context pointer is represented as void*. Instead, it should follow the type system rules, as if the context pointer is correctly specified. For example:

With delegates to struct and class non-static member functions it should be fairly obvious what is the right type:
---
struct S { int x; char[] cx; ref char foo(); }
S s;
auto dg = &s.foo; // Context = S*;

immutable S s;
auto dg2 = &s.foo; // Context = immutable(S)*;
---

With nested functions is where most people get confused:

---
auto test()
{
    int x;
    char[] cx;
    ref char nested() // Context = (struct { int x; char[] cx; })*;
    { /* ... */ }
    return &nested;
}
---

--
February 14, 2021
https://issues.dlang.org/show_bug.cgi?id=1983

--- Comment #30 from timon.gehr@gmx.ch ---
(In reply to Bolpat from comment #28)
> However, I make myself aware that this is an implementation detail and in principle, other implementations are possible where the context pointer is not actually part of the delegate object but externalized to a global associative array.

Sorry, but this falls flat on its face because a `pure` function cannot access a mutable global associative array. The context pointer is indeed part of the delegate.

--
February 23, 2021
https://issues.dlang.org/show_bug.cgi?id=1983

--- Comment #31 from Bolpat <qs.il.paperinik@gmail.com> ---
(In reply to timon.gehr from comment #30)
> (In reply to Bolpat from comment #28)
> > However, I make myself aware that this is an implementation detail and in principle, other implementations are possible where the context pointer is not actually part of the delegate object but externalized to a global associative array.
> 
> Sorry, but this falls flat on its face because a `pure` function cannot access a mutable global associative array. The context pointer is indeed part of the delegate.

My AA externalization code was to convey an idea, not to propose an implementation. It was to help people visualize how contexts could be not part of  the delegate object. I was well aware about that limitation.

--
November 05, 2021
https://issues.dlang.org/show_bug.cgi?id=1983

--- Comment #32 from moonlightsentinel@disroot.org ---
*** Issue 11043 has been marked as a duplicate of this issue. ***

--
May 24, 2022
https://issues.dlang.org/show_bug.cgi?id=1983

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |safe
                 CC|                            |bugzilla@digitalmars.com

--
May 24, 2022
https://issues.dlang.org/show_bug.cgi?id=1983

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|---                         |FIXED

--- Comment #33 from Walter Bright <bugzilla@digitalmars.com> ---
Changing writefln to writeln, let's try the opening example today:

---
test.d(19): Error: mutable method `test.A.x` is not callable using a `const`
`a`
---

where test.d(19) is the line:

    (&a.x)(2);

So the bug has been fixed at some point.

--
May 24, 2022
https://issues.dlang.org/show_bug.cgi?id=1983

timon.gehr@gmx.ch changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|FIXED                       |---

--- Comment #34 from timon.gehr@gmx.ch ---
This has not been fixed, even though the original example no longer compiles.

--
May 24, 2022
https://issues.dlang.org/show_bug.cgi?id=1983

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |RESOLVED
         Resolution|---                         |WORKSFORME

--- Comment #35 from Walter Bright <bugzilla@digitalmars.com> ---
Separate issues should get their own bugzilla entries. The issue that started this thread is fixed.

--
1 2 3
Next ›   Last »