Markdown code blocks part 3 (#20117)

No logic was added, just 4 more files migrated.
This commit is contained in:
Andrey Makarov 2022-08-04 10:32:23 +03:00 • committed by GitHub
commit a34dd3d77a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 409 additions and 283 deletions

View file

@ -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"
```