May 06, 2013
On Mon, 06 May 2013 11:31:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 5/6/13 11:12 AM, Steven Schveighoffer wrote:
>>> If x > 100, the code is saving a reference to a destroyed temporary.
>>> If you couldn't see it, how many do you expect would see similar
>>> issues in even simpler and cleaner D code?
>>
>> No, I was wondering whether the compiler detects this and keeps the
>> temporary in scope (after all, it is in control of that temporary's
>> lifetime).
>
> It can't.
>
> Consider the body of min isn't known (eliminate templates etc). Then what the compiler sees is a function call that returns a const ref. All it can assume is it's a valid reference which it will subsequently bind to the name given by the caller. The reference will refer therefore to a destroyed rvalue (temporaries are destroyed at the end of the full expression).

Well, given that we intend to infer some special behavior given the types of the parameters, I wouldn't think it was impossible to do the same here.  This would make the rvalue live beyond the expression, so maybe that's not allowed in C++.

> Your example is irrelevant to this discussion because returning an rvalue and subsequently binding it to a const T& is a completely different scenario.

I quote from your original rebuttal:

> ref int min(ref int a, ref int b) { return b < a ? b : a; }
> ...
> int x;
> fun(min(x, 100));

Which is returning an rvalue ref and subsequently binding it to a ref parameter of fun.

Isn't that the same thing?  I would note that my code continued to return the rvalue for chained operator<< calls.

> It would be also sound if it weren't for this:
>
> struct A {
>    A(const T& x) : a(x) {}
>    const T& a;
> };
>
> In _this_ case, initializing A with an rvalue of type T compiles and subsequently runs with undefined behavior.

This seems like a separate ref problem.  But we don't have ref members, so it would require an address-of in D.  That should be forbidden, right?

> I repeat: binding rvalues to ref would make every mistake C++ has done in the area, and add a few original ones. It is not a simple problem; if it seems, more study is required.

I never said it was a simple problem.  I said that if you have solved the escape problem, the logic problem is difficult to solve, but not necessarily required.  Even though it is pointless to bind rvalues to refs in some instances, it's not dangerous memory-wise.

If you are saying we haven't solved the escape problem, that is news to me.  I thought the runtime check solves that.

-Steve
May 06, 2013
On Monday, 6 May 2013 at 15:39:07 UTC, Andrei Alexandrescu wrote:
> Yes, because it's dynamically checked.
>

The check will see that the reference is in the current stack frame and pass.
May 06, 2013
On 5/6/13 11:48 AM, Steven Schveighoffer wrote:
> On Mon, 06 May 2013 11:31:05 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>> Consider the body of min isn't known (eliminate templates etc). Then
>> what the compiler sees is a function call that returns a const ref.
>> All it can assume is it's a valid reference which it will subsequently
>> bind to the name given by the caller. The reference will refer
>> therefore to a destroyed rvalue (temporaries are destroyed at the end
>> of the full expression).
>
> Well, given that we intend to infer some special behavior given the
> types of the parameters, I wouldn't think it was impossible to do the
> same here. This would make the rvalue live beyond the expression, so
> maybe that's not allowed in C++.

I'm not sure I understand what you're suggesting.

>> Your example is irrelevant to this discussion because returning an
>> rvalue and subsequently binding it to a const T& is a completely
>> different scenario.
>
> I quote from your original rebuttal:
>
>> ref int min(ref int a, ref int b) { return b < a ? b : a; }
>> ...
>> int x;
>> fun(min(x, 100));
>
> Which is returning an rvalue ref and subsequently binding it to a ref
> parameter of fun.
>
> Isn't that the same thing?

No. It's a very different thing handled by a special rule in C++.

> I would note that my code continued to return
> the rvalue for chained operator<< calls.

Of course.

>> It would be also sound if it weren't for this:
>>
>> struct A {
>> A(const T& x) : a(x) {}
>> const T& a;
>> };
>>
>> In _this_ case, initializing A with an rvalue of type T compiles and
>> subsequently runs with undefined behavior.
>
> This seems like a separate ref problem. But we don't have ref members,
> so it would require an address-of in D. That should be forbidden, right?

