April 27, 2013
On 27 April 2013 05:31, Jonathan M Davis <jmdavisProg@gmx.com> wrote:

> On Friday, April 26, 2013 14:15:07 Manu wrote:
> > > I mean is that the way that auto ref should work with non-templated
> > > functions
> > > is that
> > >
> > > auto foo(auto ref T param) {...}
> > >
> > > becomes
> > >
> > > auto foo(ref T param) {...}
> > >
> > > underneath the hood. Then when you pass an rvalue to it - e.g.
> foo(T(5)) -
> > > that gets translated to something like
> > >
> > > T __temp = T(5);
> > > foo(__temp);
> > >
> > > Then auto ref works with both lvalues and rvalues with only one
> function
> > > definition, and ref is unchanged in how it works (it still only accepts
> > > lvalues).
> >
> > Why bother with 'auto'? Why not just make this default behaviour?
>
> For the same reason that T& doesn't take rvalues in C++ while const T&
> does.
> There's a big difference between wanting an argument to be passed as
> efficiently
> as possible and specifically wanting to alter the argument being passed in.
> Plain ref is for cases where you explicitly want to mutate the argument.
> You're just asking for bugs if you allow ref to accept rvalues. We've had
> problems like this before when some literals were treated as lvalues. The
> behavior of a function which takes its argument by ref and the behavior of
> one
> which takes its argument by auto ref are fundamentally different.
>

So you're saying it should be const ref instead of auto ref... I agree.


April 27, 2013
On Saturday, April 27, 2013 10:21:32 Manu wrote:
> > > Why bother with 'auto'? Why not just make this default behaviour?
> > 
> > For the same reason that T& doesn't take rvalues in C++ while const T&
> > does.
> > There's a big difference between wanting an argument to be passed as
> > efficiently
> > as possible and specifically wanting to alter the argument being passed
> > in.
> > Plain ref is for cases where you explicitly want to mutate the argument.
> > You're just asking for bugs if you allow ref to accept rvalues. We've had
> > problems like this before when some literals were treated as lvalues. The
> > behavior of a function which takes its argument by ref and the behavior of
> > one
> > which takes its argument by auto ref are fundamentally different.
> 
> So you're saying it should be const ref instead of auto ref... I agree.

Not at all. const is so much more restrictive in D that it really doesn't make sense to have const be required in order to be able to pass an argument to a function efficiently without caring whether it's an rvalue or an lvalue. auto ref permits const but doesn't require it - which is what we need - whereas const ref is always const.

I'm arguing that we need an attribute which differs from naked ref which indicates that the function doesn't care whether it's given an lvalue or an rvalue - it just wants it to be passed as efficiently as possible. auto ref is supposed to serve this purpose. Naked ref on the other hand is specifically for when the function needs to alter the argument and not a copy of the argument. const ref is ultimately kind of useless IMHO.

The only real hangup with that at this point is that templated functions should be able to use the auto ref solution that non-templated functions should use (invisibly creating a variable when an rvalue is passed so that an lvalue can be passed), but we also need the current auto ref functionality that we have for templated functions. So, either we need a new attribute, or we need to do what Timon suggested and make the compiler smart enough to figure out when it can get away with using the non-templated auto ref solution with a templated function without changing the function's semantics.

- Jonathan M Davis
April 27, 2013
On 27 April 2013 12:26, Jonathan M Davis <jmdavisProg@gmx.com> wrote:

> On Saturday, April 27, 2013 10:21:32 Manu wrote:
> > > > Why bother with 'auto'? Why not just make this default behaviour?
> > >
> > > For the same reason that T& doesn't take rvalues in C++ while const T&
> > > does.
> > > There's a big difference between wanting an argument to be passed as
> > > efficiently
> > > as possible and specifically wanting to alter the argument being passed
> > > in.
> > > Plain ref is for cases where you explicitly want to mutate the
> argument.
> > > You're just asking for bugs if you allow ref to accept rvalues. We've
> had
> > > problems like this before when some literals were treated as lvalues.
> The
> > > behavior of a function which takes its argument by ref and the
> behavior of
> > > one
> > > which takes its argument by auto ref are fundamentally different.
> >
> > So you're saying it should be const ref instead of auto ref... I agree.
>
> Not at all. const is so much more restrictive in D that it really doesn't
> make
> sense to have const be required in order to be able to pass an argument to
> a
> function efficiently without caring whether it's an rvalue or an lvalue.
> auto
> ref permits const but doesn't require it - which is what we need - whereas
> const ref is always const.
>
> I'm arguing that we need an attribute which differs from naked ref which
> indicates that the function doesn't care whether it's given an lvalue or an
> rvalue - it just wants it to be passed as efficiently as possible. auto
> ref is
> supposed to serve this purpose. Naked ref on the other hand is
> specifically for
> when the function needs to alter the argument and not a copy of the
> argument.
> const ref is ultimately kind of useless IMHO.
>
> The only real hangup with that at this point is that templated functions
> should be able to use the auto ref solution that non-templated functions
> should use (invisibly creating a variable when an rvalue is passed so that
> an
> lvalue can be passed), but we also need the current auto ref functionality
> that we have for templated functions. So, either we need a new attribute,
> or
> we need to do what Timon suggested and make the compiler smart enough to
> figure
> out when it can get away with using the non-templated auto ref solution
> with a
> templated function without changing the function's semantics.
>

