-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Open
Labels
Description
- bug1: with default gc, destructor is not called with seq when seq is accessed through a field
- bug2: with default gc and --gc:arc, destructor is called for a default-initialized object (wasteful at best)
Example
when true: # gitissue D20200608T005906:here
type Foo = object
x: int
p: ptr int
type Vec = object
vals: seq[Foo]
proc `=destroy`*(a: var Foo) {.inline.} =
echo ("in destroy", a.x, a.p == nil)
if a.p != nil:
dealloc(a.p)
proc initFoo(x: int): Foo =
result = Foo(x: x, p: create(int))
proc add2(v: var Vec, a: Foo) = v.vals.add a
proc add3(v: var Vec, a: sink Foo) = v.vals.add a
proc main1()=
echo "main1"
var a = newSeqOfCap[Foo](2)
a.add initFoo(10)
a.add initFoo(11)
proc main2()=
echo "main2"
var a: Vec
a.vals = newSeqOfCap[Foo](2) # ditto with `a.vals.setLen 2; a.vals.setLen 0` or even with nothing
a.vals.add initFoo(12)
a.add2 initFoo(13)
a.add3 initFoo(14)
main1()
main2()
echo "done"
Current Output
nim r t10917.nim
main1
("in destroy", 10, false)
("in destroy", 11, false)
main2
("in destroy", 0, true)
("in destroy", 0, true)
done
=> ("in destroy", 0, true)
should not be called
=> destructor not called for 12,13,14
nim r --gc:arc t10917.nim
main1
("in destroy", 10, false)
("in destroy", 11, false)
main2
("in destroy", 0, true)
("in destroy", 0, true)
("in destroy", 12, false)
("in destroy", 13, false)
("in destroy", 14, false)
done
=> ("in destroy", 0, true)
should not be called (and there are 2 instead of 3 (or 0) of them)
Expected Output
both with and without --gc:arc should produce:
main1
("in destroy", 10, false)
("in destroy", 11, false)
main2
("in destroy", 12, false)
("in destroy", 13, false)
("in destroy", 14, false)
done
Additional Information
- devel 1.3.5 52841db
proposal
- bug1 should be fixed, even if --gc:arc doesn't have that issue (--gc:arc has other issues and isn't the default yet)
- don't call destructor for a variable that was not initialized or default-initialized (would fix bug2)
This requires tracking which variables are default-initialized or un-initialized, which would also be useful for cgen creates redundantnimZeroMem
calls #14602
proc main() =
var a: Foo
a = Foo(x: 10)
main() # this should only call 1 destructor (for Foo(x:10)), not for the default initialized `Foo()`
for seq, the simplest correct way is to assume that all elements from 0..<len are initialized, and shall have their destructor called when seq goes out of scope; potential optimizations to track which range of indexes are default initialized would be possible but not needed, eg:
proc main()=
var a = newSeqOfCap[Foo](1)
a.add Foo(x: 10)
a.setLen 3
main()
# only 3 `=destroy(a: var Foo)` shall be called