On Fri, 30 Sep 2011 09:27:31 -0400, Christophe <[email protected]> wrote:

"Steven Schveighoffer" , dans le message (digitalmars.D:145767), a
 écrit :
On Thu, 29 Sep 2011 13:26:11 -0400, Steven Schveighoffer
<[email protected]> wrote:

On Thu, 29 Sep 2011 13:06:55 -0400, Simen Kjaeraas
<[email protected]> wrote:

On Thu, 29 Sep 2011 16:54:24 +0200, Steven Schveighoffer
<[email protected]> wrote:

I just thought of an interesting way to make a logical const object
without casts.  It requires a little extra storage, but works without
changes to the current compiler (and requires no casts).
[snip]
What do people think about this?

This is what I think about it:


I agree this breaks immutability, and needs to be addressed.  I think
probably implicit casting of delegates (or items that containe
delegates) to immutable from strong-pure functions should be disallowed.

But it's not the pattern I described, and uses a relatively new trick
(implicit immutable casting).  I'll file a bug for this case.


http://d.puremagic.com/issues/show_bug.cgi?id=6741


Well, if the langage wants to be consistent, you should prevent implicit
casting of delegates to const, not just to immutable.

What you revealed is very interesting, but it is clearly a bug. A
delegate context pointer in a const object should be const, and the
delegate function should be const with regard to its context pointer if
you want the function to be called when the delegate is const.
Constness has to apply to delegates just like it applies to structs. The
explanation gets messy.

If const must be a part of the signature, then you have lost the typeless aspect of the context pointer. If we expose the const (or not const) nature of the pointer, then you lose the interoperability that delegates provide. The beauty of delegates is you don't *have to* care what the type of the context pointer is. That's defined by the function the delegate represents.

It's why, for example, a function accepting a delegate does not distinguish between a delegate literal and a delegate to a member function of type Foo or a delegate to a member function of type const(Bar). You must think of the context pointer as a hidden parameter to the delegate, as defined when the delegate was created *not* when it is called. The fact that it's actually stored with the delegate pointer is irrelevant. Conceptually, it's not stored anywhere, it's just a parameter.

But in the case of the bug I filed, we are talking about a compiler-sanctioned implicit transformation to immutable. We *must* guarantee that when we allow an implicit transformation, that there are no existing mutable copies of the data. An explicit transformation should be allowed, because then the user has accepted responsibility.

The same is not true for const. It's *perfectly legal* to have a const and mutable reference to an object at the same time. The only reason transitive const exists and is necessary is to support transitive immtuable.

Const is never guaranteed to prevent changing data on an object:

class C
{
   int x;
   void multiply(C other) const {other.x *= x;}
}

Is multiply guaranteed not to change the object?  No:

c.multiply(c);

However, if c is immutable, it *is* guaranteed, because there's no way to pass c as the parameter to multiply.

That is why I think the solution works -- if you allow the compiler to enforce the rules of const and immutable, you can still do unexpected things, but you do not break the guarantees of immutability. It's definitely a loophople, but I think it's valid.

I guess you could also hide a mutable pointer of an immutable object
during its construction with the same trick.

But saving a mutable object as immutable requires a cast. It means "compiler, I take responsibility for ensuring this no longer has any mutable references". When the cast is *not* required, it's the compiler's responsibility.

-Steve

Reply via email to