Updated Destructors (rest)
This commit is contained in:
parent
555af14f09
commit
9525f9dcc9
|
@ -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 consume(c: var Container; element: sink T) =
|
||||
c[i] = element
|
||||
proc put*(t: Table; key, value: sink string) = ...
|
||||
|
||||
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
|
||||
========================
|
||||
|
|
Loading…
Reference in New Issue