On Friday, 5 April 2013 at 07:45:33 UTC, Dicebot wrote:
On Friday, 5 April 2013 at 00:12:33 UTC, Zach the Mystic wrote:
struct Large { ... }
ref Large process1(@temp ref Large a) { return a; }
ref Large process2(@temp ref Large a) { return a; }
Large* lar = &process2(process1(Large("Pass"," a ", "Large",
"here")));
This is exactly type of code I consider to be bad style and
want to see banned. Function that gets rvalue ref should never
return it as it knows nothing about its lifetime. Actually, I
can't even find scope definition for temporaries in dlang.org,
but it is hardly a good thing to rely on anyway. Best is to
assume that when function is gone, so is rvalue temporary.
It may be bad style. (I don't know if it's bad style.) But there
is significant thought (such as in DIP25) going into the idea
that the programmer doesn't even *need* to track the lifetime of
a reference, that the compiler can do it automatically. I don't
think it's documented, but there is already an error issued for
local variables which are returned by reference, which is the
same behavior as 'scope' described.
ref int func() {
int y;
return y; // Error: may not be escaped
}
There is no current checking, however, for returning the result
of a function which returns ref.
ref int func(ref int a) { return a; }
ref int func2() {
int x;
return func(x); // Passes when it should error, x escaped
}
DIP25, to be on the safe side, proposes that since func() takes a
ref, it must be assumed to return the ref it takes. So func(x)
will be treated as a local since x is a local. The only flaw with
DIP25 is that it's actually *too* safe, and it will shut down
some cases which are perfectly fine:
ref int copy(ref int a) {
int *b = new int;
*b = a;
return *b; // return by ref
}
I have tried to address this issue elsewhere (and I actually need
to make formal proposals which I haven't done yet), but the main
point is that even accepting a temporary rvalue is the kind of
thing which can be tracked at the call site, and not the function
itself.
ref int func(ref int a) { return a; }
ref int func2() {
int x;
return func(x); // Error, according to DIP25
return func(25); // Error also, temp 25 is local, just like x
static int* y;
return func(y); // Okay because y is not local
}
So there are two different issues going on.
Now what's the big deal, you may ask. Any function designed to
accept an rvalue temporary can't possibly want to return that
parameter by reference. And you may be right. In fact, the only
reason I know to allow it was mentioned above, because it would
allow efficient chaining of operations on a single entity passed
by reference instead of by value, with each function modifying it
before passing it on. But it's the *caller* who needs to know how
local the reference is, not the receiving function. And that
leads me into the other issue, which is that I had thought
'scope' would be a great way to tell a calling function "no, this
ref will not be returned to you". (This is the issue I need to
make a formal proposal on.) And that leads to a direct
contradiction with the proposed use of 'scope ref', as I have
written in my answer to Kenji Hara's defense of 'scope ref'.
ref int func(scope int a) {
return *new int; // Okay
return a; //Error: can't return a parameter marked scope
}
Now the checking used by DIP25 could call func() safely:
ref int testFunc() {
int x;
return func(x); // Okay, I know x will not be returned
}
So 'scope' and 'scope ref' would mean two different things, and
there might be some cases where you only want one of the features
and not both.
To address this issue, I thought of a slightly desperate way to
actually resolve this problem without any new storage class, such
as 'ref&' or '@temp ref'. In order to indicate 'scope' in
addition to 'scope ref', you'd simply write 'scope scope ref'.
Two scopes! The defense of this position is that the actual use
of 'scope' by itself would rarely be used, and so the strange
appearance of two scopes would almost never happen.
Your code begs for using plain refs and storing Large in a
stack variable before calling function chain. May look a bit
less convenient but much more reliable and understandable.
Yes, this issue would be simplified by simply saying that any
function which accepts rvalue temporaries must treat those
parameters as locals and not allow returning them. It imposes a
minor inconvenience on the programmer who must declare an lvalue
to use any function which *does* return a reference to the
parameter. I actually think this is a sound design choice, but at
least it will be a choice and not a "lucky" accident.
One last thing about 'scope ref', which would be usable for the
new feature, assuming the design choice just mentioned was
accepted. It's not as obvious that it implicitly allows rvalue
temporaries as something blunt like '@temp ref' would be, or as
inconspicuous as 'ref&' would be. Also, it sort of suggests that
ordinary 'scope' is not in fact passed by ref, which I is
somewhat misleading. So it does save on syntax creep, but it also
has those three disadvantages.
I think that's all I have to say about this topic!