auto ref is on the docket

kinke via Digitalmars-d digitalmars-d at puremagic.com
Fri Jun 26 17:13:06 PDT 2015


On Thursday, 25 June 2015 at 10:10:42 UTC, Jonathan M Davis wrote:
> Whether a reference escapes is an orthogonal issue. The return 
> attribute is for dealing with that. The function should be able 
> to return by ref or not and still accept both rvalues and 
> lvalues for its parameters. As long as the temporary variable 
> created for holding an rvalue exists for the duration of the 
> statement that the function call is in, it doesn't matter.

When I was talking about escaping references, I didn't mean 
returned references; I meant storing a pointer to a ref parameter 
somewhere outside (global/instance variable).

This snippet is a rough sketch for an approach based on `scope 
ref`, to prevent escaping rvalues:

<CODE>

struct S
{
     int a;
     ref S chain() return { return this; } // returns a `scope 
ref` for rvalues, `ref` for lvalues
}

// pointers derived from `scope ref` params can safely be passed 
down to scope pointer params only
void manipulateWithoutEscaping(scope ref S s) { manipulate(&s, 
S.sizeof); }

// doesn't escape any part of its `s` reference, hence `scope ref`
// analog to S.chain(), returns a `scope ref` for rvalues, `ref` 
for lvalues
ref S forward(return scope ref S s) { return s; }

// low-level manipulation functions not escaping any parts of 
their pointees
void manipulate(scope void* ptr, size_t size) { ptr[0..size] = 0; 
}
void manipulate(scope void[] data) { manipulate(data.ptr, 
data.length); }

struct State
{
     S* s;        // external ownership; should most likely 
actually be shared ownership
     void[] data; // ditto
     void initByEscaping(ref S s)
     {
         this.s = &s; // obvious
         data = (cast(void*)&s.a)[0 .. int.sizeof]; // less obvious
     }
     void manipulate() { if (s) manipulate(data); }
}

void foo()
{
     // forward rvalue ref and invoke chain() on it twice (each 
call forwarding the rvalue ref)
     // rvalue is destructed at the end of the statement
     forward(S(1)).chain().chain();

     // forward rvalue ref and let it be manipulated before 
destruction
     manipulateWithoutEscaping(forward(S(2)));

     // promote forwarded rvalue ref to lvalue
     S s3 = forward(S(3));
     // let the lvalue escape and manipulate it indirectly
     auto state = new State();
     state.initByEscaping(forward(s3)); // requires lvalue `ref`, 
forward() returns that for lvalue arg
     state.manipulate();
     // GC-managed `state` outlives `s3` => dangling pointers 
`state.s` and `state.data.ptr`!
}

</CODE>

The first observation is that a `scope ref` system would probably 
need to introduce scope pointers/slices too, to allow taking the 
address of `scope ref` params.
There'd probably be quite a few `scope` usages, cluttering code 
and introducing additional complexity.

I do see a point in trying to let `ref` alone and the compiler 
work it out. After all, that scope system would only allow us to 
prevent ourselves from escaping rvalues (if we cared to type 
'scope'). With escape analysis, the compiler could disallow 
attempts to bind rvalues to escapable references, by inferring 
scope-ness for ref and pointer params automatically. In the 
future, escaping params could be disallowed in @safe functions, 
and otherwise require an explicit `ref` keyword when supplying 
the lvalue argument, hinting at the potential for dangling 
pointers.

In the meantime, what about making all `ref` params accept 
rvalues via lowering (at least for @system)? As a first step 
until escape analysis is sufficiently implemented and the 
compiler will help us out with compile errors for escaping 
rvalues..


More information about the Digitalmars-d mailing list