Skip to content

Destructor not invoked because it is instantiated too late, old runtime  #12883

@cooldome

Description

@cooldome

Test case

type
  SPInner[T] = tuple[atomicCounter: int, value: T]
  SharedPtr[T] = object
    val: ptr SPInner[T]

proc `=destroy`*[T](p: var SharedPtr[T]) =
  mixin `=destroy`
  debugecho "=destroy SharedPtr[", $type(T), "]"
  if p.val != nil:
    let c = atomicDec(p.val.atomicCounter)
    if c == 0:
      `=destroy`(p.val.value)
      deallocshared(p.val)
    p.val = nil

proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) {.inline.} =
  if dest.val != nil:
    `=destroy`(dest)
  if src.val != nil:
    discard atomicInc(src.val[].atomicCounter)
  dest.val = src.val

proc `=sink`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) {.inline.} =
  if dest.val != nil:
    `=destroy`(dest)
  dest.val = src.val

proc newSharedPtr*[T](val: sink T): SharedPtr[T] =
  result.val = cast[ptr SPInner[T]]( allocShared0( sizeof(SPInner[T]) ) )
  result.val.atomicCounter = 1
  result.val.value = val

func `$`*[T](p: SharedPtr[T]): string =
  mixin `$`
  if p.val == nil: "nil"
  else: "SharedPtr(" & $p.val.value & ")"


#------------------------------------------------------------
#------------------------------------------------------------
#------------------------------------------------------------
  
type 
  TopObject = object
    internal: SharedPtr[int]

proc deleteTop(p: ptr TopObject) =
  if p != nil:    
    `=destroy`(p[]) # !!! this operation leak the integer
    deallocshared(p)

proc createTop(): ptr TopObject =
  result = cast[ptr TopObject](allocShared0(sizeof(TopObject)))
  result.internal = newSharedPtr(1)

#------------------------------------------------------------
#------------------------------------------------------------
#------------------------------------------------------------
  
proc main() = # little tester code
  let x = createTop()
  echo $x.internal
  deleteTop(x)
  
echo "app begin"
main()
echo "app end"

The test case expected output is

app begin
SharedPtr(1)
=destroy SharedPtr[int]
app end

But actual output is:

app begin
SharedPtr(1)
app end

After a bit of analysis I can say what is going on. createTypeBoundOps for object type TopObject is not invoked early enough hence destructor is not generated while line =destroy(p[]) needs it. Generated destructor needs to be a candidate in overloaded call resolution but it doesn't exist yet.
Clearly invoking createTypeBoundOps in sempass2 is too late, destructor needs to be generated before semOverloadedCallAnalyseEffects for explicit =destroy call in sempass.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions