March 02, 2015
On Monday, 2 March 2015 at 21:00:54 UTC, Andrei Alexandrescu wrote:
> On 3/2/15 12:37 PM, Walter Bright wrote:
>> On 3/1/2015 12:51 PM, Michel Fortin wrote:
>>> That's actually not enough. You'll have to block access to global
>>> variables too:
>>>
>>>     S s;
>>>
>>>     void main() {
>>>         s.array = RCArray!T([T()]);   // s.array's refcount is now 1
>>>         foo(s.array[0]);           // pass by ref
>>>     }
>>>     void foo(ref T t) {
>>>         s.array = RCArray!T([]);      // drop the old s.array
>>>         t.doSomething();              // oops, t is gone
>>>     }
>>
>> Thinking about it, there are many other ways this can happen. At the
>> moment, I'm stuck thinking of a solution other than requiring foo() to
>> be pure. Anyone have ideas?
>
> I have a solution (as in working implementation), but also a deadline that's staring me in the face so this will have to wait a couple more days.
>
> I also have a bit of an attack to const/immutable support but that's less good (allows too much freedom).

"I have discovered a marvellous solution, but this post is too short to describe it."
March 02, 2015
On Monday, 2 March 2015 at 20:54:20 UTC, Walter Bright wrote:
> I looked at how Rust does it. I was hoping it was something clever, but it isn't. A copy is made of the object to which a borrowed reference is taken - in other words, the reference count is incremented.
>
> For D structs, that means, if there's a postblit, a copy must be made. For D ref counted classes, a ref count increment must be done.
>
> I was hoping to avoid that, but apparently there's no way.
>
> There are cases where this can be avoided, like calling pure functions. Another win for pure functions!

I do think you are confusing how Rust does it. In Rust, borrowing makes the source and borrowed reference immutable by default. So by default the problem do not occurs.

You can also borrow a mutable reference, in which case the source is disabled for the duration of the borrowing. That makes it impossible to borrow a mutable reference twice.

The above mentioned scenario cannot happen at all in Rust, by design. As a result, there is solution for the problem, as the problem cannot occur in the first place.
March 02, 2015
On Monday, 2 March 2015 at 20:54:20 UTC, Walter Bright wrote:
> On 3/2/2015 12:42 PM, Walter Bright wrote:
> For D structs, that means, if there's a postblit, a copy must be made. For D ref counted classes, a ref count increment must be done.
>
> I was hoping to avoid that, but apparently there's no way.
>
> There are cases where this can be avoided, like calling pure functions. Another win for pure functions!

It seems like the most common use case for passing a global to a parameter is to process that global in a pure way. You already have access to it in an impure function, so you could just access it directly. The good news is that from within the passed-to function, no further calls will treat it as global.

March 02, 2015
On Monday, 2 March 2015 at 21:12:20 UTC, deadalnix wrote:
> On Monday, 2 March 2015 at 20:54:20 UTC, Walter Bright wrote:
>> I looked at how Rust does it. I was hoping it was something clever, but it isn't. A copy is made of the object to which a borrowed reference is taken - in other words, the reference count is incremented.
>>
>> For D structs, that means, if there's a postblit, a copy must be made. For D ref counted classes, a ref count increment must be done.
>>
>> I was hoping to avoid that, but apparently there's no way.
>>
>> There are cases where this can be avoided, like calling pure functions. Another win for pure functions!
>
> I do think you are confusing how Rust does it. In Rust, borrowing makes the source and borrowed reference immutable by default. So by default the problem do not occurs.
>
> You can also borrow a mutable reference, in which case the source is disabled for the duration of the borrowing. That makes it impossible to borrow a mutable reference twice.
>
> The above mentioned scenario cannot happen at all in Rust, by design. As a result, there is solution for the problem, as the problem cannot occur in the first place.

I don't think you were advocating for this but,
+1 for a borrow system similar to Rust.
March 02, 2015
On 3/2/2015 1:12 PM, deadalnix wrote:
> I do think you are confusing how Rust does it. In Rust, borrowing makes the
> source and borrowed reference immutable by default. So by default the problem do
> not occurs.

May I refer you to:

http://static.rust-lang.org/doc/0.6/tutorial-borrowed-ptr.html#borrowing-managed-boxes-and-rooting

"Again the lifetime of y is L, the remainder of the function body. But there is a crucial difference: suppose x were to be reassigned during the lifetime L? If the compiler isn't careful, the managed box could become unrooted, and would therefore be subject to garbage collection. A heap box that is unrooted is one such that no pointer values in the heap point to it. It would violate memory safety for the box that was originally assigned to x to be garbage-collected, since a non-heap pointer---y---still points into it.
[...]
For this reason, whenever an & expression borrows the interior of a managed box stored in a mutable location, the compiler inserts a temporary that ensures that the managed box remains live for the entire lifetime. [...] This process is called rooting."


It's possible that this is an old and obsolete document, but it's what I found.
March 02, 2015
On 3/2/2015 1:09 PM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote:
> "I have discovered a marvellous solution, but this post is too short to describe
> it."

Fortunately, Fermat (er, Andrei) was able to pass along his dazz idea to me this afternoon, before he expired to something he had to attend to. We were both in the dumps about this problem last night, but I think he solved it.

