March 02, 2015
On Monday, 2 March 2015 at 05:57:35 UTC, 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:
>
> Hmm. That's not so easy to solve.

But consider this. It's only an impure function which might alias a global. And since you already have access to the global in the impure function, there might be less incentive in general to pass it through a function. Other than that, you're stuck with a theoretical "@impure!varName" function attribute, for example, which tells the caller which globals are accessed.
March 02, 2015
On Sunday, 1 March 2015 at 20:51:35 UTC, Michel Fortin wrote:
> On 2015-03-01 19:21:57 +0000, Walter Bright said:
>
>> The trouble seems to happen when there are two references to the same object passed to a function. I.e. there can be only one "borrowed" ref at a time.
>> 
>> I'm thinking this could be statically disallowed in @safe code.
>
> 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
> 	}

What's the difference between that and this:

void fun() {
  T[] ta = [T()].dup;
  T* t = ta[0];
  delete ta; // or however you do it
  *t = ...;
}

Why is this a parameter passing issue and not a "you kept a sub-reference to a deleted chunk" issue?
March 02, 2015
On Monday, 2 March 2015 at 15:22:33 UTC, Zach the Mystic wrote:
> void fun() {
>   T[] ta = [T()].dup;
>   T* t = ta[0];

I meant: T* t = &ta[0];
March 02, 2015
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?

March 02, 2015
On 3/1/15 2:21 PM, Walter Bright wrote:
> On 3/1/2015 7:44 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>"
> wrote:
>> A weakness of the same kind affects DIP25, too. The core of the
>> problem is
>> borrowing (ref return as in DIP25), combined with manual (albeit
>> hidden) memory
>> management. An example to illustrate:
>>
>>      struct T {
>>          void doSomething();
>>      }
>>      struct S {
>>          RCArray!T array;
>>      }
>>      void main() {
>>          auto s = S(RCArray!T([T()])); // s.array's refcount is now 1
>>          foo(s, s.array[0]);           // pass by ref
>>      }
>>      void foo(ref S s, ref T T) {
>>          s.array = RCArray!T([]);      // drop the old s.array
>>          t.doSomething();              // oops, t is gone
>>      }

This is an odd example, how does one take a ref to an RCArray element without the machinery to retain the array? I would think that RCArray[x] would return something that isn't passable to a function. Or am I missing something?

>
> The trouble seems to happen when there are two references to the same
> object passed to a function. I.e. there can be only one "borrowed" ref
> at a time.

Not exactly. Note that we are taking by reference S, which is NOT reference counted. So you are passing indirectly a reference to an RC object. You aren't "borrowing" that reference.

-Steve
March 02, 2015
On 3/2/2015 12:37 PM, Steven Schveighoffer wrote:
> Not exactly. Note that we are taking by reference S, which is NOT reference
> counted. So you are passing indirectly a reference to an RC object. You aren't
> "borrowing" that reference.

It's seems that to solve the problem, it has to be borrowed, in that a static check will be needed that what it points to cannot be modified/deleted through other references to the same data.

March 02, 2015
On 3/2/2015 12:42 PM, Walter Bright wrote:
> On 3/2/2015 12:37 PM, Steven Schveighoffer wrote:
>> Not exactly. Note that we are taking by reference S, which is NOT reference
>> counted. So you are passing indirectly a reference to an RC object. You aren't
>> "borrowing" that reference.
>
> It's seems that to solve the problem, it has to be borrowed, in that a static
> check will be needed that what it points to cannot be modified/deleted through
> other references to the same data.


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!
March 02, 2015
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).


Andrei

March 02, 2015
On 3/2/15 12:53 PM, Walter Bright wrote:
> I was hoping to avoid that, but apparently there's no way.

There is! There is! -- Andrei
March 02, 2015
On Sunday, 1 March 2015 at 19:22:06 UTC, Walter Bright wrote:
> On 3/1/2015 7:44 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote:
>> A weakness of the same kind affects DIP25, too. The core of the problem is
>> borrowing (ref return as in DIP25), combined with manual (albeit hidden) memory
>> management. An example to illustrate:
>>
>>     struct T {
>>         void doSomething();
>>     }
>>     struct S {
>>         RCArray!T array;
>>     }
>>     void main() {
>>         auto s = S(RCArray!T([T()])); // s.array's refcount is now 1
>>         foo(s, s.array[0]);           // pass by ref
>>     }
>>     void foo(ref S s, ref T T) {
>>         s.array = RCArray!T([]);      // drop the old s.array
>>         t.doSomething();              // oops, t is gone
>>     }
>
> The trouble seems to happen when there are two references to the same object passed to a function. I.e. there can be only one "borrowed" ref at a time.
>
> I'm thinking this could be statically disallowed in @safe code.

Yes, it's a classical aliasing problem. Of course, if the references can't possible alias because of their types, it's ok. However, for non-pure functions, it's always (?) unsafe, because they have access to all kinds of global variables. Too bad we don't have pure by default :-(