An important potential change to the language: transitory ref

Steven Schveighoffer schveiguy at yahoo.com
Fri Mar 19 22:56:45 PDT 2010


On Sat, 20 Mar 2010 00:07:55 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> On 03/19/2010 10:20 PM, Steven Schveighoffer wrote:
>> What about returning refs that are ref returns or part of other refs?
>> For example:
>>
>> ref int foo(ref int x)
>> {
>> return x;
>> }
>>
>> ref int bar()
>> {
>> int x;
>> return foo(x);
>> }
>>
>> The reason I bring this up is because it's exactly what a struct is
>> doing. Basically, the problem is not so much that you cannot squirrel it
>> away, but you can return it out of the stack scope it was allocated on.
>> I don't know if there's a way to fix this without restricting struct
>> members from returning ref items.
>
> I remember you brought up a similar point in a related discussion a  
> couple of years ago. It's a good point, and my current understanding of  
> the matter is that functions that take and return ref could and should  
> be handled conservatively.

I don't like the sound of that...  What I fear is that the compiler will  
force people to start using pointers because refs don't cut it.  I'm  
guessing you mean you cannot return ref returns from other functions?   
That breaks abstraction principles, I should be able to delegate a task to  
a sub-function.

>
>> For instance, try to find a rule that prevents the above from compiling,
>> but allows the following to compile.
>>
>> struct S
>> {
>> private int x;
>> ref int getX() { return x;}
>> }
>>
>> struct T
>> {
>> S s;
>> ref int getSX() { return s.x; }
>> }
>
> In the approach discussed with Walter, S is illegal. A struct can't  
> define a method to return a reference to a direct member. This is  
> exactly the advice given in Scott's book for C++. (A class can because  
> classes sit on the heap.)

A struct may sit on the heap too.  I don't know how else to describe it,  
but it feels like you are applying the solution in the wrong place.  I  
understand what you are trying to solve, but your solution may be too  
blunt an instrument.

Here's another case:

struct S
{
   int*x;
   ref int getX() {return *x;}
}

Is x on the heap or not?  How do you know?  Arrays are just a wrapped  
pointer, so they too could be stack allocated.

Consider this:

void foo(ref int x)
{
   x++;
}

struct S
{
    int x;
    int y;
    bool xisy;
    ref int getX() {if(xisy) return y; return x;}
}

foo(S.x);
foo(S.getX());

Another case:

struct S
{
    int x;
    ref S opUnary(string op)() if (op == "++") {++x; return this;}
}

I feel this should all be possible.


------
counter proposal:

What about having a new kind of ref that can only be passed up the stack,  
or down only one level if you are the one who initiated it.

Call it scope ref:

ref int baz(ref y)
{
return y;
}
scope ref int foo(scope ref int x, ref int y)
{
    //return x; // illegal, we did not make x scope ref
    //return baz(x); // illegal, cannot convert scope ref into ref
    return y; // legal, you can convert a ref parameter into scope ref.
}

scope ref int bar()
{
    int y;
    //return foo(y, y); //illegal, you cannot pass scope refs down the  
stack more than one level
}

At least this leaves ref alone to be used without restrictions that the  
compiler can't prove are necessary.  If we find scope ref is the only kind  
of ref we ever use, then maybe we can get rid of scope ref and just make  
ref be the restricted form.  Or you could keep scope ref and reserve ref  
for only provable heap-variables.

Man, it would be nice to have escape analysis...

-Steve



More information about the Digitalmars-d mailing list