Thread overview | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
October 26, 2020 Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
This is an interesting read: https://cyclone.thelanguage.org/wiki/Region%20Common%20Uses/ The typical use case I see is a tree `T` that `emplace`s all its nodes in a region `R` where `R`'s lifetime and, in turn, `T`s nodes are all deterministically bound to `T`s lifetime. Can such a tree be written today with the help of DIP-1000 and `@live`? That provides @safe `scope`d access to its nodes in -dip1000. And would a potential solution differ in implementation depending on whether the node is a value (POD or `struct`) or a reference type (`class` or pointer)? |
October 26, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to Per Nordlöw | On Monday, 26 October 2020 at 14:27:49 UTC, Per Nordlöw wrote: > The typical use case I see is a tree `T` that `emplace`s all its nodes in a region `R` where `R`'s lifetime and, in turn, `T`s nodes are all deterministically bound to `T`s lifetime. Here's the essence of what I had in mind: struct Tree(Node) if (is(Node == class)) { @safe: Node root() return scope pure nothrow @nogc { return _root; } this(uint dummy) @trusted { import core.lifetime : emplace; _root = emplace!Node(_store); } private Node _root; private void[__traits(classInstanceSize, Node)] _store; // replaced with a region } class C { this() {} int x; } @safe pure unittest { C f() { Tree!C t; return t.root; } // should error? C g() { scope Tree!C t; return t.root; } // errors } What's the reasoning behind disallowing g() but not f()? Lack of `scope`-inference in the compiler? Calling f() will result in a memory error aswell. |
October 26, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to Per Nordlöw | On Monday, 26 October 2020 at 19:47:57 UTC, Per Nordlöw wrote:
> What's the reasoning behind disallowing g() but not f()? Lack of `scope`-inference in the compiler? Calling f() will result in a memory error aswell.
Reduced:
class Node {}
struct Tree
{
Node root;
}
@safe unittest
{
Node f() { auto t = Tree(); return t.root; } // shouldn't this error aswell?
Node g() { scope t = Tree(); return t.root; } // errors
}
|
October 26, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to Per Nordlöw | On Monday, 26 October 2020 at 21:10:21 UTC, Per Nordlöw wrote:
> On Monday, 26 October 2020 at 19:47:57 UTC, Per Nordlöw wrote:
> Node f() { auto t = Tree(); return t.root; } // shouldn't this error aswell?
> Node g() { scope t = Tree(); return t.root; } // errors
Node is assumed to be gc allocated, but scope is transitive?
It would be easier to reason about if D had a gcpointer type.
|
October 27, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On 26.10.20 22:32, Ola Fosheim Grøstad wrote:
> On Monday, 26 October 2020 at 21:10:21 UTC, Per Nordlöw wrote:
>> On Monday, 26 October 2020 at 19:47:57 UTC, Per Nordlöw wrote:
>> Node f() { auto t = Tree(); return t.root; } // shouldn't this error aswell?
>> Node g() { scope t = Tree(); return t.root; } // errors
>
> Node is assumed to be gc allocated, but scope is transitive?
`scope` isn't transitive. It just applies to the pointers in the variable (i.e. `t.root`).
It doesn't apply to the location of the variable, because the compiler already knows that references to locals must not escape.
|
October 27, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to Per Nordlöw | On 26.10.20 22:10, Per Nordlöw wrote:
> Reduced:
>
> class Node {}
>
> struct Tree
> {
> Node root;
> }
>
> @safe unittest
> {
> Node f() { auto t = Tree(); return t.root; } // shouldn't this error aswell?
> Node g() { scope t = Tree(); return t.root; } // errors
> }
You've reduced that a bit too much. There's nothing unsafe left in the code. `g` only errors, because you're using `scope` to tell the compiler that `t.root` is dangerous (when it's not actually).
|
October 27, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to ag0aep6g | On Tuesday, 27 October 2020 at 06:16:11 UTC, ag0aep6g wrote:
> `scope` isn't transitive. It just applies to the pointers in the variable (i.e. `t.root`).
But technically the t.root pointer address is a value, so apparently scope does not apply only to t, but also to the member root?
So if Node was a linked list, then t.root is limited by escape analysis, but not t.root.next?
I don't think t.root should be limited by scope.
|
October 27, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On Tuesday, 27 October 2020 at 07:07:49 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 27 October 2020 at 06:16:11 UTC, ag0aep6g wrote:
>> `scope` isn't transitive. It just applies to the pointers in the variable (i.e. `t.root`).
>
> But technically the t.root pointer address is a value, so apparently scope does not apply only to t, but also to the member root?
Ok, I guess this is to allow scope limiting of custom smart pointers...
Maybe it would be better to have some way of telling the compiler that something is a smart pointer.
|
October 27, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On 27.10.20 08:07, Ola Fosheim Grøstad wrote: > But technically the t.root pointer address is a value, so apparently scope does not apply only to t, but also to the member root? I'm not sure what you mean by "is a value". `t.root` is a class reference, which (to `scope`) is a pointer with a fancy name. It being an indirection is what makes it interesting. `scope` doesn't do anything for non-indirections. But yes, `scope` applies to `t.root`, because it's the first level of indirection in `t`. If it was `t.x.y.z.root`, with `x`, `y`, and `z` being more structs, `scope` would still apply to `root`. But if `x`, `y`, or `z` were some kind of indirection, `scope` would apply to that and not to `root`. `scope` doesn't work on types. It works on indirections. `scope` sees through types until it finds an indirection. And then it stops. If you have two or more levels of indirections, `scope` still only affects the first. An example in code: ---- struct A { int* pa; B b; } struct B { int* pb; C* c; } struct C { int* pc; int x; } int* f(scope A a) @safe { return a.pa; /* error; can't return a `scope` pointer */ return a.b.pb; /* error; still covered by `scope` */ return a.b.c.pc; /* ok; `scope` is not transitive */ } ---- If you replace `scope` with `const`, all three lines fail, because `const` is transitive. (Note: `ref` is handled differently from other forms of indirection.) > So if Node was a linked list, then t.root is limited by escape analysis, but not t.root.next? Exactly. |
October 27, 2020 Re: Safe Regions with Deterministic Destruction | ||||
---|---|---|---|---|
| ||||
Posted in reply to ag0aep6g | On Tuesday, 27 October 2020 at 15:33:08 UTC, ag0aep6g wrote: > On 27.10.20 08:07, Ola Fosheim Grøstad wrote: >> But technically the t.root pointer address is a value, so apparently scope does not apply only to t, but also to the member root? > > I'm not sure what you mean by "is a value". `t.root` is a class reference, which (to `scope`) is a pointer with a fancy name. It being an indirection is what makes it interesting. `scope` doesn't do anything for non-indirections. Yes, what I meant is that it is a value on the stack. But there is a disconnect with classes. "oddball" passes as a class, even though it would not pass as a struct: class A { int x; } class B { A a; } A oddball() { scope B b = new B; b.a = new A; return b.a; } But WHY does this pass? class A { int x; } struct B { A a; } A misbehave() @safe { scope A tmp = new A; scope B b; b.a = tmp; return b.a; } > But yes, `scope` applies to `t.root`, because it's the first level of indirection in `t`. If it was `t.x.y.z.root`, with `x`, `y`, and `z` being more structs, `scope` would still apply to `root`. But if `x`, `y`, or `z` were some kind of indirection, `scope` would apply to that and not to `root`. I understand. So for structs it works the same way as scope-parameters? > then it stops. If you have two or more levels of indirections, `scope` still only affects the first. So you basically should just apply scope to pointers and smart pointers. >> So if Node was a linked list, then t.root is limited by escape analysis, but not t.root.next? > > Exactly. I wonder how a transitive scope would play out? |
Copyright © 1999-2021 by the D Language Foundation