Skip to content

Destructors: case objects have issues #13149

@cooldome

Description

@cooldome

Test case:

type TMyObj = object p: pointer len: int proc `=destroy`(o: var TMyObj) = if o.p != nil: dealloc o.p o.p = nil echo "myobj destroyed" proc `=`(dst: var TMyObj, src: TMyObj) = `=destroy`(dst) dst.p = alloc(src.len) dst.len = src.len proc `=sink`(dst: var TMyObj, src: TMyObj) = `=destroy`(dst) dst.p = src.p dst.len = src.len type TObjKind = enum Z, A, B TCaseObj = object case kind: TObjKind  of Z: discard  of A: x1: int # this int plays important role  x2: TMyObj  of B: y: TMyObj proc test: TCaseObj = result = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) result = TCaseObj(kind: B, y: TMyObj(len: 3, p: alloc(3))) let x1 = test()

The test crashes at the runtime with the following stack trace:

myobj destroyed Traceback (most recent call last) C:\Nim\zcase.nim(39) zcase C:\Nim\zcase.nim(36) test C:\Nim\zcase.nim(19) =sink C:\Nim\zcase.nim(8) =destroy C:\Nim\lib\system\alloc.nim(945) dealloc C:\Nim\lib\system\alloc.nim(819) rawDealloc C:\Nim\lib\system\alloc.nim(370) isSmallChunk SIGSEGV: Illegal storage access. (Attempt to read from nil?) 

I have investigated the reason of the crash. Sink and copy operators generated in the following way:

proc `=`(dst: var TCaseObj, src: TCaseObj) = `=destroy`(fieldsUnderCase) # all good dst.kind = src.kind # all good `=`(dst.fieldsUnderCase, src.fieldsUnderCase) # problem here: dst.fieldsUnderCase contains garbage when union is switched and it will try to destroy a garbage 

Solution:
We need to reset memory before invoking copy/sink or use weakAsgn/memMove proposed by Clybber

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions