[Issue 9149] Disallow converting delegates to const

via Digitalmars-d-bugs digitalmars-d-bugs at puremagic.com
Wed May 25 12:02:12 PDT 2016


https://issues.dlang.org/show_bug.cgi?id=9149

--- Comment #11 from timon.gehr at gmx.ch ---
(In reply to Steven Schveighoffer from comment #10)
> I think the delegate assignment should be legal. The auto-conversion from
> pure ctor to immutable should be illegal (note that converting to const is
> not a good test case, because you can easily assign an A to a const A
> without purity), because casting the A to immutable does not affect the
> 'this' pointer of the delegate member, and that could point at the given
> object that you are casting or any member.

There might be a misunderstanding about what this report is about.

> I had thought that the 'this'
> pointer was const-agnostic. That is:
> 
> class C
> {
>    void foo() {}
>    void ifoo() immutable {}
> }
> 
> void main()
> {
>    mc = new C;
>    ic = new C;
>    void delegate() x = &mc.foo;
>    x = &mc.ifoo; // why not?
> }
> 
> Why does it matter if x's delegate has an immutable 'this'? that detail is
> insignificant to the caller.

I assume this is the code you intended to write:

void main(){
    auto mc=new C,ic=new immutable(C);
    void delegate() x = &mc.foo;
    x = &ic.ifoo; // why not?
}

DMD disallows this code. (Hopefully. I have checked multiple times :-).)
It is always okay to remove guarantees on the 'this' pointer, such as pure,
const or immutable, because the closure data is untyped (you can access it only
as a void*), so this should indeed be fine. You might consider opening an issue
about this.


This report is about the converse case.

The trouble is that the conversion delegate() -> const(delegate()) breaks
transitivity of const if we are allowed to call the const(delegate())
afterwards. I.e. if delegate() -> const(delegate()) conversion and calling
const(delegate())'s is allowed, and we have a function

void foo(const C c)pure{ ... }

then this function might modify state reachable by its argument. However, const
is designed to guarantee this cannot happen: Code should not be able to modify
data through a const reference (no matter whether the type system is able to
prove that it was mutable originally).

import std.stdio;
@safe:
class C{
    int x=0;
    this(){ mthis=unconstify(this); }
    void mutate()const pure{ mthis.x++; }
private:
    const(Unconstify!C) mthis;
}

void foo(const C c)pure{
    c.mutate();
}

void main(){
    auto c=new C;
    auto x1=c.x;
    foo(c);
    auto x2=c.x;
    writeln(x1," ",x2); // 0 1
}


struct Unconstify(T){
    T delegate()pure _get;
    @property T ptr()const pure{ return _get(); }
    alias ptr this;
}

const(Unconstify!T) unconstify(T)(T ptr)pure{
    return Unconstify!T(()=>ptr);
}

The fix I originally proposed in the first post removes the unsoundness in the
const system, but it is ugly. It means the line "const x = y;" fails if y is a
delegate, but not if y is an instance of a struct wrapping a delegate. A more
elegant solution would be to disallow calling const delegates whose context is
not typed const or immutable. I.e. const(int delegate()const) can be called,
but const(int delegate()) cannot be called.

Basically the same problem exists for immutable.

--


More information about the Digitalmars-d-bugs mailing list