Updated Destructors, 2nd edition (rest)
This commit is contained in:
parent
eaa3fba25f
commit
3d847d40b7
|
@ -2,6 +2,7 @@
|
||||||
Nim Destructors and Move Semantics
|
Nim Destructors and Move Semantics
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,8 +119,9 @@ The general pattern in ``=destroy`` looks like:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
proc `=destroy`(x: var T) =
|
proc `=destroy`(x: var T) =
|
||||||
|
# first check if 'x' was moved to somewhere else:
|
||||||
if x.field != nil:
|
if x.field != nil:
|
||||||
dealloc(x.field)
|
freeResource(x.field)
|
||||||
x.field = nil
|
x.field = nil
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,7 +174,7 @@ The general pattern in ``=`` looks like:
|
||||||
# protect against self-assignments:
|
# protect against self-assignments:
|
||||||
if dest.field != source.field:
|
if dest.field != source.field:
|
||||||
`=destroy`(dest)
|
`=destroy`(dest)
|
||||||
dest.field = copy source.field
|
dest.field = duplicateResource(source.field)
|
||||||
|
|
||||||
|
|
||||||
The ``=`` proc can be marked with the ``{.error.}`` pragma. Then any assignment
|
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:
|
This has further consequences:
|
||||||
|
|
||||||
* Objects that contain pointers that point to the same object are not supported
|
* 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.
|
* 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
|
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.
|
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:
|
For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
var x: owned ref T = nil
|
var x: owned ref T = nil
|
||||||
# gets turned into:
|
# gets turned into by the compiler:
|
||||||
var tmp = nil
|
var tmp = nil
|
||||||
move(x, tmp)
|
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.
|
# Note: Moves are the same as assignments.
|
||||||
`=`(x, y)
|
`=`(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