His insight was that the deletion of the payload occurred before the end of the lifetime of the RC object, and that this was the source of the problem. If the deletion of the payload occurs during the destructor call, rather than the postblit, then although the ref count of the payload goes to zero, it doesn't actually get deleted.

I.e. the postblit manipulates the ref count, but does NOT do payload deletions. The destructor checks the ref count, if it is zero, THEN it does the payload deletion.

Pretty dazz idea, dontcha think? And DIP25 still stands unscathed :-)

Unless, of course, we missed something obvious.
March 02, 2015
On 3/2/15 2:57 PM, Walter Bright wrote:
> On 3/2/2015 1:09 PM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>"
> wrote:
>> "I have discovered a marvellous solution, but this post is too short
>> to describe
>> it."
>
> Fortunately, Fermat (er, Andrei) was able to pass along his dazz idea to
> me this afternoon, before he expired to something he had to attend to.
> We were both in the dumps about this problem last night, but I think he
> solved it.
>
> His insight was that the deletion of the payload occurred before the end
> of the lifetime of the RC object, and that this was the source of the
> problem. If the deletion of the payload occurs during the destructor
> call, rather than the postblit, then although the ref count of the
> payload goes to zero, it doesn't actually get deleted.
>
> I.e. the postblit manipulates the ref count, but does NOT do payload
> deletions. The destructor checks the ref count, if it is zero, THEN it
> does the payload deletion.
>
> Pretty dazz idea, dontcha think? And DIP25 still stands unscathed :-)
>
> Unless, of course, we missed something obvious.

And since an RCArray may undergo several assignments during its lifetime (thus potentially needing to free several chunks of memory), the arrays to be destroyed will be kept in a freelist-style structure. Destructor walks the freelist and frees the chunks.

Andrei
March 02, 2015
On 3/2/2015 3:22 PM, Andrei Alexandrescu wrote:
> And since an RCArray may undergo several assignments during its lifetime (thus
> potentially needing to free several chunks of memory), the arrays to be
> destroyed will be kept in a freelist-style structure. Destructor walks the
> freelist and frees the chunks.

Right, I had neglected to add that.

March 02, 2015
On 3/2/15 3:22 PM, Andrei Alexandrescu wrote:
> On 3/2/15 2:57 PM, Walter Bright wrote:
>> On 3/2/2015 1:09 PM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>"
>> wrote:
>>> "I have discovered a marvellous solution, but this post is too short
>>> to describe
>>> it."
>>
>> Fortunately, Fermat (er, Andrei) was able to pass along his dazz idea to
>> me this afternoon, before he expired to something he had to attend to.
>> We were both in the dumps about this problem last night, but I think he
>> solved it.
>>
>> His insight was that the deletion of the payload occurred before the end
>> of the lifetime of the RC object, and that this was the source of the
>> problem. If the deletion of the payload occurs during the destructor
>> call, rather than the postblit, then although the ref count of the
>> payload goes to zero, it doesn't actually get deleted.
>>
>> I.e. the postblit manipulates the ref count, but does NOT do payload
>> deletions. The destructor checks the ref count, if it is zero, THEN it
>> does the payload deletion.
>>
>> Pretty dazz idea, dontcha think? And DIP25 still stands unscathed :-)
>>
>> Unless, of course, we missed something obvious.
>
> And since an RCArray may undergo several assignments during its lifetime
> (thus potentially needing to free several chunks of memory), the arrays
> to be destroyed will be kept in a freelist-style structure. Destructor
> walks the freelist and frees the chunks.

We may apply the same principle (memory may only be freed during destruction) to DIP74. If we figure a cheap way to keep a freelist of objects to be deallocated, we can borrow multiple times without a reference count add. But I didn't think this through yet. -- Andrei
March 02, 2015
On Monday, 2 March 2015 at 22:51:03 UTC, Walter Bright wrote:
> On 3/2/2015 1:12 PM, deadalnix wrote:
>> I do think you are confusing how Rust does it. In Rust, borrowing makes the
>> source and borrowed reference immutable by default. So by default the problem do
>> not occurs.
>
> May I refer you to:
>
> http://static.rust-lang.org/doc/0.6/tutorial-borrowed-ptr.html#borrowing-managed-boxes-and-rooting
>
> "Again the lifetime of y is L, the remainder of the function body. But there is a crucial difference: suppose x were to be reassigned during the lifetime L? If the compiler isn't careful, the managed box could become unrooted, and would therefore be subject to garbage collection. A heap box that is unrooted is one such that no pointer values in the heap point to it. It would violate memory safety for the box that was originally assigned to x to be garbage-collected, since a non-heap pointer---y---still points into it.
> [...]
> For this reason, whenever an & expression borrows the interior of a managed box stored in a mutable location, the compiler inserts a temporary that ensures that the managed box remains live for the entire lifetime. [...] This process is called rooting."
>
>
> It's possible that this is an old and obsolete document, but it's what I found.


That's actually very old(0.6 - nearly 2 years ago.) I believe rooting was dropped from the language completely.

http://doc.rust-lang.org/book/ownership.html

also, rust by example chapter 17-19 cover this
http://rustbyexample.com/move.html