May 27, 2022

On Friday, 27 May 2022 at 07:06:08 UTC, Per Nordlöw wrote:

>

On Thursday, 26 May 2022 at 14:35:57 UTC, deadalnix wrote:

>

Fantastic. Every single one of them is a false positive so far. I now face the situation where I will have deprecation warning forever, or add attribute soup to the program.

Can you briefly highlight what those false positives are?

It's typically complaining about accessors, because reference to member escape. And yes, i know, this is exactly the point of the accessor.

> >

I'd be happy to add attributes for something that could actually track ownership/lifetime. DIP1000 is not that. Adding attributes for that is not worth it.

Would having attributes be inferred for non-templated functions alleviate some these problems? Or isn't that possible because a definition might have forward declarations with qualifiers needing to be in sync with the definition?

Maybe. In the case of trivial accessors, I assume that it would. Nevertheless, that would not convince me that DIP1000 is the right path forward, because that wouldn't be super useful to me.

Consider that the accessors can be used on objects on the heap as much as objects on the stack. DIP1000 is therefore unable to make these accessors safe, simply make it so in a very limited set of circumstances. This is simply not good enough to justify breaking anything.

It would be useful in a static analyzer.

May 27, 2022
On Friday, 27 May 2022 at 09:43:02 UTC, Nick Treleaven wrote:
> On Thursday, 26 May 2022 at 22:54:22 UTC, deadalnix wrote:
>>
>> If immutable instead meant immutable in most places, you you can mutate it with this weird construct, then it is effectively useless as a language construct, because it restrict my expressiveness on one axis without granting me greater expressiveness on another.
>
> scope actually does allow that. Any local heap allocation only passed to scope parameters can be allocated on the stack instead of the heap.
>
> void f(scope T);
>
> T v = new T; // can be stack allocated
> f(v);

I don't think this is a valid optimization, because DIP1000 cannot track indirections. Therefore, I could have an objects within the subgraph reachable from `v` which escape and which itself can reaches back to `v`.

In the case it is possible to optimize, LDC can already do it.

DIP1000 is of no help here.
May 27, 2022
On Friday, 27 May 2022 at 14:36:25 UTC, Nick Treleaven wrote:
> On Friday, 27 May 2022 at 09:43:02 UTC, Nick Treleaven wrote:
>> On Thursday, 26 May 2022 at 22:54:22 UTC, deadalnix wrote:
>>>
>>> If immutable instead meant immutable in most places, you you can mutate it with this weird construct, then it is effectively useless as a language construct, because it restrict my expressiveness on one axis without granting me greater expressiveness on another.
>>
>> scope actually does allow that. Any local heap allocation only passed to scope parameters can be allocated on the stack instead of the heap.
>>
>> void f(scope T);
>>
>> T v = new T; // can be stack allocated
>> f(v);
>
> To finish my point, scope allows using `new` in @nogc functions. And it enables allocation optimizations, so scope can't just be for a static analyzer.

This is fundamentally broken. See https://forum.dlang.org/post/omcottkussnewheixydq@forum.dlang.org .

This is not salvageable.
May 27, 2022
On 5/27/2022 1:58 PM, deadalnix wrote:
> The reason you can't do RC on classes, at least safely, is BECAUSE there is no escape analysis.

The idea is to write the class so its fields are not exposed. All access is controlled by member functions.
May 28, 2022

On Friday, 27 May 2022 at 21:09:49 UTC, deadalnix wrote:

> >

void f(scope T);

T v = new T; // can be stack allocated
f(v);

I don't think this is a valid optimization, because DIP1000 cannot track indirections. Therefore, I could have an objects within the subgraph reachable from v which escape and which itself can reaches back to v.

Can you give an example? The following currently compiles:

@safe @nogc:

class C {
    C g() => this;
}

//C f(C c); // error
C f(scope C c); // OK
//C f(scope C c) { return c.g; } // error

void main()
{
    scope c = new C(); // allocated on the stack, scope currently required
    f(c);
}

If I switch in the third version of f, I get an error with -dip1000:
scopeclass.d(7): Error: scope variable c assigned to non-scope parameter this calling scopeclass.C.g

May 28, 2022
On 5/26/2022 10:25 PM, rikki cattermole wrote:
> I did come up with a design, but I'm not convinced it is entirely doable.
> 
> I explained this to Timon earlier today (so have examples).

I recommend starting a new thread with this.
May 28, 2022
On 5/27/2022 1:53 AM, Dukc wrote:
> DIP1000 is sure a difficult concept to learn,

It did until recently have a serious usability problem in that it was hard to discern what `ref return scope` did. That has been replaced with a simple rule.
May 28, 2022
On Saturday, 28 May 2022 at 21:22:37 UTC, Walter Bright wrote:
> On 5/27/2022 1:53 AM, Dukc wrote:
>> DIP1000 is sure a difficult concept to learn,
>
> It did until recently have a serious usability problem in that it was hard to discern what `ref return scope` did. That has been replaced with a simple rule.

The other big usability issue is the way `scope` works with `ref` parameters. D programmers who haven't already learned DIP 1000's rules generally expect the following two function declarations to be equivalent:

    void foo(scope int** p);
    void bar(scope ref int* p);
May 28, 2022
On Saturday, 28 May 2022 at 21:22:37 UTC, Walter Bright wrote:
> On 5/27/2022 1:53 AM, Dukc wrote:
>> DIP1000 is sure a difficult concept to learn,
>
> It did until recently have a serious usability problem in that it was hard to discern what `ref return scope` did. That has been replaced with a simple rule.

I find it marvelous that you can type this and not be like "wait a minute, something went sideways really badly here".

You really needed borrow and own as semantic. Borrow is adequately represented as scope, and owning as a new type qualifier.

Instead, we get "ref return scope" and the whole damn thing can only track things on the stack.
May 29, 2022
On 28.05.22 12:55, Nick Treleaven wrote:
> On Friday, 27 May 2022 at 21:09:49 UTC, deadalnix wrote:
>>> void f(scope T);
>>>
>>> T v = new T; // can be stack allocated
>>> f(v);
>>
>> I don't think this is a valid optimization, because DIP1000 cannot track indirections. Therefore, I could have an objects within the subgraph reachable from `v` which escape and which itself can reaches back to `v`.
> 
> Can you give an example? The following currently compiles:
> 
> ```d
> @safe @nogc:
> 
> class C {
>      C g() => this;
> }
> 
> //C f(C c); // error
> C f(scope C c); // OK
> //C f(scope C c) { return c.g; } // error
> 
> void main()
> {
>      scope c = new C(); // allocated on the stack, scope currently required
>      f(c);
> }
> ```
> 
> If I switch in the third version of `f`, I get an error with -dip1000:
> scopeclass.d(7): Error: scope variable `c` assigned to non-scope parameter `this` calling scopeclass.C.g

```d
class D{
    C c;
}
class C {
    D d;
    int x=3;
    this(D d)@safe @nogc{
        d.c=this;
        this.d=d;
    }
}
C foo(D d)@nogc @safe{
    scope c = new C(d); // remove `scope` and program does not crash
    return c.d.c; // escape c
}
void main(){
    import std.stdio;
    writeln(foo(new D)); // segfault
}
```

Not sure if this was already in bugzilla, added it:

https://issues.dlang.org/show_bug.cgi?id=23145