October 29, 2016
On Saturday, 22 October 2016 at 08:10:51 UTC, Walter Bright wrote:
> On 10/21/2016 8:38 AM, Dicebot wrote:
>    int foo(scope Unique u);
>
> it is guaranteed that u's value does not escape from foo(). So,
>
>    Unique u = malloc();
>    foo(u);
>    free(u); // guaranteed to not leave a dangling pointer

That's unsafe code calling safe code, but the more relevant case is safe code interfacing with trusted code.
October 29, 2016
On 10/27/2016 12:43 PM, Walter Bright wrote:
> I think I thought of a solution. Currently, the design ignores 'scope' if it is applied to a variable with no indirections. But we can make it apply, in that the 'scope' of a function's return value is tied to the 'scope' of the argument, even if that argument has no indirections.
> 
> This should make Dicebot's design work.

This is the step in the right direction but doesn't fix more general issue. While RNG engine example doesn't have any indirections, that won't be true in all cases where one needs to return a scoped range.

Most obvious problem is an owning container type:

struct Tree (T)
{
    private Node!T* head;

    ~this ( ) { delete this.head; }

    TreeRange range ( ); // returned range must not outlive
                         // this struct instance
}

There really needs to be a way to bind returned value lifetime to `this` explicitly. No special cases, no magic, just one of straightforward use cases.



October 29, 2016
On 10/29/2016 4:30 AM, Dicebot wrote:
> Most obvious problem is an owning container type:
>
> struct Tree (T)
> {
>     private Node!T* head;
>
>     ~this ( ) { delete this.head; }
>
>     TreeRange range ( ); // returned range must not outlive
>                          // this struct instance
> }
>
> There really needs to be a way to bind returned value lifetime to `this`
> explicitly. No special cases, no magic, just one of straightforward use
> cases.

This is covered by my proposed modification:

struct Tree
{
    TreeRange range ( ) scope;
}


October 30, 2016
On Sunday, 30 October 2016 at 02:17:36 UTC, Walter Bright wrote:
> This is covered by my proposed modification:
>
> struct Tree
> {
>     TreeRange range ( ) scope;
> }

Maybe I misunderstand what you propose in that case? You have said "if it is applied to a variable with no indirections", but `struct Tree` in my example does contain indirection in form of `head` pointer (as struct is composition of fields).
October 30, 2016
On 10/29/2016 10:33 PM, Dicebot wrote:
> On Sunday, 30 October 2016 at 02:17:36 UTC, Walter Bright wrote:
>> This is covered by my proposed modification:
>>
>> struct Tree
>> {
>>     TreeRange range ( ) scope;
>> }
>
> Maybe I misunderstand what you propose in that case? You have said "if it is
> applied to a variable with no indirections", but `struct Tree` in my example
> does contain indirection in form of `head` pointer (as struct is composition of
> fields).

Since the caller cannot see inside the function, it must rely on the interface. If the returned pointer is or is not derived from the value of the  scope parameter is immaterial, the caller will act as if it is - meaning that the scope of the returned value will be restricted to being the same scope of the argument.

It's just tracking the scope of expressions:

    return x;

    return x + 3;

    return i ? x : y;

so they work through function calls:

    return foo(x);

    return foo(x + 3);

    return foo(x, y);

The 'return scope' and 'return ref' make that work so that the compiler only has to examine the function's interface, not its implementation.
October 31, 2016
On Monday, 31 October 2016 at 06:43:48 UTC, Walter Bright wrote:
> If the returned pointer is or is not derived from the value of the  scope parameter is immaterial, the caller will act as if it is - meaning that the scope of the returned value will be restricted to being the same scope of the argument.

Is it how you want to change the implementation or how you intend it to work now? Because currently it acts differently and no lifetime restriction ever happens in absence of explicit `return scope` annotation:

int* foo ( scope int* );
int* global;

void main () {
    scope int* ptr;
    global = foo(ptr); // compiles
}

-----------------------------

Let's get back to the old snippet as you haven't answered my question:

struct Tree
{
    Node* head;

    TreeRange range ( ) scope;
}

Per my understanding, it is roughly equivalent to this:

struct Tree { Node* head; }
TreeRange range ( scope ref Tree this );

With current rules `scope` here is completely ignored because `this` is already a reference and thus is not allowed to escape. And `TreeRange` lifetime has no relation with `this` lifetime, because such relation is only defined by `return scope`/`return ref`.

Now in http://forum.dlang.org/post/nusi80$4h0$1@digitalmars.com you propose to make so that such `scope` annotation does indeed transfer lifetime: ".. the design ignores 'scope' if it is applied to a variable with no indirections. But we can make it apply". But that does not seem applicable to discussed example as struct Tree does contain indirections.

Another issue is that would be completely out of line from existing difference between `scope` and `return scope`.
October 31, 2016
On 10/31/2016 1:08 AM, Dicebot wrote:
> On Monday, 31 October 2016 at 06:43:48 UTC, Walter Bright wrote:
>> If the returned pointer is or is not derived from the value of the  scope
>> parameter is immaterial, the caller will act as if it is - meaning that the
>> scope of the returned value will be restricted to being the same scope of the
>> argument.
>
> Is it how you want to change the implementation or how you intend it to work
> now? Because currently it acts differently and no lifetime restriction ever
> happens in absence of explicit `return scope` annotation:
>
> int* foo ( scope int* );
> int* global;
>
> void main () {
>     scope int* ptr;
>     global = foo(ptr); // compiles
> }

