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