rvalue references

Zach the Mystic reachzach at gggggmail.com
Tue Apr 23 12:45:35 PDT 2013


On Tuesday, 23 April 2013 at 03:30:35 UTC, Walter Bright wrote:
> Previous discussions:
>
> http://forum.dlang.org/thread/4F84D6DD.5090405@digitalmars.com#post-4F84D6DD.5090405:40digitalmars.com
>
> http://d.puremagic.com/issues/show_bug.cgi?id=9238

This is of course an important issue. I don't know if it can be 
divorced from the larger ref safety issue, the reason being that 
you don't want to allow rvalue references which aren't safe. And 
the ref safety issue in itself has some subtleties.

With this in mind, my analysis so far distinguishes three 
(unimplemented) features which it might be desirable for any 
given ref parameter to have. Let's say a ref parameter which 
accepts an rvalue be tagged with '@rval' for the purpose of this 
analysis. And one which may not return itself as a reference as 
'@noreturn'. And one which does not assign by reference to any 
heap, static, or other ref parameter as '@noassign'.

I believe an important goal is to see how much mileage can be 
gotten out of existing syntaxes. The two existing syntaxes in 
question are 'ref' and 'auto ref'. I will leave 'auto ref' aside 
for now.

DIP25: Sealed References is divided into two parts, the first 
part dealing with 'ref' parameters, and the second with the '&' 
operator. In the first part ('DIP25A'), 'ref' is defined in such 
a way as to make it completely safe. This makes perfect sense as 
a beginning, and although it imposes significant restrictions, I 
don't have a great way to know just how much existing code would 
be affected by them. My ideal way to procede would be to 
implement DIP25A and to field test it on existing code with 
willing participants, with an eye toward finding the most common 
and general use cases where DIP25A was indeed too restrictive for 
everyday use. In my DIP35, I suggested the 'copy' function, as 
opposed to Andrei's 'identity' function, which I thought might be 
sufficiently common to warrant an addition to DIP25A, but I'm 
shooting somewhat in the dark without real world data.

The parameter attribute which would alleviate the problem with 
the 'copy' function is '@noreturn' (or some variant), which is 
pretty straightforward as to its functionality.

ref T func(ref T x, @noreturn ref T y) {
   return x; // pass
   return y; // error
   static T* t = &y; // pass!?
}

As you can see, '@noret' is pretty limited in that it doesn't 
actually prevent assigning to a global reference, but it is good 
for the purposes of this analysis. '@noassign' is the opposite.

ref T func(ref T x, @noassign ref T y, ref T* z) {
   return x; // pass
   return y; // pass
   z = &x; // pass
   z = &y; // error
   static T* t = &x; // pass
   t = &y; // error
}

My current experience leads me to look at the above code and 
think that '@noassign' should not be a separate attribute, but 
rather the default for *all* @safe 'ref' parameters. Assigning 
the address of a 'ref', which in itself does not indicate whether 
it is stored on the stack or on the heap, seems inherently 
dangerous, and I can't think of any easy way to verify that it's 
safe. The existing way to allow it would be to mark functions 
which do this '@trusted' or '@system', but there has also been 
suggested the inverse of '@noassign', '@global', which verifies 
at the call site that a non-stack reference will be passed.

void func(ref T a, @global ref T b) {
   static T* t = &a; // error with '@noassign' default
   t = &b; // pass
}

void fun(ref T a, @global ref T b) {
   T c;
   static T d;
   func(c, d); // pass
   func(d, c); // error, 'c' local

   func(a, b); // pass
   func(b, a); // error, 'a' assumed local
}

'@global' would only be useful if it proved necessary to 
distinguish the kind of assignments illustrated above from 
'@trusted' code, but it would require use case data.

'@rval' accepts rvalue references. Safety, in this case, means 
assuming the reference comes from the stack. Clearly, 
'@noassign', but what else? If the reference is returned, and 
some form of '@noreturn' is implemented, the calling function can 
know perfectly well just how local the return by reference is. So 
for me the dilemma is whether to merge '@rval' with '@noreturn' 
or not.

ref int func(@rval ref int x, @noreturn ref int y, @rval 
@noreturn ref int z) {
   return x; // pass
   return y; // error
   return z; // error
}

ref T fun() {
   T t;
   return func(1, t, 2); //pass, safety enforceable
   return func(1, 2, t); // error, 2 not lvalue
   return func(t, 1, 2); // error, t local, so func() is local
}

One possible definition of 'scope ref' is equivalent to '@rval 
@noreturn @noassign'. The advantage of this definition is that 
there is no need for yet another parameter attribute, 
'@noreturn', because it can be folded into 'scope ref'.

Another definition is simply '@rval @noassign'. The advantage 
here is that 'scope ref' parameters can still allow the 
'identity' function and call chains.

My goal with this analysis has been to express the extent of my 
current thought process. I understand the powerful dilemma 
between the desirability of concise and usable syntax and the 
desirability of a powerful and flexible system which covers all 
of the common use cases for the features involved. The best 
analysis uses actual data from the field.


More information about the Digitalmars-d mailing list