Yes. My point was to illustrate that a special rule that works in a situation can't help another.

>> I repeat: binding rvalues to ref would make every mistake C++ has done
>> in the area, and add a few original ones. It is not a simple problem;
>> if it seems, more study is required.
>
> I never said it was a simple problem. I said that if you have solved the
> escape problem, the logic problem is difficult to solve, but not
> necessarily required. Even though it is pointless to bind rvalues to
> refs in some instances, it's not dangerous memory-wise.
>
> If you are saying we haven't solved the escape problem, that is news to
> me. I thought the runtime check solves that.

It does. But binding rvalues to ref makes bounds check failures more frequent, less predictable, and harder to debug. Failures will be more frequent because there's more chance that a ref refers to a defunct rvalue; less predictable because conditional execution may cause some paths to be rarely exercised; and harder to debug because rvalues come and go following implicit rules, not visible scopes.


Andrei


May 06, 2013
On 5/6/13 11:52 AM, deadalnix wrote:
> On Monday, 6 May 2013 at 15:39:07 UTC, Andrei Alexandrescu wrote:
>> Yes, because it's dynamically checked.
>>
>
> The check will see that the reference is in the current stack frame and
> pass.

No. The check will fail (unless wrongly written).

Andrei
May 06, 2013
On Mon, 06 May 2013 12:03:27 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 5/6/13 11:48 AM, Steven Schveighoffer wrote:
>> On Mon, 06 May 2013 11:31:05 -0400, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> wrote:
>>> Consider the body of min isn't known (eliminate templates etc). Then
>>> what the compiler sees is a function call that returns a const ref.
>>> All it can assume is it's a valid reference which it will subsequently
>>> bind to the name given by the caller. The reference will refer
>>> therefore to a destroyed rvalue (temporaries are destroyed at the end
>>> of the full expression).
>>
>> Well, given that we intend to infer some special behavior given the
>> types of the parameters, I wouldn't think it was impossible to do the
>> same here. This would make the rvalue live beyond the expression, so
>> maybe that's not allowed in C++.
>
> I'm not sure I understand what you're suggesting.

Not suggesting anything.  I was inferring that since the code worked, maybe the compiler was correctly handling it.  And given that we plan to have special rules regarding ref, it's not out of the question C++ might also.

>
>>> Your example is irrelevant to this discussion because returning an
>>> rvalue and subsequently binding it to a const T& is a completely
>>> different scenario.
>>
>> I quote from your original rebuttal:
>>
>>> ref int min(ref int a, ref int b) { return b < a ? b : a; }
>>> ...
>>> int x;
>>> fun(min(x, 100));
>>
>> Which is returning an rvalue ref and subsequently binding it to a ref
>> parameter of fun.
>>
>> Isn't that the same thing?
>
> No. It's a very different thing handled by a special rule in C++.

This isn't helping.  You keep saying its different but not how.  I repeat, isn't it possible to solve the problem of binding rvalues to references?  Yours and my examples seem to say it works in C++, but yet you say it's not feasible in D.  Why is C++ able to handle this while D is not?

>>> It would be also sound if it weren't for this:
>>>
>>> struct A {
>>> A(const T& x) : a(x) {}
>>> const T& a;
>>> };
>>>
>>> In _this_ case, initializing A with an rvalue of type T compiles and
>>> subsequently runs with undefined behavior.
>>
>> This seems like a separate ref problem. But we don't have ref members,
>> so it would require an address-of in D. That should be forbidden, right?
>
> Yes. My point was to illustrate that a special rule that works in a situation can't help another.

Another situation that's already solved?  Don't see the point.

>> If you are saying we haven't solved the escape problem, that is news to
>> me. I thought the runtime check solves that.
>
> It does. But binding rvalues to ref makes bounds check failures more frequent, less predictable, and harder to debug. Failures will be more frequent because there's more chance that a ref refers to a defunct rvalue;

That is a lifetime issue.  We can make the lifetime last long enough for the current statement.

