May 06, 2013
On 5/6/13 1:45 PM, Steven Schveighoffer wrote:
> 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?

No. I believe I was very specific about what I destroyed and in all likelihood so do you. Probably at this point we've reached violent agreement a couple of iterations back.

Long story short: binding rvalues to ref is fraught with peril and must be designed very carefully.


Andrei


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

> On 5/6/13 1:45 PM, Steven Schveighoffer wrote:

>> 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?
>
> No. I believe I was very specific about what I destroyed and in all likelihood so do you. Probably at this point we've reached violent agreement a couple of iterations back.

OK, I was confused (seriously, I was not playing devil's advocate here).  We are in agreement (at least at what should be possible).

> Long story short: binding rvalues to ref is fraught with peril and must be designed very carefully.

I think empirical proof from this newsgroup is pretty good evidence.

-Steve
May 06, 2013
On Monday, 6 May 2013 at 14:05:48 UTC, Andrei Alexandrescu wrote:
> template <class T> const T& min(const T& a, const T& b) {
>     return b < a ? b : a;
> }
> ...
> int x = ...;
> auto & weird = min(x, 100);
>

What I see going on is an attempt to double up on the use of ref for twp conflicting purposes. Perhaps part of the solution is to use a new variation of ref that allows rvalues and lvalues, while normal ref continues to disallow rvalues, eg ref vs refr.


void foo(ref b)
{
...
}

ref T min(ref T a, refr T b) {
     ++b; // error refr cannot be modified
     foo(b); // error, cannot pass refr to normal ref
     return b < a ? b : a; // error cannot return refr
 }

The "auto ref" system can then be extended to determine if normal ref or refr is required, and refuse to compile when the rules are violated rather than try and fake a real ref with a temporary, since I would think that's something you'd normally never want done anyway.

A runtime safety check will still be needed for returns of normal ref that may escape.

I can definitely agree on the runtime safety check, but I have doubts about the idea of faking a real ref.

--rt
May 06, 2013
On 5/6/2013 8:31 AM, Andrei Alexandrescu wrote:
> In _this_ case, initializing A with an rvalue of type T compiles and
> subsequently runs with undefined behavior.

This is why D does not allow ref as a storage class for variables.
May 07, 2013
On Monday, May 06, 2013 10:16:57 Steven Schveighoffer wrote:
> Could be the time change, haven't rebooted my Mac since flying back. My clock is correct, but Opera may be confused.

Oh, the wonders of dealing with time... :)

- Jonathan M Davis
May 09, 2013
On 5 May 2013 10:37, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>wrote:

> On 5/4/13 7:31 PM, Walter Bright wrote:
>
>> On 5/4/2013 3:51 PM, w0rp wrote:
>>
>>> Does all of this also mean that a
>>> function with a ref parameter will automagically work with r-values?
>>>
>>
>> Yes.
>>
>
> This is new to me. My understanding is that the discussed design addresses safety, and leaves the rvalue discussion for a future iteration.


I was left under the same impression that Walter also seems to be under.


May 09, 2013

On 04.05.2013 20:33, Walter Bright wrote:
> Static Compiler Detection (in @safe mode):
>
> 1. Do not allow taking the address of a local variable, unless doing a
> safe type 'paint' operation.

I'm not exactly sure what a "safe type paint operation" does, and whether the following has already been considered, but I just like to be assured it has:

Taking a slice of a stack allocated fixed-size array also includes taking its address, so it is also forbidden? This might disallow any range based algorithms on the static array.

May 09, 2013
On Saturday, 4 May 2013 at 18:33:04 UTC, Walter Bright wrote:
> Thanks to the many recent threads on this, and the dips on it, everyone was pretty much up to speed and ready to find a resolution. This resolution only deals with the memory safety issue.

...

What if an argument is captured by a delegate?

import std.stdio;

alias long[100] T;

T delegate() dg;

//ref T foo(ref T i) @safe
void foo(ref T i) @safe
{
   dg = { return i; } ;
   //return i;
}

//ref T bar()
void bar() @safe
{
   T i = 1;
   //return foo(i);
   foo(i);
}

void rewrite_stack() @safe
{
   T tmp = -1;
}

void main()
{
   //T i = bar();
   bar();
   rewrite_stack();
   writeln(dg());
}

I believe that even taking your runtime solution into account there is still flaw in the code which is caused by capturing reference (pointer) to passed object. Since definition of 'foo' may be unavailable, compiler cannot know during issuing call to 'bar' whether to allocate argument on the stack or in the heap.

By the way, lazy+delegate is broken.

auto foo(lazy int i) @safe
{
   return { return i; } ;
}

auto bar() @safe
{
   int i = 4;
   return foo(i);
}

void baz() @safe
{
   int[1] arr = 2;
}

void main() @safe
{
   auto x = bar();
   baz();
   assert(x() is 2); // stack value hijacktion
}

First example: http://dpaste.dzfl.pl/4c84a5e4
Second example: http://dpaste.dzfl.pl/9399adc6
May 09, 2013
On Thursday, May 09, 2013 21:30:00 Rainer Schuetze wrote:
> On 04.05.2013 20:33, Walter Bright wrote:
> > Static Compiler Detection (in @safe mode):
> > 
> > 1. Do not allow taking the address of a local variable, unless doing a safe type 'paint' operation.
> 
> I'm not exactly sure what a "safe type paint operation" does, and whether the following has already been considered, but I just like to be assured it has:
> 
> Taking a slice of a stack allocated fixed-size array also includes taking its address, so it is also forbidden? This might disallow any range based algorithms on the static array.

Asuming that taking the slice of a static array is treated like ref (as @safe) rather than like taking the address of a local variable is (as @system), then we'll have to add similar runtime checks for arrays, and that would be way, way worse given that without purity, they could be assigned to a global dynamic array (or could be assigned to a member variable in a return value even with pure functions). It's fairly clean for ref simply because ref is a storage class and not a type constructor. Array slices on the other hand could escape all over the place.

I'm inclined to believe that taking a slice of a static array should be considered @system just like taking the address of a local variable is considered @system. If I could, I'd even disallow the implicit slicing of static arrays when passing them to functions taking dynamic arrays, but I question that Walter would go that far. But I don't know what we can do other than making slicing static arrays @system given how difficult it would be to have runtime checks catch that.

I'd brought this issue up in the past but had not remembered it during the recent discussions on ref safety. Good catch. We don't want any holes like this to persist.

- Jonathan M Davis
May 26, 2013
On 05/05/2013 12:30 AM, Walter Bright wrote:
> On 5/4/2013 3:03 PM, deadalnix wrote:
>>> Where you miss the point, is that these annotations may be omitted
>>> (and they
>>> are most of the time). When nothing is specified, the lifetime of the
>>> returned
>>> reference is considered to be the union of the lifetime of parameters
>>> lifetime, which is what you want in 99% of cases.
>>
>> Note : We may also choose the lack of explicit lifetime means runtime
>> check as
>> proposed, instead of being an error.
>
> D omits the check when it can prove that the returned ref is not a ref
> to one of the parameters that is local.

ref int foo(ref int a, ref int b);

It's a very nice observation that calling foo with only non-local references means that the returned reference is non-local too.
In a way this works like inout but with a safe default so
that no annotation is needed.

In fact it's also possible to know that these don't return a reference to their parameter.

ref double foo(ref int a);

Struct S {}
ref double foo(ref S a);

It can become somewhat complicated to check though.

Anyhow I think using flow-analysis to omit runtime checks is a nice approach.