May 05, 2013
On 5/4/2013 4:51 PM, Timon Gehr wrote:
> What is the point? Rust conservatively assumes this by default.

We could do that, too, and then disallow all code that looks like:

      ref T foob(ref U u);

      ref T bar() { U u; return foob(u); }

which I doubt would be very popular. Or we could add "scope ref" annotations everywhere, which brings another set of problems as I pointed out.

I.e. there is no free lunch with this. Rust uses annotations, it doesn't have a clever way to not have them. The choices are:

1. use annotations
2. issue error on otherwise useful cases
3. add runtime check
4. put 'suspicious' locals on the heap, like what is done for closures

We decided that (3) was the most practical and was the easiest for users to deal with.
May 05, 2013
Couple amendments:

On 5/4/13 2:33 PM, Walter Bright wrote:
> Case B:
> ref T foob(ref U u) { return u.t; } // note that T is derivable from U
> ref U bar() { T t; return foob(t); }

That's not derivable, it's embedded: type U transitively has a member of type T.

Same case applies to statically-sized arrays:

ref T foob(ref T[42] u) { return u[13]; }
ref T[42] bar() { T[42] t; return foob(t); }

Here the notion that a statically-sized arrays behaves much like a struct is applicable. This case probably deserves notice too:

ref T fooa(ref T t) { return t; }
ref T bar() { T[42] t; return fooa(t[13]); }

> 1. Always involves a return statement.

Except if pointers are used, which leaves the question of what we do when people take the address of refs returned by functions.

> 2. The return type must always be the type of the stack variable or a
> type type derived from a stack variable's type via safe casting or
> subtyping.

That's not subtyping, it's transitive member access. Here transitive goes through members but not through indirections. Not sure how to call that to not make it confusing.

> 3. Returning rvalues is the same issue, as rvalues are always turned
> into local stack temporaries.

The complicating factor here is that lvalues have well-understood lifetimes whereas rvalues are more subtle and opened to subtleties and interpretations. I think right now D destroys temporaries too early.

> 4. Whether a function returns a ref derived from a parameter or not is
> not reflected in the function signature.

Yes! That's why any static solution is either conservative or complicates the language.

> 5. Always involves passing a local by ref to a function that returns by
> ref, and that function gets called in a return statement.

There's also the case of e.g. "return *p;" and "return a[13];".


Andrei
May 05, 2013
On 5/4/13 4:32 PM, Diggory wrote:
> So just to be clear, "ref" parameters can now take rvalues?

That part of the design isn't finished yet.

Andrei

May 05, 2013
On 5/4/13 5:22 PM, deadalnix wrote:
> I still think this is inferior to Rust's solution and like to see ref as
> a equivalent of the Rust burrowed pointer.

http://static.rust-lang.org/doc/tutorial-borrowed-ptr.html

Andrei

May 05, 2013
On 5/4/13 7:03 PM, Andrej Mitrovic wrote:
> This "resolution" should be a DIP that goes through a review just like
> all the other DIPs, otherwise DIPs are pointless if they get overruled
> by some behind-the-scenes conversation.

Yes.

Andrei
May 05, 2013
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.

Andrei

May 05, 2013
Andrei Alexandrescu:

> http://static.rust-lang.org/doc/tutorial-borrowed-ptr.html

The management of pointers is one of the most refined parts of the Rust design. It offers safety, allows per-thread GCs, and more. It's powerful but it also adds some complexity to the language.

Bye,
bearophile
May 05, 2013
On 5/4/2013 5:02 PM, deadalnix wrote:
> On Saturday, 4 May 2013 at 23:30:01 UTC, Walter Bright wrote:
>> On 5/4/2013 3:50 PM, deadalnix wrote:
>>> Require isn't the right word, or you hav to explain yourself much more.
>>
>> You need an explicit annotation if a ref parameter is returned by ref by that
>> function. This is what Rust's annotations do.
>>
>> Consider:
>>
>>     ref T foob(ref U u) { return u.t; }
>>
>>     ref T bar() { U u; return foob(u); }
>>
>> The compiler cannot know that the ref return of foob is referring to local u
>> (as opposed to, say, a ref to a global) unless it is annotated to say so. Rust
>> is no different.
>
> This code sample won't require any annotation in Rust.

