Updated Destructors, 2nd edition (rest)
This commit is contained in:
parent
eaa3fba25f
commit
3d847d40b7
|
@ -2,6 +2,7 @@
|
|||
Nim Destructors and Move Semantics
|
||||
==================================
|
||||
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
|
@ -118,8 +119,9 @@ The general pattern in ``=destroy`` looks like:
|
|||
.. code-block:: nim
|
||||
|
||||
proc `=destroy`(x: var T) =
|
||||
# first check if 'x' was moved to somewhere else:
|
||||
if x.field != nil:
|
||||
dealloc(x.field)
|
||||
freeResource(x.field)
|
||||
x.field = nil
|
||||
|
||||
|
||||
|
@ -172,7 +174,7 @@ The general pattern in ``=`` looks like:
|
|||
# protect against self-assignments:
|
||||
if dest.field != source.field:
|
||||
`=destroy`(dest)
|
||||
dest.field = copy source.field
|
||||
dest.field = duplicateResource(source.field)
|
||||
|
||||
|
||||
The ``=`` proc can be marked with the ``{.error.}`` pragma. Then any assignment
|
||||
|
@ -202,7 +204,7 @@ as ``let tmp = move(a); b = move(a); a = move(tmp)``!
|
|||
This has further consequences:
|
||||
|
||||
* Objects that contain pointers that point to the same object are not supported
|
||||
by Nim's model. Objects can be swapped and end up in an inconsistent state.
|
||||
by Nim's model. Otherwise swapped objects would end up in an inconsistent state.
|
||||
* Seqs can use ``realloc`` in the implementation.
|
||||
|
||||
|
||||
|
@ -284,13 +286,15 @@ Const temporaries
|
|||
|
||||
Constant literals like ``nil`` cannot be easily be ``=moved``'d. The solution
|
||||
is to pass a temporary location that contains ``nil`` to the sink location.
|
||||
In other words, ``var T`` can only bind to locations, but ``sink T`` can bind
|
||||
to values.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
var x: owned ref T = nil
|
||||
# gets turned into:
|
||||
# gets turned into by the compiler:
|
||||
var tmp = nil
|
||||
move(x, tmp)
|
||||
|
||||
|
@ -430,3 +434,49 @@ Let ``U`` be an unowned ``ref`` type. Conceptually its hooks look like:
|
|||
# Note: Moves are the same as assignments.
|
||||
`=`(x, y)
|
||||
|
||||
|
||||
Hook lifting
|
||||
============
|
||||
|
||||
The hooks of a tuple type ``(A, B, ...)`` are generated by lifting the
|
||||
hooks of the involved types ``A``, ``B``, ... to the tuple type. In
|
||||
other words, a copy ``x = y`` is implemented
|
||||
as ``x[0] = y[0]; x[1] = y[1]; ...``, likewise for ``=move`` and ``=destroy``.
|
||||
|
||||
Other value-based compound types like ``object`` and ``array`` are handled
|
||||
correspondingly. For ``object`` however, the compiler generated hooks
|
||||
can be overridden. This can also be important to use an alternative traversal
|
||||
of the involved datastructure that is more efficient or in order to avoid
|
||||
deep recursions.
|
||||
|
||||
|
||||
|
||||
Hook generation
|
||||
===============
|
||||
|
||||
The ability to override a hook leads to a phase ordering problem:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Foo[T] = object
|
||||
|
||||
proc main =
|
||||
var f: Foo[int]
|
||||
# error: destructor for 'f' called here before
|
||||
# it was seen in this module.
|
||||
|
||||
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
|
||||
hooks for all types in *strategic places* so that an explicitly provided
|
||||
hook that comes too "late" can be detected reliably. These *strategic places*
|
||||
have been derived from the rewrite rules and are as follows:
|
||||
|
||||
- In the construct ``let/var x = ...`` (var/let binding)
|
||||
hooks are generated for ``typeof(x)``.
|
||||
- In ``x = ...`` (assignment) hooks are generated for ``typeof(x)``.
|
||||
- In ``f(...)`` (function call) hooks are generated for ``typeof(f(...))``.
|
||||
|
|
Loading…
Reference in New Issue