On 10 November 2012 21:07, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
On Saturday, November 10, 2012 13:21:42 Manu wrote:
> I'm still not buying this. Here's a common struct I will pass by ref
> (perhaps the most common struct in my industry):
>
> struct Vector { float, x,y,z,w; }
> struct Matrix { Vector xRow, yRow, zRow, wRow; }
>
> Vector mul( scope const ref Matrix m, scope const Vector v)
> {
>   Vector v;
>   // perform a matrix multiply against the vector...
>   // this work uses every single field of the inputs given, but the result
> it produces has to references to the sources.
>   // everything is operated on and copied to the output struct, which is
> returned.
>   return result;
> }
>
> Why should this be a problem?
> The majority of my work-horse structs apply to this pattern. This is what I
> imagine 'scope' to be for...
> The main advantage I expect is that I can have confidence that passing
> rvalues (temporaries) is safe, and that external code won't take references
> to memory that I may not own/control. Is that not the point?
>
> Surely the problem that scope should be protecting against is a pointer to
> any part of the argument escaping. *Copies* of values contained in the
> argument/s are fine.

Hmmmm. scope on value types is pointless, because there are no references to
escape, but if you pass by ref, then it does become possible for a pointer to
the argument to escape,

Precisely, to me, this seems like the ENTIRE POINT of 'in'?  (I originally presumed 'in' implied ref, but I was wrong, 'in ref' is supported however)


but I don't know that that's actually actually covered
by scope. The description for scope in docs is that "ref­er­ences in the pa­
ra­me­ter can­not be es­caped (e.g. as­signed to a global vari­able)." And
taking the address of a local variable (which is the only way that any sort of
reference to the data could escape) is never @safe anyway.

When did '@safe'ty enter into it? Are you saying that a ref variable is somehow a local variable? It's a local pointer to a foreign variable... and a function can usually operate on that data however it likes.
Scope would promise that nothing other than the function I give it to will get its grubby little hands on it.
Let's say that function wanted to call through to some other function and pass the variable along (by ref). Obviously, the second function would also have to have it's inputs marked scope, to promise that it never escapes from there.
I imagine scope similarly to const, once it goes scope, the whole callstack must maintain the scope property, otherwise the outermost function can't trust it anymore.

This makes perfect sense for any function that is likely to receive immediate or local variables by reference (which is extremely common).
It also seems absolutely relevant to the rvalues -> ref thing.

If you passed in a
pointer, and scope were fully working, then you'd be protected against the
pointer escaping, but passing by ref isn't really the same thing. I'd have
thought that taking the address of a variable passed by ref would fall into
pretty much the same camp as taking the address of any other local variable,
which is completely unsafe to escape to the point that I'm not sure that
there's any point in protecting against it. It's just completely stupid to do
anyway and is definitely @system. Outside of taking the address of a ref
parameter, taking the address of a local variable and escpaing it is _always_
going to result in garbage, and ref parameters aren't really references in the
normal sense, so I don't know.

What do you mean 'aren't really references in the normal sense'?


You bring up a good point, but I don't know if it's applicable. Certainly,
without the ref there (like is the case with the Vector that you're passing
in), scope would never do anything, because it doesn't even theoretically have
anything to do. It's purely a value type that's not even being passed by ref.

Correct, scope on a purely value type passed by-value means absolutely nothing.
scope on a pointer parameter means something; I would expect the pointer its self, nor a pointer INTO anything under the pointer could escape.
scope on a by-value parameter that contains pointers (like delegates/slices) has meaning, I presume scope would be transitive like const, apply the pointer rule above.
ref is sugar for a pointer, and the above applies verbatim. If you take the address of the ref argument, you have the pointer, and it mustn't escape, likewise, no pointers or pointer to anything beneath it.

If my imagination of this concept completely wrong?
This sounds useful to me, I can't imagine another scenario where the keyword a) makes sense, and b) is useful...

In general though, putting scope on struct parameters would cause a lot of
problems, because of arrays that they might hold and whatnot. Slices wouldn't
be able to escape (and so copies of the struct wouldn't be able escape without
deep copying, let alone the array itself).

That would be the point though. If you don't want that, then you don't want scope.

That said, I would suggest that it may be considered, for practicality, that immutable members WERE allowed to escape from scope controlled structs...
I think this makes sense, and is certainly practical. Strings are the most likely thing to fall under the scenario you illustrated above. Deep-copying strings (or any similarly immutable data) would be a bit silly.

So, while scope may be very useful
in some such cases (assuming that it worked), it's not necessarily something
that you'd want as a matter of course.

Of course not.

Part of it probably depends on your
programming style though. If you have a lot of functions that take arguments
and don't return anything that was in them ever, then scope is less of a big
deal, but that's the sort of thing that happens a _lot_ in my experience, so
scope would very quickly become extremely annoying.

And actually, to make matters worse, I'm not sure that scope on delegates is
working correctly. I thought that it was, but this code compiles:

import std.stdio;

void delegate() global;

void foo(scope void delegate() del)
{
    global = del;
}

void main()
{
    {
        char[5] bar = "hello";
        foo((){writeln(bar);});
    }
    char[7] baz = "goodbye";

    global();
}

It also prints out "hello", and if a closure had not been allocated, I would
have at least half-expected it to print out "goodb", because I'd have thought
that baz would have been taking up the same memory that bar had been. So, it
looks like scope may be completely and utterly broken at this point. I don't
know.

- Jonathan M Davis