If the compiler accepts that code, it will crash at runtime. If it doesn't accept that code, then it will also disallow legitimate code like:

     ref T foob(ref U u) { static T t; return t; }

     ref T bar() { U u; return foob(u); }

> And it illustrate
> wonderfully what I'm saying : most people in the discussion (and it has been
> shown now that this includes you) were unaware of how does Rust solve the problem.
>
> I don't think excluding a solution that isn't understood is the smartest thing
> to do.

I suggest you enumerate the cases with a Rust-like system and show us how it solves the problem without annotations. Note that Rust has pretty much zero real world usage - it's one thing to say needing to use annotations is 'rare' and another to know it based on typical usage patterns of the language.

For example, if the default is "assume the ref return refers to the ref parameter", then some containers would require the annotation and some would not. This is not very viable when doing generic coding, unless you are willing to provide two copies of each such function - one with the annotations and the other without.

Note also that if you have A calls B calls C, the annotation on C doesn't propagate up to B, again leading to a situation where you're forced to make two versions of the functions.

(I say doesn't propagate because in a language that supports separate compilation, all the compiler knows about a function is its signature.)

May 05, 2013
On Saturday, May 04, 2013 20:37:36 Andrei Alexandrescu 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.

That is definitely where things were when we ended the discussion on Wednesday night. Walter favored making ref accept rvalues, but we never agreed on that. Manu was still in favor of scop ref (and David Nadlinger agreed with him IIRC), and you and I were arguing for auto ref to designate that a function accepts rvalues. We all agreed on the bounds check solution for @safety, but we explicitly tabled the discussion about accepting rvalues, because it was getting late, and we'd already been discussing it / arguing about it for quite some time. So, unless further discussion occurred after that which I missed, there is still no agreement on how to handle having a parameter accept both lvalues and rvalues by ref.

- Jonathan M Davis
May 05, 2013
On Sunday, May 05, 2013 01:03:17 Andrej Mitrovic wrote:
> On 5/4/13, Walter Bright <newshound2@digitalmars.com> wrote:
> > Andrei & I argued that we needed to make it work with just ref annotations.
> 
> So to recap, 2.063 turns slices into r-values which will break code that used ref, e.g.:
> 
> -----
> void parse(ref int[] arr) { }
> 
> void main()
> {
>     int[] arr = [1, 2];
>     parse(arr[]);  // ok in 2.062, error in 2.063
> }
> -----
> 
> Then the user might introduce a non-ref overload:
> 
> -----
> void parse(ref int[] arr) { }
> void parse(int[] arr) { }  // picks this one
> -----
> 
> And later down the road, maybe even in 2.064, ref will take r-values making the new code error because of ambiguity between the two functions.
> 
> Has code breakage ever been taken into account during this dconf conversation?
> 
> I doubt a short verbal conversation can solve design problems or take into account all edge-cases, this is why we have the web where we can document all code samples and the flaws of some design spec.
> 
> This "resolution" should be a DIP that goes through a review just like all the other DIPs, otherwise DIPs are pointless if they get overruled by some behind-the-scenes conversation.

The rvalue part wasn't agreed upon, just the @safety solution. I'm sure that the @safety solution can be discussed further if there's dissension, but it's completely non-breaking change (the only case where you'd get an Error is one where the code was operating on a variable which had already left scope and been destroyed). So, I wouldn't expect there to be any real issues with that. The rvalue portion, however, definitely needs further discussion. Walter was in favor of ref accepting rvalues (I think that he though that accepting rvalues was only a safety issue, but I'm not sure), so maybe that's why he was thinking that that was resolved, but there was certainly no agreement on it even between him and Andrei, let alone among the rest of us.

- Jonathan M Davis