Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bad order of destruction #24719

Open
arnetheduck opened this issue Feb 24, 2025 · 3 comments
Open

Bad order of destruction #24719

arnetheduck opened this issue Feb 24, 2025 · 3 comments

Comments

@arnetheduck
Copy link
Contributor

Description

I expect destructors to be called in reverse creation order, inherited first then base - this is important for lots of reasons, ie all the way from multiple locks being released in the correct order to prevent deadlocks to pointers to the base class being valid during deconstruction etc.

This order of destruction is already imposed for local variables, ie reverse declaration order - there's no reason objects shouldn't work the same...

type
  Aaaa {.inheritable.} = object
    vvvv: int
  Bbbb = object of Aaaa
    c: Cccc
    d: Dddd
  Cccc = object
  Dddd = object

  Holder = object
    member: ref Aaaa

proc `=destroy`(v: Cccc) =
  echo "destroying c"

proc `=destroy`(v: Dddd) =
  echo "destroying d"

proc `=destroy`(v: Aaaa) =
  echo "destroying a ", v.vvvv

func makeHolder(vvvv: int): ref Holder =
  (ref Holder)(member: (ref Bbbb)(vvvv: vvvv))

block:
  var v = makeHolder(1)
  var v2 = makeHolder(2)

Nim Version

2.2

Current Output

destroying a 2
destroying c
destroying d
destroying a 1
destroying c
destroying d

Expected Output

destroying d
destroying c
destroying a 2
destroying d
destroying c
destroying a 1

Known Workarounds

No response

Additional Information

No response

@forchid
Copy link

forchid commented Feb 26, 2025

You shoud add the destructor of Bbbb when customizing the destructor for Aaaa. Such as:

type
  Aaaa {.inheritable.} = object
    vvvv: int
  Bbbb = object of Aaaa
    c: Cccc
    d: Dddd
  Cccc = object
  Dddd = object

  Holder = object
    member: ref Aaaa

proc `=destroy`(v: Cccc) =
  echo "destroying c"

proc `=destroy`(v: Dddd) =
  echo "destroying d"

proc `=destroy`(v: Aaaa) =
  echo "destroying a ", v.vvvv
  
proc `=destroy`(v: Bbbb) =
  `=destroy`(v.d)
  `=destroy`(v.c)
  `=destroy`(Aaaa(v))

func makeHolder(vvvv: int): ref Holder =
  let b = (ref Bbbb)(vvvv: vvvv)
  (ref Holder)(member: b)

block:
  var v = makeHolder(1)
  var v2 = makeHolder(2)

The test ok:

destroying d
destroying c
destroying a 2
destroying d
destroying c
destroying a 1

@arnetheduck
Copy link
Contributor Author

You shoud add the destructor

While the effect of a well-defined destruction order indeed can be achieved manually, this issue is about bringing the feature in line with other "evaluation order" features in the language so as to reduce surprises and contextual complexity.

@elcritch
Copy link
Contributor

While the effect of a well-defined destruction order indeed can be achieved manually, this issue is about bringing the feature in line with other "evaluation order" features in the language so as to reduce surprises and contextual complexity.

I second this. If it's not well defined and changes between Nim versions it'd cause a lot of painful headaches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants