summary refs log tree commit diff stats
path: root/doc/destructors.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/destructors.md')
-rw-r--r--doc/destructors.md103
1 files changed, 46 insertions, 57 deletions
diff --git a/doc/destructors.md b/doc/destructors.md
index c98e94536..612c47926 100644
--- a/doc/destructors.md
+++ b/doc/destructors.md
@@ -30,8 +30,7 @@ Motivating example
 With the language mechanisms described here, a custom seq could be
 written as:
 
-.. code-block:: nim
-
+  ```nim
   type
     myseq*[T] = object
       len, cap: int
@@ -91,7 +90,7 @@ written as:
     for i in 0..<result.len: result.data[i] = elems[i]
 
   proc len*[T](x: myseq[T]): int {.inline.} = x.len
-
+  ```
 
 
 Lifetime-tracking hooks
@@ -119,20 +118,18 @@ to return.
 
 The prototype of this hook for a type `T` needs to be:
 
-.. code-block:: nim
-
+  ```nim
   proc `=destroy`(x: var T)
-
+  ```
 
 The general pattern in `=destroy` looks like:
 
-.. code-block:: nim
-
+  ```nim
   proc `=destroy`(x: var T) =
     # first check if 'x' was moved to somewhere else:
     if x.field != nil:
       freeResource(x.field)
-
+  ```
 
 
 `=sink` hook
@@ -149,20 +146,19 @@ provide `=destroy` and `=copy`, the compiler will take care of the rest.
 
 The prototype of this hook for a type `T` needs to be:
 
-.. code-block:: nim
-
+  ```nim
   proc `=sink`(dest: var T; source: T)
-
+  ```
 
 The general pattern in `=sink` looks like:
 
-.. code-block:: nim
+  ```nim
 
   proc `=sink`(dest: var T; source: T) =
     `=destroy`(dest)
     wasMoved(dest)
     dest.field = source.field
-
+  ```
 
 **Note**: `=sink` does not need to check for self-assignments.
 How self-assignments are handled is explained later in this document.
@@ -177,29 +173,27 @@ operations.
 
 The prototype of this hook for a type `T` needs to be:
 
-.. code-block:: nim
-
+  ```nim
   proc `=copy`(dest: var T; source: T)
-
+  ```
 
 The general pattern in `=copy` looks like:
 
-.. code-block:: nim
-
+  ```nim
   proc `=copy`(dest: var T; source: T) =
     # protect against self-assignments:
     if dest.field != source.field:
       `=destroy`(dest)
       wasMoved(dest)
       dest.field = duplicateResource(source.field)
-
+  ```
 
 The `=copy` proc can be marked with the `{.error.}` pragma. Then any assignment
 that otherwise would lead to a copy is prevented at compile-time. This looks like:
 
-.. code-block:: nim
-
+  ```nim
   proc `=copy`(dest: var T; source: T) {.error.}
+  ```
 
 but a custom error message (e.g., `{.error: "custom error".}`) will not be emitted
 by the compiler. Notice that there is no `=` before the `{.error.}` pragma.
@@ -215,9 +209,9 @@ memory or resources, but memory safety is not compromised.
 
 The prototype of this hook for a type `T` needs to be:
 
-.. code-block:: nim
-
+  ```nim
   proc `=trace`(dest: var T; env: pointer)
+  ```
 
 `env` is used by ORC to keep track of its internal state, it should be passed around
 to calls of the built-in `=trace` operation.
@@ -233,8 +227,7 @@ prevent the automatic creation.
 
 The general pattern in using `=destroy` with `=trace` looks like:
 
-.. code-block:: nim
-
+  ```nim
   type
     Test[T] = object
       size: Natural
@@ -255,6 +248,7 @@ The general pattern in using `=destroy` with `=trace` looks like:
       for i in 0 ..< dest.size: `=trace`(dest.arr[i], env)
 
   # following may be other custom "hooks" as required...
+  ```
 
 **Note**: The `=trace` hooks (which are only used by `--mm:orc`) are currently more experimental and less refined
 than the other hooks.
@@ -307,8 +301,7 @@ not a linear type system.
 The employed static analysis is limited and only concerned with local variables;
 however, object and tuple fields are treated as separate entities:
 
-.. code-block:: nim
-
+  ```nim
   proc consume(x: sink Obj) = discard "no implementation"
 
   proc main =
@@ -316,16 +309,16 @@ however, object and tuple fields are treated as separate entities:
     consume tup[0]
     # ok, only tup[0] was consumed, tup[1] is still alive:
     echo tup[1]
-
+  ```
 
 Sometimes it is required to explicitly `move` a value into its final position:
 
-.. code-block:: nim
-
+  ```nim
   proc main =
     var dest, src: array[10, string]
     # ...
     for i in 0..high(dest): dest[i] = move(src[i])
+  ```
 
 An implementation is allowed, but not required to implement even more move
 optimizations (and the current implementation does not).
@@ -344,11 +337,10 @@ use `{.push sinkInference: on.}` ... `{.pop.}`.
 The `.nosinks`:idx: pragma can be used to disable this inference
 for a single routine:
 
