https://issues.dlang.org/show_bug.cgi?id=1983
--- Comment #27 from timon.g...@gmx.ch --- (In reply to Bolpat from comment #26) > (In reply to timon.gehr from comment #25) > > > It's even hard to pin-point which exact part of the code should be an > > > error. > > > > The problem is that you shouldn't be able to call "dg" in "kaboom", as its > > type is `const(void delegate()pure)`. It would have to be at least something > > like `const(void delegate()pure const)`. > > My position is that distinguishing delegates for mutability of the context > is a bad idea. No, it's just the obvious way it has to be type checked, because the delegate context is covariant, but the delegate argument is contravariant. struct DG{ void function(Ctx* ctx) dgptr; Ctx* ctxptr; } DG a; a.dgptr(a.ctxptr); // ok const(DG) b; b.dgptr(b.ctxptr); // error Delegates are not above documented type system guarantees. The analogy above is not perfect, because the delegate type `B delegate(A)q` is actually an existential type `∃C. q(C)×(A×q(C)→B)` with an evaluation map `ev[A,B,q]: (∃C. q(C)×const(A×q(C)→B))×A→B`, but this aspect remains the same: `const(∃C. q(C)×(A×q(C)→B)) = ∃C. const(q(C))×const(A×q(C)→B)`. You can't instantiate the evaluation map with any arguments `[A,B,q]` that makes it accept arguments of type `(∃C. const(q(C))×const(A×q(C)→B))×A` unless `q` includes `const` (or `immutable`). However, we still have `B delegate(A)q ⊆ B delegate(A)` because: `B delegate(A)q = ∃C. q(C)×(A×q(C)→B) ⊆ ∃C'. C'×(A×C'→B) = B delegate(A)`, where I have set `C'=q(C)`. Delegate contexts are not magic, they are just an opaque pointer to data. Where exactly do you disagree? > Thinking about it for a while, I come to the conclusion the > problem is elsewhere, namely uniqueness deduction is broken: > > > this(int value) pure > > { > > this.value = value; > > this.dg = &this.mut; > > } > > The mere fact that the struct has a delegate field means a reference to it > *can* exist in it. Therefore, the result of the constructor cannot be > assumed to be unique. Sure, there can be an internal reference. That's by design. However, everything that is not immutable is newly allocated memory and the only way to reach it is through the constructor's result. > The implicit cast to immutable is invalid. No, it's perfectly fine due to the transitivity of immutable. Even if that implicit cast was not allowed, the type system would be broken, it would just be less obvious. > IMO, the way the type system currently works, this is the issue. > ... The type checking of delegates is unsound, because it allows implicit unsafe type coercion in the wrong direction, from supertype to subtype. Pure functions can't change `const` arguments. Implicit hiding of mutation capabilities within `const` structures was never part of the design, and delegate contexts are not exempt from sound type checking. > ... > > > void main()pure{ > > immutable a=new A(0); // pure constructor, so this is okay > > "pure constructor, so this is okay": That's where the bad stuff begins. > Uniqueness isn't just slapping pure on stuff. A pure delegate need not > return a unique result as it can use its context to get its result from. > That's basically what happens here. > > Requiring const or immutable annotations on delegates for the context is a > breaking change and hell is it breaking. Just for `const` or `immutable` delegates. The annotations are not required to call a mutable delegate, you can still remove context qualifiers from mutable delegates as much as you wish, because the context is opaque. This will still work just fine: immutable int x=3; void delegate()immutable dg = ()immutable => x; void delegate() dg2 = dg; Not sure what your issue is. Is there really a lot of code out there that cares about annotating data const/immutable, yet relies on buggy delegate type checking to bypass the annotations? Those projects should not annotate in the first place. --