I don't see myself ever getting on board with this auto-ref idea. I just think it's crazy. It makes no sense to me, they are completely unrelated concepts. It will only lead to confusion.

I'm back at scope-ref. Kenji is right as far as I'm concerned. Not to
mention, he actually did the work.
It makes perfect sense, and I can't see any reason why auto-ref should be
used instead of something that actually makes intuitive sense, and people
will eventually want to use anyway...


April 27, 2013
> I don't see myself ever getting on board with this auto-ref idea. I just
> think it's crazy. It makes no sense to me, they are completely unrelated
> concepts. It will only lead to confusion.
>
> I'm back at scope-ref. Kenji is right as far as I'm concerned. Not to
> mention, he actually did the work.
> It makes perfect sense, and I can't see any reason why auto-ref should be
> used instead of something that actually makes intuitive sense, and people
> will eventually want to use anyway...

I'm against "scope ref" as the one thing "scope" means is that the parameter cannot be returned, and preventing rvalue references being returned breaks the one thing they are useful for.

The primary use of rvalue references in C++ is so that these three cases work:
vector<T> vec;
T t;
vec.push_back(T()) // Move semantics (temporary is destroyed in the process)
vec.push_back(t) // Copy semantics ('t' is unmodified)
vec.push_back(move(t)) // Move semantics ('t' is destroyed in the process)

This allows best efficiency (move is at least as fast as copy, so when the original is no longer needed a move should be performed)

The way it works is that push_back() has two overloads:
push_back(T&& v) {
    // Move 'v' into the vector, afterwards 'v' will have had its guts ripped out so to speak...
}
push_back(const T& v) {
    // Copy 'v' into the vector, 'v' is unmodified
}

- r-values such as 'T()' will default to the first overload.
- l-values such as 't' will default to the second overload.
- 'move(t)' returns an r-value causing the first overload to be called.

As you can see, using "scope ref" will break this third case because 'move()' won't be able to return an r-value, and if only the first two cases are going to be possible, this can be done without any special notion of r-value references anyway.

I'm starting to think there does need to be new syntax if all three of the above cases are to be covered - it would work just making "ref" accept rvalues but then "move" would be the default for lvalues, and that would be confusing - but I don't think using "scope" solves any problems, as the semantics required for rvalues are orthogonal to what "scope" actually means.

As good as it is to avoid adding new keywords, I think this might be a case where it's warranted...
April 27, 2013
On 27 April 2013 13:29, Diggory <diggsey@googlemail.com> wrote:

> I don't see myself ever getting on board with this auto-ref idea. I just
>> think it's crazy. It makes no sense to me, they are completely unrelated concepts. It will only lead to confusion.
>>
>> I'm back at scope-ref. Kenji is right as far as I'm concerned. Not to
>> mention, he actually did the work.
>> It makes perfect sense, and I can't see any reason why auto-ref should be
>> used instead of something that actually makes intuitive sense, and people
>> will eventually want to use anyway...
>>
>
> I'm against "scope ref" as the one thing "scope" means is that the parameter cannot be returned, and preventing rvalue references being returned breaks the one thing they are useful for.
>

It would have to return 'scope ref' also. This returns ownership to the calling statement, which is fine, because it is where the temporary originated in the first place.


The primary use of rvalue references in C++ is so that these three cases
> work:
> vector<T> vec;
> T t;
> vec.push_back(T()) // Move semantics (temporary is destroyed in the
> process)
> vec.push_back(t) // Copy semantics ('t' is unmodified)
> vec.push_back(move(t)) // Move semantics ('t' is destroyed in the process)
>
> This allows best efficiency (move is at least as fast as copy, so when the original is no longer needed a move should be performed)
>
> The way it works is that push_back() has two overloads:
> push_back(T&& v) {
>     // Move 'v' into the vector, afterwards 'v' will have had its guts
> ripped out so to speak...
> }
> push_back(const T& v) {
>     // Copy 'v' into the vector, 'v' is unmodified
> }
>
> - r-values such as 'T()' will default to the first overload.
> - l-values such as 't' will default to the second overload.
> - 'move(t)' returns an r-value causing the first overload to be called.
>
> As you can see, using "scope ref" will break this third case because 'move()' won't be able to return an r-value, and if only the first two cases are going to be possible, this can be done without any special notion of r-value references anyway.
>
> I'm starting to think there does need to be new syntax if all three of the above cases are to be covered - it would work just making "ref" accept rvalues but then "move" would be the default for lvalues, and that would be confusing - but I don't think using "scope" solves any problems, as the semantics required for rvalues are orthogonal to what "scope" actually means.
>
> As good as it is to avoid adding new keywords, I think this might be a case where it's warranted...
>

