July 21, 2019
On 7/19/2019 1:32 AM, Mike Franklin wrote:
> How can the compiler statically determine whether `foo` and `bar` will return a reference to the same data?

It can't.


> It seems the language or type system must provide this guarantee, which would require a full-fledged ownership/borrowing system to accurately enforce what this DIP is proposing.

A full fledged one disallows accessing mutable global state, and now you know why :-)

(Because the function signature does not tell the complete story when the function accesses mutable global state.)

July 21, 2019
On Friday, 19 July 2019 at 08:01:50 UTC, Walter Bright wrote:
> On 7/18/2019 7:59 PM, David Bennett wrote:
>
>> Also, does it also check simple non-heap references, for example would this become compile time error?
>> 
>> ---
>> @safe void f(scope ref int a, scope ref int b)
>> {
>>       a=1;
>>       assert(b==1);
>> }
>> @safe void main()
>> {
>>      int i = 0;
>>      f(i, i);
>>      assert(i==1);
>> }
>> ---
>
> Yes, because you are passing two mutable references to the same memory.
>
> ...
>
> Since the DIP disallows passing two mutable pointers to the same memory object, any code that does will break.

Thanks for clearing that up, it was not obvious what a "mutable pointer" was in that context as in this example only the data appeared to be mutable and not the pointer.

With that in mind would passing 2 slices to overlaping data cause a compile time error?

---
@safe void f(scope int[] a, scope int[] b)
{
    a[1] = 1;
    assert(b[0]==1);

}
@safe void main()
{
    int[] i = new int[32];
    f(i[0..$], i[1..$]);
    assert(i[1]==1);
}
---

Sorry for all the questions, I believe this DIP or something link it is a good step in the right direction. I'm just trying to update my mental model of D so I can write code with this in mind today and have a better understanding.
July 22, 2019
On Sunday, 21 July 2019 at 09:34:42 UTC, Walter Bright wrote:
> On 7/19/2019 1:32 AM, Mike Franklin wrote:
>> How can the compiler statically determine whether `foo` and `bar` will return a reference to the same data?
>
> It can't.
>
>
>> It seems the language or type system must provide this guarantee, which would require a full-fledged ownership/borrowing system to accurately enforce what this DIP is proposing.
>
> A full fledged one disallows accessing mutable global state, and now you know why :-)

But it's the same issue even without the global state:

--- someLibrary.d
module someLibrary;

import core.stdc.stdlib;

struct S
{
    int a = 1;
    int b = 2;

    ref int foo() @safe
    {
        if ((rand() % 2) == 0)
            return a;
        else
            return b;
    }

    ref int bar() @safe
    {
        if ((rand() % 4) == 0)
            return a;
        else
            return b;
    }
}

--- main.d
import someLibrary;

void doSomething(scope ref int a, scope ref int b) @safe
{
    // whatever...
}

void main() @safe
{
    S s;
    for(int i = 0; i < 100; i++)
    	doSomething(s.foo(), s.bar());
}

I still don't see how the compiler can know statically whether or not `foo()` and `bar()` will return a reference to the same data.

>
> (Because the function signature does not tell the complete story when the function accesses mutable global state.)

That's why a full ownership/borrowing system seems necessary because with such a system (either through the type system or the language's semantics or both), there is only ever one mutable reference to any data, and therefore, attribution of functions and/or function parameters is not required.

It appears a full ownership/borrowing system would make what this DIP is proposing obsolete, and is the only way to properly do what this DIP is proposing.

Mike
July 22, 2019
On Monday, 22 July 2019 at 00:05:12 UTC, Mike Franklin wrote:
> import core.stdc.stdlib;
>
> struct S
> {
>     int a = 1;
>     int b = 2;
>
>     ref int foo() @safe
>     {
>         if ((rand() % 2) == 0)
>             return a;
>         else
>             return b;
>     }
>
>     ref int bar() @safe
>     {
>         if ((rand() % 4) == 0)
>             return a;
>         else
>             return b;
>     }
> }
>
> ...
>
> I still don't see how the compiler can know statically whether or not `foo()` and `bar()` will return a reference to the same data.

Assuming you compile with dip1000 and dip1021, foo and bar in the above code would have to be marked `return` (that is, `this` is a `return ref`) or trigger a compile error.

The compiler would presumably be conservative and assumes that they always return references to overlapping data, which is usually what you want (it's what the Rust compiler does, at least).
July 22, 2019
On Monday, 22 July 2019 at 08:45:11 UTC, Olivier FAURE wrote:

> Assuming you compile with dip1000 and dip1021, foo and bar in the above code would have to be marked `return` (that is, `this` is a `return ref`) or trigger a compile error.

Indeed. That seems to work.

> The compiler would presumably be conservative and assumes that they always return references to overlapping data, which is usually what you want (it's what the Rust compiler does, at least).

I'd rather not make any assumptions.  Walter, what say you?  Given the code below (with added `return` lifetime annotations), how would DIP1021 behave?

--- someLibrary.d
module someLibrary;

import core.stdc.stdlib;

struct S
{
    int a = 1;
    int b = 2;

    ref int foo() @safe return
    {
        if ((rand() % 2) == 0)
            return a;
        else
            return b;
    }

    ref int bar() @safe return
    {
        if ((rand() % 4) == 0)
            return a;
        else
            return b;
    }
}

--- main.d
import someLibrary;

void doSomething(scope ref int a, scope ref int b) @safe
{
    // whatever...
}

void main() @safe
{
    S s;
    for(int i = 0; i < 100; i++)
    	doSomething(s.foo(), s.bar());
}

Thanks,
Mike

July 25, 2019
On Friday, 19 July 2019 at 22:36:40 UTC, Olivier FAURE wrote:
>     byte* identity(ref return byte b) @safe {
>         return &b;
>     }

When I saw this (last week), I was surprised it compiled, but it does (with -dip1000). The returned pointer is scope, but that has a different lifetime from ref returns, which only last for the line that called the function.

I don't think `return &b` should compile (but even with that change, this DIP doesn't make exposing heap memory as ref work with @safe).
July 31, 2019
Implementation:

https://github.com/dlang/dmd/pull/10249
July 31, 2019
On 7/21/2019 5:05 PM, Mike Franklin wrote:
> void main() @safe
> {
>      S s;
>      for(int i = 0; i < 100; i++)
>          doSomething(s.foo(), s.bar());
> }
> 
> I still don't see how the compiler can know statically whether or not `foo()` and `bar()` will return a reference to the same data.

Because they're both returning a reference to `s`. The compiler already checks this with dip25 and dip1000. This DIP just extends this to more checking.

> It appears a full ownership/borrowing system would make what this DIP is proposing obsolete, and is the only way to properly do what this DIP is proposing.

A full OB requires the @live sections. This DIP makes it partially available in @safe sections, enough so containers can control access to their components.

July 31, 2019
On 7/19/2019 6:09 AM, Nick Treleaven wrote:
> Walter - is this correct?

Yup.
July 31, 2019
On Wednesday, 31 July 2019 at 07:09:49 UTC, Walter Bright wrote:
> On 7/19/2019 6:09 AM, Nick Treleaven wrote:
>> Walter - is this correct?
>
> Yup.

After Nick posted this example, Timon posted this refutal:
https://forum.dlang.org/post/qgsroo$1l8n$1@digitalmars.com

The general argument being that Nick's code isn't any more @safe with DIP-1021 than it is now, since even with DIP-1021 you can still use Array to corrupt memory in @safe code.

What is your take on it?