November 15, 2015
On 11/14/2015 06:45 PM, Timon Gehr wrote:
> How to implement COW without support for writing?

That'll be the next topic. -- Andrei

November 15, 2015
On 11/14/2015 06:48 PM, Timon Gehr wrote:
> On 11/15/2015 12:20 AM, 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
>>
>
> There's also this closely related situation:
> https://issues.dlang.org/show_bug.cgi?id=9149
>
> (I.e. delegates with mutable context pointer can be implicitly converted
> to delegates with const context pointer, but when type checking the
> delegate, a mutable context pointer is assumed.)

That's a hole straight into the middle of things. We need to fix that. -- Andrei
November 15, 2015
On Saturday, 14 November 2015 at 21:02:46 UTC, Observer wrote:
> On Saturday, 14 November 2015 at 16:27:17 UTC, Dicebot wrote:
>> All trouble comes from trying to use physical immutable as logical one while still pretending it gives physical guarantees. Even if existing immutability is not widely applicable, I'd prefer to have narrow applicability over wide false confidence. Right now I know for sure that if I can use immutable data without any thread locking and it is not possible to screw it up. It is rarely important, but when it is, it is priceless.
>> I can't say I'm following this discussion in great detail, but
> one thing strikes me.  "const"-ness and "immutable"-ility are
> at some abstract level flavors of value stability.  And that
> suggests to me that perhaps we should not be looking so much
> for backdoors as for new terms, leaving the old terms alone.
> Perhaps a given old keyword won't work well with RC; so be it.
> Perhaps a new keyword such as "stable" could be used to describe
> a storage category where the payload fields are unchanging but
> any metadata fields are potentially mutable.  Then perhaps const
> could mean physically immutable, while stable means logically
> immutable.  Or something like that.

I simply don't see much value in logical immutability (and only small value I n logical const). Recently I had experience of porting lot of D1 code (which had const as a storage class) to D2 and was surprised how fake most of added safety seemed. In most case modifying such value wouldn't cause any trouble at all and case for real trouble comes from subtle indirectional mutation.

Because of that I am very unhappy with attempts to break the type system which had costed considerable part of limited language resource for the sake of fake convenience (all @mutable speculation)
November 15, 2015
On Sunday, 15 November 2015 at 13:30:30 UTC, Dicebot wrote:
>
> Because of that I am very unhappy with attempts to break the type system which had costed considerable part of limited language resource for the sake of fake convenience (all @mutable speculation)
>

+1, same attitude here.

---
/Paolo


November 15, 2015
On Sunday, 15 November 2015 at 08:59:52 UTC, Ola Fosheim Grøstad wrote:
> On Sunday, 15 November 2015 at 02:49:49 UTC, Jonathan M Davis wrote:
>> the program's data. Certainly, any immutable objects created at runtime are only protected from mutation by the type system.
>
> That would be a bad assumption. It could be read only pages with MMU protection. (files, shared memory etc)

Well, that's news to me, but my understanding of stuff that low level isn't very good. Regardless, my point is that there's no guarantee that anything other than the type system protects immutable variables against mutation. For instance, this code compiles and runs just fine.

    void main()
    {
        auto i = new immutable int(5);
        assert(*i == 5);
        auto j = cast(int*)i;
        *j = 42;
        assert(*i == 42);
    }

And there's this bug about how non-shared static constructors routinely mutate immutable variables:

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

So, while there _are_ cases where mutating immutable memory will cause a segfault, there is no guarantee that casting and mutating an immutable variable will cause any obvious problems at all.

So, allowing const to be cast away for the purposes of mutation becomes _very_ dangerous, because it's very easy to screw up and mutate immutable objects, and given how differently the compiler treats immutable objects (e.g. they're implicitly shared and allow for eliding function calls when used in conjunction with pure) on top of the guarantees that the compiler gives about immutable objects never mutating, the consequences of mutating an object variable are far worse than they are with mutating a mutable object being via a const reference, even when doing that via a cast is currently undefined behavior.

So, if we choose to put a backdoor in const, it would be far better to do so with something like @mutable than to treat casting away const from an object and mutating it as defined behavior - though I think that we should be _very_ sure that we're willing to pay the cost of that backdoor before we make that choice.

- Jonathan M Davis
November 15, 2015
> For instance, this code compiles and runs just fine.
>
>     void main()
>     {
>         auto i = new immutable int(5);
>         assert(*i == 5);
>         auto j = cast(int*)i;
>         *j = 42;
>         assert(*i == 42);
>     }

AFAIK this is UB already (in practice), you will get different results depending on compiler and optimization flags.
November 15, 2015
On Sunday, 15 November 2015 at 12:56:27 UTC, Andrei Alexandrescu wrote:
> On 11/14/2015 05:49 PM, Timon Gehr wrote:
>> List uses RC internally. I don't think the UB casts will stay for the
>> final version, unless you are willing to completely dilute the meaning
>> of const in ways that Walter has explicitly expressed will not be done
>> in the past.
>
> As I mentioned, he's okay with changing the language to make the casts well defined. -- Andrei

