Updated Destructors (rest)

This commit is contained in:
Andreas Rumpf 2018-01-07 12:02:31 +01:00
parent 555af14f09
commit 9525f9dcc9
1 changed files with 9 additions and 31 deletions

View File

@ -50,29 +50,21 @@ A consume can also be forwarded, "pass sp to a different proc as a sink paramete
Use after consume
-----------------
Locations passed to a ``sink`` parameter are invalidated after the call
and the compiler tries to prove that it is not used again afterwards. For
local variables this is quite easy to prove:
After having read https://codesynthesis.com/~boris/blog//2012/06/19/efficient-argument-passing-cxx11-part1/ I have changed my mind about how ``sink`` parameters need to work. ``sink`` parameters are purely an optimization
to eliminate copies. Instead of doing the copy at ``location = sinkParam`` it's turned into a sink and then
*at* the callsite you can specify a *move* if it's not already an expression of the form ``lastReadOf(z)``.
This is much simpler than the original idea of introducing a "use after consume" error state that empties the container and would to lead to error prone code constructs just to save some object copies. It also implies we don't need yet another overloading disambiguation rule, a table's put proc can look like
.. code-block:: nim
proc put*(t: Table; key, value: sink string) = ...
proc consume(c: var Container; element: sink T) =
c[i] = element
proc main() =
var x = initT()
for i in 0..3:
container.consume(x) # Error: attempt to re-use already moved value 'x'
For arbitrary locations involving array accesses etc it is too hard to prove
it is not used afterwards. The compiler transforms ``takeAsSink(sp)`` into
``takeAsSink(sp); reset(sp)``. ``reset`` sets the value back into its default
value. For locals the ``reset`` can be optimized away (stores to a dead object),
for function calls there is no location to reset at all.
With no need of further non-sink overloads.
For a location that has had its value moved into a sink parameter no
destructor call needs to be injected. This is an important optimization
to keep the produced code small.
to keep the produced code size small. There is a ``system.move`` proc that can be used to annotate the moves at callsite that can further eliminate copies.
Sink for locals
@ -153,20 +145,6 @@ Rule Pattern Transformed into
5.3 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp)
======== ==================== ===========================================================
``sink T`` also affects overloading resolution rules; by the time type checking is performed we have no control flow graph yet so the property ``lastReadOf z`` is not available. However, passing a call expression ``f()`` to a ``g`` taking a sink parameter is a syntactic property and so is available for overloading resolution. Thus I propose the following rule:
.. code-block:: nim
proc add(c: var Container; x: T) # version A
proc add(c: var Container; x: sink T) # version B
var c: Container
var x: T
c.add x # calls version A
c.add f() # calls version B
# object construction counts as proc call:
c.add T() # calls version B
Interactions with the GC
========================