October 24, 2016
On Sunday, 23 October 2016 at 06:42:41 UTC, Walter Bright wrote:
> On 10/22/2016 7:34 PM, Walter Bright wrote:
>> 2. You're right that the above should error, I'll look into it.
>
> Added the fix to the PR.

Somehow my previous (mailed yesterday afternoon) message didn't go through, so I'm going to paste it here again:

Just looked at it.

The diff shows:
+fail_compilation/retscope.d(198): Error: scope variable e may not be returned
+void* escapeDg1(scope void* d) @safe
+{
+    Escaper e;
+    e.DG = d;
+    return e.DG; // L198
+}

So it means that the storage class of 'e' changes during semantic analysis of the function. I don't think that's something we want to ever do.
Crafting a test case that exploit this was trivial:
void* escape3 (scope void* p) @safe
{
    Escaper e;
    scope dg = () { return e.e; };
    e.e = p;
    return dg();
}

Not to mention, if we ever relax the restriction on taking the address of a local variable, as the DIP seems to hint as a goal, you can just do:
Escaper e;
Escaper* pe = &e;
And you'll have the same issue.
October 24, 2016
On 10/24/2016 12:13 PM, Mathias Lang wrote:
> Crafting a test case that exploit this was trivial:
> void* escape3 (scope void* p) @safe
> {
>     Escaper e;
>     scope dg = () { return e.e; };
>     e.e = p;
>     return dg();
> }

I think it is quite possible to keep non-transitive semantics to enable designing correct @trusted wrappers, the problem comes from trying to magically work as valid @safe code.

Really, all we need is for developer to be able to say that some pointer/reference may only be used as long as host aggregate exists. With that transitivity can be emulated by removing direct access to reference-type fields and marking matching getters as scope.



