@Araq, regarding your latest revision to spec 2 regarding "Hook Lifting":
[https://github.com/nim-lang/Nim/wiki/Destructors,-2nd-edition#id15](https://github.com/nim-lang/Nim/wiki/Destructors,-2nd-edition#id15),
and "Hook Generation":
[https://github.com/nim-lang/Nim/wiki/Destructors,-2nd-edition#id16](https://github.com/nim-lang/Nim/wiki/Destructors,-2nd-edition#id16),
I see that "Hook Lifting" is necessary and is as I implied an a
"foreshadowing" suggestion somewhere further up this thread, but regarding
"Hook Lifting":
> Other value-based compound types like object and array are handled
> correspondingly. For object however, the compiler generated hooks can be
> overridden...
and regarding "Hook Generation":
> The ability to override a hook leads to a phase ordering problem:...
>
> 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...
Doesn't this start to make us think that there must be a better solution than
covering all the special cases?
My proposed solution (if I understand it correctly ;-) ) is as follows:
1\. To have "hooks" defined by default for pointer and automatically generate
"lifting hooks" for every general type class that could be a container as ptr T
(ref/owned ref already covered in spec 2), tuple, array[T], etc. **ahead of
time** when the type is defined so that they are automatically available when
bindings are declared implicitly or explicitly that use those types. These
default hooks can also be overridden ahead-of-time in which case the default
hooks wouldn't be used. If this can be done, you can avoid the extra compiler
logic required to determine where to automatically generate the hook lifting as
in "strategic places" as the lifted hook already exist as defaults, or if
specialized behaviour is desired then the default overrides will have already
been written when the type was defined.
Having the above defaults predefined for specific types such as pointer, ptr T,
and UncheckedArray{T} would prevent memory leaks as in forgetting to deallocate
as follows:
# default hooks...
template `=destroy`{T](ua: UncheckedArray[T]) =
let uap = cast[pointer](`ua`)
if uap!= nil: uap.dealloc; `ua`.unsafeAddr[] = nil
template `=`[T](dst, src: UncheckedArray[T]) =
let dstp = cast[pointer](`dst`); let srcp = cast[pointer](`src`)
if dstp != srcp: `=destroy`(`dst`); `dst`.unsafeAddr[] =
`src`.unsafeAddr[]
template `=move`[T](dst, src: UncheckedArray[T]) =
let dstp = cast[pointer](`dst`); let srcp = cast[pointer](`src`)
if dstp != srcp: `=`(`dst`, `src`); `src`.unsafeAddr[] = nil
# now the following will be automatically deallocated at the end of scope...
var myua = cast[UncheckedArra[int]](int.sizeof.alloc)
Run
Obviously it isn't a safe system as it doesn't have the checks of a ref/owned
ref to prevent deallocation too soon when there is more than one copy and one
of the copies goes out of scope while others are still in use in which case one
should be using ref/owned ref, but at least it prevents memory leaks by always
deallocating at the end of scope.
Having default hooks for every type means that hook generation should not be
necessary other than exactly when types are defined, and the only extra logic
would be not to auto generate when there are override hooks for the specific
type.
2 To make the code more consistent and complete, it should be possible to
define overrides for any type that may contain elements that could need to be
destroyed, which would include object as currently but also types that are
specializations of ptr T, tuple, array[T], UncheckedArray{T], etc. for the same
reasons as given for object in the "Hook Lifting" section of spec 2:
> 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.