diff --git a/Destructors.rest b/Destructors.rest index d27e08a..0c724f6 100644 --- a/Destructors.rest +++ b/Destructors.rest @@ -6,7 +6,7 @@ Rewrite rules (simplified) ========================== ======== ==================== =========================================================== -Rule Pattern Meaning +Rule Pattern Transformed into ======== ==================== =========================================================== 1 var x; stmts var x; try stmts finally: `=destroy`(x) 2 x = f() `=sink`(x, f()) @@ -127,3 +127,39 @@ it doesn't outlive its origin. # everything turned into moves: let t = construct(@[construct(@[]), construct(@[])]) echo t[0] # accessor does not copy the element! + + +``sink T`` and ``lent T`` introduce further rewrite rules but lead to more efficient code. Even better, these rules optimize away create/copy <-> destroy pairs and so can also make atomic reference counting more efficient by eliminating incref <-> decref pairs. + + +Rewrite rules (extended) +======================== + +======== ==================== =========================================================== +Rule Pattern Transformed into +======== ==================== =========================================================== +1.1 var x: T; stmts var x: T; try stmts finally: `=destroy`(x) +1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x) +2 x = f() `=sink`(x, f()) +3 x = lastReadOf z `=sink`(x, z) +4.1 sinkParam = y `=sink`(sinkParam, y) +4.2 x = y `=`(x, y) # a copy +5.1 f_sink(g()) f_sink(g()) +5.2 f_sink(y) f_sink(y); reset(y) + # 'reset(y)' for locals usually optimized away +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 +