[Issue 9238] Support rvalue references

d-bugmail at puremagic.com d-bugmail at puremagic.com
Wed Jan 9 16:07:06 PST 2013


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



--- Comment #12 from Andrei Alexandrescu <andrei at erdani.com> 2013-01-09 16:07:04 PST ---
Design #1: statically sealed ref
================================

One possible design is to give desideratum "4. Sealed containers" priority and
start from there.

Continuing the ScopedContainer example, we notice that to make it work we need
the lifetime of c[n] is bounded by the lifetime of c. We set out to enforce
that statically. The simplest and most conservative rule would be:

----------
For functions returning ref, the lifetime of the returned object spans at least
through the scope of the caller.
----------

Impact on desiderata:

To enforce safety we'd need to disallow any ref-returning function from
returning a value with too short a scope. Examples:

ref int fun(int a) { return a; }
// Error: escapes address of by-value parameter

ref int gun() { int a; return a; }
// Error: escapes address of local

ref int hun() { return *(new int); }
// fine

ref int iun(int* p) { return *p; }
// fine

ref int identity(ref int a) { return a; }
// Should work

This last function typechecks if and only if the argument is guaranteed to have
a lifetime that expands through the end of the scope of the caller. In turn, if
we want to observe (2) and allow rvalues to bind to ref, that means any rvalue
created in the caller must exist through the end of the scope in which the
rvalue was created. This is a larger extent than what D currently allows
(destroy rvalues immediately after the call) and also larger than what C++
allows (destroy rvalues at the end of the full expression). It is unclear
whether this has bad consequences; probably not.

One interesting consequence is that ref returns are intransitive, i.e. cannot
be passed "up". Consider:

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

Under the rule above this code won't compile although it is safe. This is
because from the viewpoint of identity(), identityImpl returns an int that can
only last through the scope of identity(). Attempting to return that is
tantamount to returning a local as far as identity() is concerned, so it won't
typecheck.

This limitation is rather severe. One obvious issue is that creating wrappers
around objects will be seriously limited. For example, a range can't forward
the front of a member:

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

Summary
=======

1. Design is safe
2. Rvalues can be bound to ref (subject to unrelated limitations) ONLY if the
lifetime of rvalues is prolonged through the end of the scope they're created
in. (Assessment: fine)
3. Implementing identity(): possible but intransitive, i.e. references can't be
passed up call chains. (Asessment: limitation is problematic.)
4. Sealed containers: possible and safe, but present wrapping problems due to
(3).
5. Simplicity: good

I'll next present a refinement of this design that improves on its
disadvantages without losing the advantages.

-- 
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