Skip to content

Destructor not called for result when exception is thrown #22672

Closed
@PMunch

Description

Description

Working further on my MAPM bindings I discovered that throwing exceptions while using result caused ARC to not create a destroy call for result. My smallest repro currently:

import system/ansi_c

type
  MyError = object of CatchableError
  MyObj = object
    internal: pointer

proc `=destroy`(x: MyObj) =
  if x.internal.isNil: return
  x.internal.c_free

proc `=copy`(x: var MyObj, y: MyObj) =
  if x.internal != y.internal:
    x.internal.c_free
    x.internal = y.internal

proc initMyObj(x: string): MyObj =
  result = MyObj(internal: c_malloc(126))
  if x.len < 128:
    result.internal.copyMem(x[0].addr, x.len)
  else:
    raise newException(MyError, "This is an error")

import strutils

try:
  echo initMyObj("Hello world")
  echo initMyObj("Hello world".repeat(128))
except MyError as e:
  echo "Got an error!"
  echo e.msg

Nim Version

v2.0.0 and devel

Current Output

From --expandArc:initMyObj:

result = MyObj(internal: c_malloc(126'u))
if len(x) < 128:
  copyMem(result.internal, addr(x[0]), chckRange(len(x), 0, 9223372036854775807))
else:
  raise
    (ref MyError)(msg: "This is an error", parent: nil)

Confirmed with valgrind --leak-check=full that it does in fact leak:

==1808387== 126 bytes in 1 blocks are definitely lost in loss record 1 of 2
==1808387==    at 0x4841848: malloc (vg_replace_malloc.c:431)
==1808387==    by 0x10E773: initMyObj__test_u10 (test.nim:18)
==1808387==    by 0x10E9DD: NimMainModule (test.nim:28)
==1808387==    by 0x109171: NimMainInner (typedthreads.nim:269)
==1808387==    by 0x109171: NimMain (typedthreads.nim:280)
==1808387==    by 0x109171: main (typedthreads.nim:288)

Expected Output

Based on the output of replacing result with a variable and returning it at the end of the function:

var res
try:
  res = MyObj(internal: c_malloc(126'u))
  if len(x) < 128:
    copyMem(res.internal, addr(x[0]), chckRange(len(x), 0, 9223372036854775807))
  else:
    raise
      (ref MyError)(msg: "This is an error", parent: nil)
  return
    result = res
    `=wasMoved`(res)
finally:
  `=destroy`(res)

Possible Solution

No response

Additional Information

No response

Activity

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

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions