Escape Analysis & Owner Escape Analysis
Richard (Rikki) Andrew Cattermole
richard at cattermole.co.nz
Wed Sep 4 11:03:46 UTC 2024
On 04/09/2024 10:24 PM, Dennis wrote:
> On Wednesday, 4 September 2024 at 03:02:10 UTC, Richard (Rikki) Andrew
> Cattermole wrote:
>> It would be an easy enough swap to change ``@safe`` to ``@tsafe``. But
>> that isn't a decision we need to make here. We can make that prior to
>> launch.
>
> `@safe` `@trusted` and `@system` are already misunderstood as they are,
> we really don't want to throw a fourth attribute into the mix.
Indeed, there are some interesting trade offs here.
But it is an option to give people a way to buy into it (by not being
forced to use it).
>> But I do want to make a point here, owner escape analysis only kicks
>> in and forces effectively const on the owner if:
>
> That's not consistent with this example from the DIP, where there's no
> `scope` or `&field`:
>
> ```D
> struct Top {
> int* field;
> }
>
> void func(ref Top owner) @safe {
> int* field = owner.field;
> // owner is now effectively const, it cannot be mutated
>
> owner = Top.init; // Error: The variable `owner` has a borrow and
> cannot be mutated
> ```
Okay that example is wrong, it was copied from an earlier iteration and
I didn't think it through.
Will fix.
```d
struct Top {
int* field;
}
void func(ref Top owner) @safe {
int** field = &owner.field;
// owner is now effectively const, it cannot be mutated
owner = Top.init; // Error: The variable `owner` has a borrow and
cannot be mutated
owner.field = null; // Error: The variable `owner` has a borrow and
cannot be mutated
if (field !is null) {
writeln(**field);
**field = 2; // ok, fully mutable
}
}
```
>> Giving ``scope`` a default escape set is to allow it to match existing
>> understanding, which does help with communicability.
>
> That's sending mixed messages. On the one hand, this DIP completely
> redefines lifetime semantics and syntax, trying to forget DIP1000 ever
> existed. On the other hand, it adds a special meaning to `scope`
> feigning some sort of backward compatibility, but adding a new double
> meaning to the keyword, which is the very thing the new syntax is
> supposed to fix!
Okay, I can entirely ditch the default escape set for ``scope``. Its not
required, it only exists as a QoL thing.
Done.
Now ``scope`` by itself won't reflect existing behaviors and require
additional annotation to make it completely consistent within the proposal.
>> Yes you are correct.
>>
>> If you were allowed to take a pointer to a by-ref variable and then
>> store it some place you are most likely escaping a pointer.
>
> The address of the variable and the pointer value it holds are two
> different things. So the following becomes impossible to express with
> this DIP:
>
> ```D
> int* global;
>
> int** f(return ref int* v) @safe
> {
> global = v;
> return &v;
> }
> ```
Yes, that is intentional.
>> I'm going to need an example of what you think is not addressed here.
>
> To clarify, the headings in my post are common DIP1000 woes that
> alternative DIPs should have an answer to. Timon has brought up the
> composability problem before:
>
> ```D
> import std.typecons;
> int* y;
> int* foo(){
> int x;
> auto t=tuple(&x,y); // type has to be Tuple!(scope(int*),int*)
> return t[1];
> }
> ```
>
> https://forum.dlang.org/post/qqgjop$kan$1@digitalmars.com
>
> The example could compile, but it doesn't because the entire tuple
> shares one lifetime.
> Another example is item 1 of my post:
> https://forum.dlang.org/post/icoavlbaxqpcnkhijcpy@forum.dlang.org
Yes I'm aware of this one.
It is a complicating factor in the analysis and should be developed
later on.
We did talk about it on Discord.
If we were to do it right now, we can do POD structs and static arrays.
But more adjustment would be needed later on for language tuples.
>> From my perspective the field gets conflated with its containing
>> instance variable and that covers composability.
>
> So this DIP's answer is: tough luck, we're still conflating.
Yes.
>> ``scope`` is not transitive, at least as far as the language knows
>> transitive to mean.
>
> Same here, I meant to say that "lack of transitive scope" is a DIP1000
> woe that the DIP should address. The DIP doesn't have a single example
> where a pointer gets dereferenced and then escaped. What happens to the
> following examples?
>
> ```D
> // Assuming -preview=dip1000
>
> int* deref(scope int** x) @safe => *x; // currently allowed
> // because x gets dereferenced and scope only applies to first indirection
Allowed too, but the return value will have a strong relationship.
``int* deref(@escape(return) int** x) @safe => *x;``
Annotating ``scope`` is optional, as it'll be upgraded by caller if needed.
This function is effectively the ``identity`` functions that I use
throughout the document. So this is covered.
> void main() @safe
> {
> int x, y;
> scope int[] arr = [&x, &y]; // currently not allowed
> // because it requires scope to apply to two levels of pointer
> indirection
That is safe due to conflation and reverse order of cleanup.
> }
> ```
Okay this would be a good addition.
```d
int* transformation(int* input) {
int value;
int*[3] array;
array[0] = input; // `array` has a weak relationship to `input`
array[1] = new int; // GC allocation has no relationships without a
constructor call or initializer to form one
array[2] = &value;
return array[0]; // Error: Variable `array` is owned by the stack due
to the variable `value` and cannot be returned
}
```
More information about the dip.ideas
mailing list