On Saturday, 14 November 2015 at 23:20:08 UTC, Andrei Alexandrescu wrote:
On 11/14/15 5:49 PM, Timon Gehr wrote:
It's supposed to guarantee that the given reference is not used to
transitively mutate the object. The casts violate this.

I think that semantics needs to change. Specifically, either we add a @mutable attribute (which means const doesn't apply to fields marked as such and immutable objects cannot be created); or we could just decree that if a const object originates in a mutable object, casts should be well-defined. -- Andrei

Either way, we'd be throwing away the idea that const is physical const and provides actual guarantees against mutation via const references. We'd essentially be going the route of having C++'s const except that it's transitive, which is a definite loss IMHO, but at the same time, with D's const, you frequently have to give up on using const, because physical const is so restrictive as to be unusable in many cases.

I honestly don't know if it's better to just say that you can't use const if you want to do something like reference counting or using an allocator or to gut the guarantees that D's const provides. Ideally, we'd keep the guarantees, but they often seem to end up being completely impractical in practice. And they don't jive at all with the recent push to support RC.

That being said, if we are going to make a change like this, I'm not sure if we even _can_ do it. immutable throws a serious wrench in any attempt have something like C++'s const in D.

As it stands, the only thing that really ensures that immutable objects aren't mutated is the type system. As I understand it, if an immutable object gets put into ROM (or memory that's being treated as ROM), then mutating it will cause a segfault, but the only case where that might happen right now AFAIK is if the object was created at compile time and stored as part of the program's data. Certainly, any immutable objects created at runtime are only protected from mutation by the type system. So, even if the compiler makes _zero_ assumptions based on const, casting away const and mutating is very dangerous, because you risk mutating an immutable object and violating all of the guarantees associated with that. The only time that casting away const and mutating could work would be in cases where you could somehow guarantee that the object you're mutating is actually a mutable variable underneath the hood. That would be possible in a restricted setting, but in a large program or in a public API, it's a lot less likely that you can guarantee that the object isn't actually immutable. There would have to be some way for the type system to guarantee that the object isn't actually immutable - which either means making it so that the type in question can't be immutable for some reason or having an attribute other than const for non-physical const.

Your @mutable suggestion/proposal does step in that direction by basically making it so that a const type which contains a @mutable member isn't really const. It's some other attribute that's not explicitly named (cpp_const, logical_const, @mutable_const, or whatever we'd want to call it). And that seems like it would work to a point. It would even allow for implicit casts instead of explicit ones and make the whole thing far safer in general than simply allowing arbitrary casting would. However, @mutable still isn't an attribute on the type. It's an attribute on a member. So, as soon as you have an opaque pointer or a base class reference, the compiler doesn't know that the object is actually @mutable_const. So, it can't know that it's not legit to have the object be immutable. Now, the compiler would have to know that when the object is created, and presumably an @mutable_const derived class couldn't convert to an immutable base class, so maybe this would actually work, but it seems like we're at serious risk of a loophole if we're not really careful here. It feels like each time work through this I flip-flop as to whether I think that it can work or not.

And of course, even if we do make @mutable work, it would basically mean that const doesn't necessarily guarantee much of anything anymore other than preventing accidental mutation, since unless the compiler can guarantee that no @mutable_const is involved, it can't assume much of anything about const (though what it can assume about const is already pretty limited - particularly when pure isn't involved), and programmers can't assume that a const object will really not be mutated by const member functions (though in practice, it's unlikely that anyone is going to slap @mutable on everything). But even then, we'd still be ahead of C++'s const if we have transitivity and disallow casting away const to mutate.

I'm torn on the whole thing. On the one hand, I think that D's guarantees with physical const have been great. On the other hand, too much stuff doesn't work with it. And system programming stuff like ref-counting, mutexes, and allocators definitely don't work with it.

- Jonathan M Davis

Reply via email to