> less predictable because conditional execution may cause some paths to be rarely exercised;

An existing problem, not made worse by rvalue references.

> and harder to debug because rvalues come and go following implicit rules, not visible scopes.

What are the rules?  Maybe we should start there.

-Steve
May 06, 2013
On Mon, 06 May 2013 09:43:38 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> ref int min(ref int a, ref int b) { return b < a ? b : a; }
> ...
> int x;
> fun(min(x, 100));
>
> Here the result of min may be bound to an lvalue or an rvalue depending on a condition. In the latter case, combined with D's propensity to destroy temporaries too early (immediately after function calls), the behavior is silently undefined; the code may pass unittests.

Focusing back on this, I think any rvalues should be treated as though they survive through the end of the statement.  If the compiler can prove they are not in use after partially executing a statement, they can be destroyed early.

Is there any reason this shouldn't be the case?

-Steve
May 06, 2013
On Monday, 6 May 2013 at 16:03:51 UTC, Andrei Alexandrescu wrote:
> On 5/6/13 11:52 AM, deadalnix wrote:
>> On Monday, 6 May 2013 at 15:39:07 UTC, Andrei Alexandrescu wrote:
>>> Yes, because it's dynamically checked.
>>>
>>
>> The check will see that the reference is in the current stack frame and
>> pass.
>
> No. The check will fail (unless wrongly written).
>

You'll have to explain more as I don't see how to make the check work with temporaries that will live in the caller stack frame. By definition they'll be valid if only addresses are checked. But the reference will exceed the lifetime of the returned reference.
May 06, 2013
On 5/6/13 12:17 PM, Steven Schveighoffer wrote:
> On Mon, 06 May 2013 12:03:27 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>> No. It's a very different thing handled by a special rule in C++.
>
> This isn't helping. You keep saying its different but not how.

In one case a reference is returned, in the other an rvalue is returned.

> I repeat,
> isn't it possible to solve the problem of binding rvalues to references?
> Yours and my examples seem to say it works in C++, but yet you say it's
> not feasible in D. Why is C++ able to handle this while D is not?

I explained twice: min and other similar C++ examples are broken.

>> Yes. My point was to illustrate that a special rule that works in a
>> situation can't help another.
>
> Another situation that's already solved? Don't see the point.

No. That situation leads to undefined behavior.


Andrei
May 06, 2013
On 5/6/13 12:20 PM, Steven Schveighoffer wrote:
> On Mon, 06 May 2013 09:43:38 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> ref int min(ref int a, ref int b) { return b < a ? b : a; }
>> ...
>> int x;
>> fun(min(x, 100));
>>
>> Here the result of min may be bound to an lvalue or an rvalue
>> depending on a condition. In the latter case, combined with D's
>> propensity to destroy temporaries too early (immediately after
>> function calls), the behavior is silently undefined; the code may pass
>> unittests.
>
> Focusing back on this, I think any rvalues should be treated as though
> they survive through the end of the statement. If the compiler can prove
> they are not in use after partially executing a statement, they can be
> destroyed early.
>
> Is there any reason this shouldn't be the case?

That should probably be a prerequisite of any working solution.

Andrei
May 06, 2013
On Mon, 06 May 2013 13:28:18 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 5/6/13 12:17 PM, Steven Schveighoffer wrote:
>> On Mon, 06 May 2013 12:03:27 -0400, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> wrote:
>>> No. It's a very different thing handled by a special rule in C++.
>>
>> This isn't helping. You keep saying its different but not how.
>
> In one case a reference is returned, in the other an rvalue is returned.

This is a trimmed down example:

int &foo(int &val) { return val; }

What I read from you (and I could be wrong) is you are saying this is not valid:

foo(foo(foo(1)));

Is that right?

>
>>> Yes. My point was to illustrate that a special rule that works in a
>>> situation can't help another.
>>
>> Another situation that's already solved? Don't see the point.
>
> No. That situation leads to undefined behavior.

In D that situation is invalid.  You can't have ref members.

-Steve