Well, that's a big change, since it pretty much means that D's const isn't physical const anymore, and Walter has been _very_ insistent about that in the past - to the point that he's argued that C++'s const is outright useless because it isn't physical const. If casting away const and mutating is well-defined behavior, then we basically have C++'s const except that it's transitive, and you have to be careful to make sure that the object isn't actually immutable underneath the hood instead of mutable, whereas C++ doesn't have immutable.

But if we go that route, personally, I think that it would be far better to do something like @mutable and leave mutating const and undefined outside of @mutable, particularly since if we can implement @mutable properly, then the compiler can guarantee that the object being mutated isn't actually immutable, and it avoids the need for casting altogether, which reduces the risk of bugs even when immutable isn't involved. It also means that the programmer can determine whether an object is really only logically const rather than physically const by looking at its member variables rather than having to dig through all of its code to see whether it ever casts away const.

- Jonathan M Davis
November 15, 2015
On Sunday, 15 November 2015 at 14:23:05 UTC, Jonathan M Davis wrote:
>> As I mentioned, he's okay with changing the language to make the casts well defined. -- Andrei
>
> Well, that's a big change, since it pretty much means that D's const isn't physical const anymore, and Walter has been _very_ insistent about that in the past - to the point that he's argued that C++'s const is outright useless because it isn't physical const. If casting away const and mutating is well-defined behavior, then we basically have C++'s const except that it's transitive ...

Casting away _const_ is already legal if programmer can himself guarantee underlying object has mutable origin (i.e. not available via immutable reference), by the very definition of const. It is casting away immutable and mutating that is strictly UB.
November 15, 2015
On Sunday, 15 November 2015 at 14:21:18 UTC, Dicebot wrote:
>> For instance, this code compiles and runs just fine.
>>
>>     void main()
>>     {
>>         auto i = new immutable int(5);
>>         assert(*i == 5);
>>         auto j = cast(int*)i;
>>         *j = 42;
>>         assert(*i == 42);
>>     }
>
> AFAIK this is UB already (in practice), you will get different results depending on compiler and optimization flags.

Oh, it's definitely undefined behavior. As far as the compiler is concerned, casting away either const or immutable means that it's up to _you_ to uphold the guarantee that they won't be mutated (whereas the compiler can guarantee it as long as you don't cast away const or immutable).

My point is that there's nothing actually stopping you from doing it, and you don't necessarily get a segfault or any other obvious problems when you do it. So, if the language were changed so that casting away const and mutating was defined behavior (at least as long as the underlying object is actually mutable), the only thing protecting folks against not accidentally casting away const on an object that is actually immutable is themselves. There is no compiler help, and the program will not necessarily blow up when you screw it up. So, you run a very real risk of introducing very subtle bugs into your programs with regards to immutable objects when you cast away const and mutate - even if the language is changed so that that is well-defined when the object is actually mutable.

At least with @mutable as Andrei proposed it, the compiler would be able to guarantee that an immutable object wasn't treated as mutable, even if it then allowed const references to mutate objects in a limited manner. So, while we'd lose out on the guarantee that const references could never mutate objects, at least that mutation would be limited and controlled with some compiler guarantees left intact, whereas making it well-defined to cast away const and mutate would be throwing pretty much all of the guarantees out the window (on top of being very error-prone).

- Jonathan M Davis
November 15, 2015
On Sunday, 15 November 2015 at 14:34:45 UTC, Dicebot wrote:
> On Sunday, 15 November 2015 at 14:23:05 UTC, Jonathan M Davis wrote:
>>> As I mentioned, he's okay with changing the language to make the casts well defined. -- Andrei
>>
>> Well, that's a big change, since it pretty much means that D's const isn't physical const anymore, and Walter has been _very_ insistent about that in the past - to the point that he's argued that C++'s const is outright useless because it isn't physical const. If casting away const and mutating is well-defined behavior, then we basically have C++'s const except that it's transitive ...
>
> Casting away _const_ is already legal if programmer can himself guarantee underlying object has mutable origin (i.e. not available via immutable reference), by the very definition of const. It is casting away immutable and mutating that is strictly UB.

No. As it stands, casting away const and mutating is _always_ considered undefined behavior, regardless of whether the object being referred to is actually mutable, const, or immutable. In fact, there was a discussion on that not long ago, and the spec was updated to be clearer on that count - with approval from Walter. AFAIK, it has never been the case that casting away const and mutating was defined behavior in D2 (I have no idea what the deal with D1 and const is other than the fact that it was quite different).

- Jonathan M Davis