[Issue 9238] Support rvalue references

d-bugmail at puremagic.com d-bugmail at puremagic.com
Wed Jan 9 18:32:24 PST 2013


http://d.puremagic.com/issues/show_bug.cgi?id=9238



--- Comment #13 from Andrei Alexandrescu <andrei at erdani.com> 2013-01-09 18:32:23 PST ---
Design #2: ref return is sealed by arguments
============================================

So design #1 has the obvious issue that call chains can't propagate ref returns
upwards even when it's safe to do so. To improve on that, let's devise a
refined rule:

----------
For functions returning ref, the lifetime of the returned object spans at least
the lifetime of its shortest-lived argument.
----------

Impact on desiderata:

Reconsidering the troublesome example:

ref int identityImpl(ref int a) { return a; }
ref int identity(ref int a) { return identityImpl(a); }

When compiling identity(), the compiler (without seeing the body of
identityImpl) figures that the lifetime of the value returned by
identityImpl(a) is at least as long as the lifetime of a itself. Therefore
identity() typechecks because it is allowed to return a proper.

Safety is still guaranteed however. This is because a function can never escape
a reference to an object of shorter lifetime than the lifetime of the
reference. Reconsidering the front() example:

struct Range {
  private AnotherRange _source;
  // ... inside some Range implementation ...
  ref T front() { return _source.front; } // fine
}

front() compiles because front is really a regular function taking a "ref Range
this". Then _source is scoped inside "this" so from a lifetime standpoint
"this", _source, and the result are in good order.

ref int fun() {
   Range r;
   return r.front; // error
}

fun() does not compile because the call r.front returns a value with the
lifetime of r, so returning a ref is tantamount to escaping the address of a
local.

ref int gun(Range r) {
   return r.front; // error
}

This also doesn't compile because the result of r.front has the lifetime of r,
which is passed by value into gun.

ref int gun(ref Range r) {
   return r.front; // fine
}

This does work because the result has the same lifetime as r.

The question remains on how to handle rvalues bound to ref parameters. The
previous design required that rvalues live as long as the scope, and this
design would allow that too. But this design also allows the C++-style
destruction of rvalues: in the call foo(bar()), if foo returns a ref, it must
be used immediately because bar will be destroyed at the end of the full
expression.

If we want to keep the current D rule of destroying rvalue parameters right
after the call to the function, that effectively disallows any use of the ref
result. This may actually be a meaningful choice.

The largest problem of this design is lifetime pollution. Consider the
ScopedContainer example:

ref T opIndex(size_t n) { return payload_[n]; }

In the call c[42], the shortest lifetime is actually that of n, which binds to
the rvalue 42. So the compiler is forced to a shorter guarantee of the result
lifetime than the actual lifetime, because of an unrelated parameter.

Summary
=======

1. Design is safe
2. Design allows binding rvalues to ref parameters. For usability, temporaries
must last at least as long as the current expression (C++ style).
3. Returning ref parameters works with fewer restrictions than the previous
design.
4. Sealed containers are implementable.
5. Difficulty is moderate on the implementation side and moderate on the user
side.

Next iteration of the design will attempt to refine the lifetime of results so
as to avoid pollution.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------


More information about the Digitalmars-d-bugs mailing list