October 24, 2016
On Sunday, 16 October 2016 at 13:45:42 UTC, Dicebot wrote:
> So, there is a pull request (https://github.com/dlang/dmd/pull/5972)
> which is intended to implement DIP1000
> (https://github.com/dlang/DIPs/blob/master/DIPs/DIP1000.md).


One thing about the DIP that I'd like more info on is:

https://github.com/dlang/DIPs/blob/master/DIPs/DIP1000.md#safe
> Errors for scope violations are only reported in @safe code.

Why is this necessary / what would be the problem in allowing it in @system ?
October 24, 2016
On 10/24/2016 2:13 AM, Mathias Lang wrote:
> So it means that the storage class of 'e' changes during semantic analysis of
> the function. I don't think that's something we want to ever do.

Why? D already infers lots of things.

> Crafting a test case that exploit this was trivial:
> void* escape3 (scope void* p) @safe
> {
>     Escaper e;
>     scope dg = () { return e.e; };
>     e.e = p;
>     return dg();
> }

I'll investigate that one.

October 24, 2016
On 10/24/2016 9:49 AM, Mathias Lang wrote:
> Why is this necessary / what would be the problem in allowing it in @system ?

For one thing, it would require all users to learn scope semantics and annotate them correctly. It would be like requiring const correctness.
October 24, 2016
On 10/24/2016 1:13 AM, Stefam Koch wrote:
> That argument can go two ways.
> Since we are already years late another month won't matter that much.

That doesn't work as an argument. Being later makes things worse, not irrelevant. Memory safety is constantly growing in importance. It is a key advantage D can have over C/C++ - one they will have great difficulty matching.


> D's main advantage is hindsight and careful consideration of complexity versus
> power!
> We should stick to those values and trying to beat someone in a marketing hype.

It's not a question of beating. It is a question of becoming irrelevant. We lose design wins constantly because of this issue.

October 24, 2016
On 10/23/2016 11:49 PM, Dicebot wrote:
> On 10/24/2016 05:29 AM, Walter Bright wrote:
>> On 10/22/2016 3:50 PM, Dicebot wrote:
>>> Beauty of borrowing is that it allows to to inc/dec skipping with no
>>> special RC support from compiler and works the very same for stack
>>> allocated data. What you are going at is simply replacing GC with
>>> (compiler enhanced) RC and that is not good at all.
>>
>> I don't see why supporting something beyond RC and GC is necessary.
>
> Both GC and RC are forms of automatic memory management with unavoidable
> associated overhead. Both have their uses but are unacceptable in most
> of the system code. Which means that would would either need to
> duplicate all Phobos/druntime functions to work with both RC and plain
> pointers or it will become even less suitable for low level code than it
> is now.

I'm afraid that is a bit vague. Can you give a more concrete example?


> The problem is DIP1000 is a lie. I started to rewrite it to match your
> implemented semantics but that would simply result in throwing much of
> its content away. Some of crucial things DIP1000 promises to deliver but
> are not possible with design as in https://github.com/dlang/dmd/pull/5972 :
>
> - @system code like std.internal.scopebuffer can be made @safe
> - reference counting systems need not adjust the count when passing
> references that do not escape
>
> That means that any counter-proposal that actually allows to do those
> things would already be considerably better.
>
> Now, I do agree that we don't want to go with amount of complexity Rust
> has. Something that makes basic idioms possible at 10% of complexity
> price would be a much better trade-off. Thus I'd propose to keep most of
> the stuff in DIP1000 as it is (including non-transitivity), but
> enhance/tweak lifetime rules, for example:
>
> - allow taking address of a `ref` and define lifetime of such expression
> to be same of `ref` lifetime

Again, why? Why can't the function return by 'ref'?


> - `return scope` of a struct methods binds returns value lifetime to one
> of the struct instance itself, not composition of its fields

'ref' return already does that.


> It is of similar complexity as existing proposal but is good enough to
> allow design patterns I have been trying to do.

Your design patterns all hinge on requiring conversion of a ref to a *. Why is this necessary, as opposed to using return by 'ref' ?


>> At some point we need to move forward. We are already years late.
>> Another month has slipped by.
> Current implementation does not bring us closer to feature rich
> compiler-verified safety in any way.

The numerous safety bugs it corrects, some reported by you, argues otherwise.


> What is even worse, going with it
> will close the option of using same syntax for something better and we
> will be stuck with it forever.
>
> I have a feeling that you completely misunderstand why Rust system has
> all the hype. Would it help if I explained some of compiler-verified
> safety patterns there? I have put some time into studying Rust last year.

Your understanding of Rust is likely better than mine. But I do know (and Rust documentation says this) that one has to rethink design patterns in order to work with the borrow checker, it cannot just be slapped onto any existing pattern, even if that pattern is correct. Trying to implement a Rust pattern in D is likely to be incorrect as well.


>> I am also not convinced that you cannot do what you want to do with
>> DIP1000. I do not understand why it is necessary to return the address
>> of a value passed by ref, when one can return by ref.
>
> You are author of the proposal. It is your obligation to convince
> everyone that it is possible to do what you claim possible, not mine. If
> you think it can be done, show me the code.

I don't know what you have in mind. I already showed you how it can help with unique pointers. The only concrete issue you have with it is that ref cannot be returned by *. I ask again why is this important as opposed to returning by ref.

October 24, 2016
On 10/24/2016 12:30 PM, Walter Bright wrote:
> Why? D already infers lots of things.

Like @safe, @trusted, @system, @nogc, pure, nothrow, return.

Consider also that Rust does extensive lifetime attribute inference.

October 24, 2016
On Monday, 24 October 2016 at 19:30:16 UTC, Walter Bright wrote:
> On 10/24/2016 2:13 AM, Mathias Lang wrote:
>> So it means that the storage class of 'e' changes during semantic analysis of
>> the function. I don't think that's something we want to ever do.
>
> Why? D already infers lots of things.

To answer your question: Because it isn't even inference.
The specs explicitly says "Unlike module level declarations, declarations within function scope are processed in order." (https://dlang.org/spec/function.html)
Changing the storage class of a previous declaration is then explicitly breaking that line of the spec, and breaking a key assumption the specs / compiler relies on. I pointed that problem using delegate, I could have done so using templates, nested functions, or whatever part of the language that relies on said assumption.

On Monday, 24 October 2016 at 19:32:45 UTC, Walter Bright wrote:
> On 10/24/2016 9:49 AM, Mathias Lang wrote:
>> Why is this necessary / what would be the problem in allowing it in @system ?
>
> For one thing, it would require all users to learn scope semantics and annotate them correctly. It would be like requiring const correctness.

Users will have to learn `scope` semantic to write `@safe` code.
If one doesn't want to learn `scope` semantics, one just doesn't use it, `scope` is opt-in after all.
I fail to see any benefit for someone that doesn't want to learn `scope`, and find it extremely confusing for users that want to use it but can't make their code `@safe` (because for example, they're accepting a delegate which can be @system so they can't just wrap code in `@trusted`).
October 24, 2016
On Monday, 24 October 2016 at 20:57:08 UTC, Walter Bright wrote:
> On 10/24/2016 12:30 PM, Walter Bright wrote:
>> Why? D already infers lots of things.
>
> Like @safe, @trusted, @system, @nogc, pure, nothrow, return.
>
> Consider also that Rust does extensive lifetime attribute inference.

Don't get me wrong, I love inference. I am afraid my feedback may have mixed up two things, so I'm going to reinstate them here:

1) As highlighted in the previous post, we should not change the STC / type of a declaration after it is declared. It's a can of worms and will break in many ways.

2) I don't know a single place in the language where a single "something" (type / attributes / STC) is infered. It's either all or nothing (e.g. you cannot have a function infering safety but not infering nothrow / @nogc, short of tricking the compiler by changing the function's code). Also, I don't know of a single place in the language where a type provided explicitly is infered. For example, if you do `char[] s = "Hello World";`, `const` or `immutable` is not infered, and you get an error, and that's a good thing. Changing it would make code way less readable and harder to reason about.