Thread overview
dip1000: why can't the addressee come into existence later?
Nov 10, 2018
Neia Neutuladh
Nov 10, 2018
Nicholas Wilson
Nov 10, 2018
Neia Neutuladh
Nov 10, 2018
Stanislav Blinov
Nov 11, 2018
Neia Neutuladh
November 10, 2018
The following code doesn't work with @safe -dip1000:

    int* p;
    int i;
    p = &i;

i has a shorter lifetime than p, the compiler complains.

But this code does:

    int i;
    int* p;
    p = &i;

In both cases, p can't point to i before i exists, and p ceases to exist when i ceases to exist.

I believe this is because the first option would let me write:

    struct Foo
    {
      bool exists = true;
      ~this() { exists = false; }
      void doStuff() { assert(exists); }
    }

    Foo* p;
    scope(exit) (*p).doStuff;
    Foo f;
    scope(exit) destroy(f);
    p = &f;

And this will run into the assert when -dip1000 should ensure it can't.

The compiler does this even in the absence of scope guards and destructors because simple, obvious rules will be easier to understand and implement than nuanced ones, even if it makes you reorder declarations sometimes.

Is this right?
November 10, 2018
On Saturday, 10 November 2018 at 06:56:29 UTC, Neia Neutuladh wrote:
> Is this right?

Are you sure you added @safe to the second example?
https://run.dlang.io/is/2RbOwK fails to compile.
November 10, 2018
On Sat, 10 Nov 2018 11:47:24 +0000, Nicholas Wilson wrote:
> On Saturday, 10 November 2018 at 06:56:29 UTC, Neia Neutuladh wrote:
>> Is this right?
> 
> Are you sure you added @safe to the second example? https://run.dlang.io/is/2RbOwK fails to compile.

Maybe take another look at the post you're replying to? I was saying that, if the compiler allowed one thing that looked safe to me, it would either require nuance (check for scope guards, destructors, and the like before saying something is un-@safe) or allow other code that is obviously invalid.
November 10, 2018
On Saturday, 10 November 2018 at 06:56:29 UTC, Neia Neutuladh wrote:
> The following code doesn't work with @safe -dip1000:
>
>     int* p;
>     int i;
>     p = &i;
>
> i has a shorter lifetime than p, the compiler complains.
>
> But this code does:
>
>     int i;
>     int* p;
>     p = &i;
>
> The compiler does this even in the absence of scope guards and destructors because simple, obvious rules will be easier to understand and implement than nuanced ones, even if it makes you reorder declarations sometimes.
>
> Is this right?

Yep, you just over-simplified the first case. Consider:

int* p;
{
    int i;
    p = &i;
}
*p = 42;

or even:

module thing;

int* global;

void foo() {
    int i;
    global = &i;
}

...much simpler to just go by the lifetime, instead of attempting to do a complex analysis. Because for the latter, it would then *need* to be deep to be of any use at all. Especially in a language that has static ifs:

// parameter is not scope, function is not pure, etc.
void nasty(int* p) { /* ... */ }

void main() {
    int *p;
    int i;
    p = &i;
    static if (someCondition) nasty(p);
}

November 11, 2018
On Sat, 10 Nov 2018 16:25:40 +0000, Stanislav Blinov wrote:
> Yep, you just over-simplified the first case.

It is too simple to clearly illustrate why the code is invalid, but not so simple that the compiler accepts that code.

> Consider:
> 
> int* p;
> {
>      int i;
>      p = &i;
> }
> *p = 42;

In that example, the scope for i ends before the scope for p ends. It's not at all surprising that that code is wrong. In the other examples I gave, both i and p go out of scope at the same time.

But there's a total ordering for when variables' lifetimes end, which is the key (and non-obvious) difference between the two.