Amending it as follows:

  @safe int* foo ( return scope int* );
  int* global;

  @safe void main () {
    scope int* ptr;
    global = foo(ptr); // compiles
  }

Produces:

  ..\dmd bug -transition=safe
  DMD v2.069 DEBUG
  bug.d(6): Error: scope variable ptr assigned to global with longer lifetime

which works as intended and is the current behavior.

'scope' means the value does not escape the function.

'return scope' means the value does not escape the function, but may be returned.

It's analogous to 'ref' and 'return ref' parameters. It works the same way, except it pertains to the value of the argument rather than its address.

I'm a bit baffled why I am unable to explain this.



> -----------------------------
>
> Let's get back to the old snippet as you haven't answered my question:
>
> struct Tree
> {
>     Node* head;
>
>     TreeRange range ( ) scope;
> }
>
> Per my understanding, it is roughly equivalent to this:
>
> struct Tree { Node* head; }
> TreeRange range ( scope ref Tree this );

Yes. Note that you can ALWAYS rewrite member functions as normal functions with the 'this' parameter as a regular parameter. There is nothing magically different about them, they MUST MUST MUST behave the same way. Otherwise, nothing will make sense.


> With current rules `scope` here is completely ignored because `this` is already
> a reference and thus is not allowed to escape. And `TreeRange` lifetime has no
> relation with `this` lifetime, because such relation is only defined by `return
> scope`/`return ref`.

Again, 'ref' refers to the ADDRESS of the argument. 'scope' refers to the VALUE of the argument.

1. 'return ref' says that the function returns that parameter by 'ref' (or is treated as if it did).

2. 'return scope' says that the function returns that parameter by value (or is treated as if it did).

3. 'return ref scope' is treated as 'return ref' and 'scope' if the function returns by ref.

4. 'return ref scope' is treated as 'ref' and 'return scope' if the function does not return by ref.

Therefore,

    TreeRange range(scope ref Tree this);

is case 4, and the return value of 'range' will have the scope of the value of the argument corresponding to 'this', which corresponds to the scope of the argument.

> Now in http://forum.dlang.org/post/nusi80$4h0$1@digitalmars.com you propose to
> make so that such `scope` annotation does indeed transfer lifetime: ".. the
> design ignores 'scope' if it is applied to a variable with no indirections. But
> we can make it apply". But that does not seem applicable to discussed example as
> struct Tree does contain indirections.

My proposed change is to make it apply even if it does not contain indirections.

> Another issue is that would be completely out of line from existing difference
> between `scope` and `return scope`.

I don't think that is at all the case.

October 31, 2016
On 10/31/2016 11:53 AM, Walter Bright wrote:
> I'm a bit baffled why I am unable to explain this.

Walter it is very uncomfortable to see that are trying to explain me my own words (".. no lifetime restriction ever happens in absence of explicit `return scope` annotation.. ") as if I don't understand that. It sometimes feels like you spot some random keywords and completely ignore actual content of the question.

You are unable to explain simply because you don't explain what was asked. I know the difference between `scope` and `return scope`, and I ask questions exactly because I know it and last proposal doesn't make sense with it.

See below:

> 4. 'return ref scope' is treated as 'ref' and 'return scope' if the function does not return by ref.
> 
> Therefore,
> 
>     TreeRange range(scope ref Tree this);
> 
> is case 4, and the return value of 'range' will have the scope of the value of the argument corresponding to 'this', which corresponds to the scope of the argument.

This makes no sense because declaration is not `TreeRange range(return
ref scope Tree this)`, it is `TreeRange range(ref scope Tree this)`
(absence of `return` is critical!)

Does that mean that you have actually meant you first example to look like this:

struct Tree
{
    TreeRange range () return ref scope
}

?



October 31, 2016
On 10/31/2016 3:13 AM, Dicebot wrote:
>> 4. 'return ref scope' is treated as 'ref' and 'return scope' if the
>> function does not return by ref.
>>
>> Therefore,
>>
>>     TreeRange range(scope ref Tree this);
>>
>> is case 4, and the return value of 'range' will have the scope of the
>> value of the argument corresponding to 'this', which corresponds to the
>> scope of the argument.
>
> This makes no sense because declaration is not `TreeRange range(return
> ref scope Tree this)`, it is `TreeRange range(ref scope Tree this)`
> (absence of `return` is critical!)
>
> Does that mean that you have actually meant you first example to look
> like this:
>
> struct Tree
> {
>     TreeRange range () return ref scope
> }
>
> ?
>

Yeah, sorry, the 'return' is necessary as you said.
November 01, 2016
On 10/25/2016 5:59 PM, Martin Nowak wrote:
> On Tuesday, 25 October 2016 at 10:05:50 UTC, Walter Bright wrote:
>> One way of fixing it besides doing multiple passes is noting its use inside
>> dg, and setting a flag saying it cannot be made scope, and so the scoped
>> assignment becomes an error.
>
> A tristate tainting, unkown/scope/escape, should indeed work here.

I implemented it, but now the PR got thoroughly fouled up after I rebased it on master.