scope ref T func(scope ref T t) { return t; }

I think this solves the problem.


April 27, 2013
On 4/27/13 2:37 AM, Manu wrote:
> scope ref T func(scope ref T t) { return t; }
>
> I think this solves the problem.

Consider:

scope ref T func(scope ref T t) { return t; }
scope ref T func2() { T t; return func(t); }

Would be great if you went through all of the existing work on this. Well after you're done preparing your DConf talks :o).


Andrei
April 27, 2013
You often say that 'auto ref' can not work for non-templates.
Why not really?
Because the resulting code bloat is not bearable, or because it is technically not possible?

I ask because actually this would be the best performing solution.
----
void foo(auto ref const A a) { }
//is converted to:
void foo(ref const A a) { }
//and
void foo(const A a) {
    return foo(a); //call ref version
}
----

And it work like:
----
A a = A(42);
foo(a); // call foo(ref const A a)
foo(A(23)); // call foo(const A a)
----
So no postblit is called and there is no temporary on the caller side.


Also I would like to see an answer of my overlooked question (just out of curiosity): http://forum.dlang.org/thread/kl4v8r$tkc$1@digitalmars.com?page=7#post-kyicmdsriwnxqiuzkaho:40forum.dlang.org
April 27, 2013
On Saturday, 27 April 2013 at 11:11:05 UTC, Andrei Alexandrescu wrote:
> On 4/27/13 2:37 AM, Manu wrote:
>> scope ref T func(scope ref T t) { return t; }
>>
>> I think this solves the problem.
>
> Consider:
>
> scope ref T func(scope ref T t) { return t; }
> scope ref T func2() { T t; return func(t); }
>
> Would be great if you went through all of the existing work on this. Well after you're done preparing your DConf talks :o).
>
>
> Andrei

Given the current way thing bind, scope bind to the function (ie the implicit parameter here, probably) and not the return type. That is annoying as hell, and getting into the way all the time.
April 27, 2013
On 27 April 2013 21:11, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>wrote:

> On 4/27/13 2:37 AM, Manu wrote:
>
>> scope ref T func(scope ref T t) { return t; }
>>
>> I think this solves the problem.
>>
>
> Consider:
>
> scope ref T func(scope ref T t) { return t; }
> scope ref T func2() { T t; return func(t); }
>

How does auto-ref address this problem?

I think the solution (and I think you proposed this yourself), is that it
must assume the lifetime of the returned ref to be the same as the argument
with the shortest life, since it can't know which argument was returned,
and it would need to be conservative.
In this case, it knows the lifetime of the argument 't', and that it's a
local. Since it is the shortest life argument, it can't return it from
func2(), because it knows the returned ref could be(/is, in this case) the
local supplied.

In the event the function was inlined, maybe there is opportunity to lift this restriction, since it can know which argument was returned.

Would be great if you went through all of the existing work on this. Well
> after you're done preparing your DConf talks :o).


I've read the bug where you made your proposals and associated threads. I
think you actually proposed this same solution iirc?
I'm only adding to it that it makes sense for 'scope' to be present,
otherwise you eliminate other useful cases where you may want to return an
arbitrary ref that's not an argument at all.
The rule detailed above should only apply to scope-ref, since it's the only
case that could possibly deal with short-lived temporaries anyway.
If scope ref were implemented in this way, it could receive temp's of
r-values, and safely receive locals. I would then disallow passing local
variables to non-scope-ref args, as this remains fundamentally unsafe, as
it is now.
vanilla 'ref' becomes useful for receiving and returning unrestricted
references, but we gain the confidence that it can't deal with short-lived
stack variables, which solves the problem we have now.

The only issue I've seen raised that I'm not sure of a good solution for is
the one walter raised of conditionally executed statements. But I don't
think that's addressed by any of the designs discussed.
I haven't thought about that yet, but I'm sure a solution exists. I suspect
it will have something to do with splitting the statement containing
conditions into non-conditional sub-statements, and treating lifetimes
normally within the sub-statements...

On a side note, my second talk is almost done. Huzzah! I have slides... it's a bit rough, but it's good enough for jazz. And it's a long flight...


April 27, 2013
>
> scope ref T func(scope ref T t) { return t; }
>
> I think this solves the problem.

It doesn't for several reasons:
- You're completely changing the meaning of "scope" for one specific case
- This will break DIP25A which only works if scope values cannot be returned
- "scope" binds to the function rather than the return type, so in this case is equivalent to making the hidden "this" pointer a "scope" parameter.