-.. code-block:: nim
-
+  ```nim
   proc addX(x: T; child: T) {.nosinks.} =
     x.s.add child
-
+  ```
 
 The details of the inference algorithm are currently undocumented.
 
@@ -456,8 +448,7 @@ The complex case looks like a variant of `x = f(x)`, we consider
 `x = select(rand() < 0.5, x, y)` here:
 
 
-.. code-block:: nim
-
+  ```nim
   proc select(cond: bool; a, b: sink string): string =
     if cond:
       result = a # moves a into result
@@ -469,13 +460,11 @@ The complex case looks like a variant of `x = f(x)`, we consider
     var y = "xyz"
     # possible self-assignment:
     x = select(true, x, y)
-
+  ```
 
 Is transformed into:
 
-
-.. code-block:: nim
-
+  ```nim
   proc select(cond: bool; a, b: sink string): string =
     try:
       if cond:
@@ -506,6 +495,7 @@ Is transformed into:
     finally:
       `=destroy`(y)
       `=destroy`(x)
+  ```
 
 As can be manually verified, this transformation is correct for
 self-assignments.
@@ -527,8 +517,7 @@ that the pointer does not outlive its origin. No destructor call is injected
 for expressions of type `lent T` or of type `var T`.
 
 
-.. code-block:: nim
-
+  ```nim
   type
     Tree = object
       kids: seq[Tree]
@@ -553,6 +542,7 @@ for expressions of type `lent T` or of type `var T`.
     # everything turned into moves:
     let t = construct(@[construct(@[]), construct(@[])])
     echo t[0] # accessor does not copy the element!
+  ```
 
 
 The cursor pragma
@@ -564,12 +554,12 @@ This means that cyclic structures cannot be freed
 immediately (`--mm:orc`:option: ships with a cycle collector).
 With the `cursor` pragma one can break up cycles declaratively:
 
-.. code-block:: nim
-
+  ```nim
   type
     Node = ref object
       left: Node # owning ref
       right {.cursor.}: Node # non-owning ref
+  ```
 
 But please notice that this is not C++'s weak_ptr, it means the right field is not
 involved in the reference counting, it is a raw pointer without runtime checks.
@@ -578,13 +568,12 @@ Automatic reference counting also has the disadvantage that it introduces overhe
 when iterating over linked structures. The `cursor` pragma can also be used
 to avoid this overhead:
 
-.. code-block:: nim
-
+  ```nim
   var it {.cursor.} = listRoot
   while it != nil:
     use(it)
     it = it.next
-
+  ```
 
 In fact, `cursor` more generally prevents object construction/destruction pairs
 and so can also be useful in other contexts. The alternative solution would be to
@@ -609,13 +598,13 @@ words, we do a compile-time copy-on-write analysis.
 This means that "borrowed" views can be written naturally and without explicit pointer
 indirections:
 
-.. code-block:: nim
-
+  ```nim
   proc main(tab: Table[string, string]) =
     let v = tab["key"] # inferred as cursor because 'tab' is not mutated.
     # no copy into 'v', no destruction of 'v'.
     use(v)
     useItAgain(v)
+  ```
 
 
 Hook lifting
@@ -639,8 +628,7 @@ Hook generation
 
 The ability to override a hook leads to a phase ordering problem:
 
-.. code-block:: nim
-
+  ```nim
   type
     Foo[T] = object
 
@@ -651,7 +639,7 @@ The ability to override a hook leads to a phase ordering problem:
 
   proc `=destroy`[T](f: var Foo[T]) =
     discard
-
+  ```
 
 The solution is to define ``proc `=destroy`[T](f: var Foo[T])`` before
 it is used. The compiler generates implicit
@@ -674,8 +662,7 @@ The experimental `nodestroy`:idx: pragma inhibits hook injections. This can be
 used to specialize the object traversal in order to avoid deep recursions:
 
 
-.. code-block:: nim
-
+  ```nim
   type Node = ref object
     x, y: int32
     left, right: Node
@@ -695,6 +682,7 @@ used to specialize the object traversal in order to avoid deep recursions:
     # notice how even the destructor for 's' is not called implicitly
     # anymore thanks to .nodestroy, so we have to call it on our own:
     `=destroy`(s)
+  ```
 
 
 As can be seen from the example, this solution is hardly sufficient and
@@ -712,19 +700,20 @@ The copy operation is deferred until the first write.
 
 For example:
 
-.. code-block:: nim
+  ```nim
   var x = "abc"  # no copy
   var y = x      # no copy
   y[0] = 'h'     # copy
-
+  ```
 
 The abstraction fails for `addr x` because whether the address is going to be used for mutations is unknown.
 `prepareMutation` needs to be called before the "address of" operation. For example:
 
-.. code-block:: nim
+  ```nim
   var x = "abc"
   var y = x
 
   prepareMutation(y)
   moveMem(addr y[0], addr x[0], 3)
   assert y == "abc"
+  ```