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