March 02, 2015 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Zach the Mystic | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 Re: RCArray is unsafe | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 :-(
|
Copyright © 1999-2021 by the D Language Foundation