Extraneous destructor call during environment init
Description
type NoCopy = object
v:int
proc `=copy`(x: var NoCopy, y: NoCopy) {.error.}
proc `=destroy`(x: NoCopy) =
echo "destroy ", x.v
proc caller(f: proc()) =
f()
proc test2(vv: NoCopy) =
echo "in here ", vv.v
proc test() =
debugEcho "1"
var v = NoCopy(v: 3)
debugEcho "2"
caller(proc() = echo v.v)
debugEcho "4"
test2(v)
test()
Nim Version
2.2.0
Current Output
1
destroy 0
2
3
4
in here 3
destroy 3
Expected Output
1
2
3
4
in here 3
destroy 3
Known Workarounds
No response
Additional Information
No response
var :env
try:
:env = [type node]()
debugEcho ["1"]
`=sink`(:env.v0, NoCopy(v: 3))
debugEcho ["2"]
caller((:anonymous, :env))
debugEcho ["4"]
test2(:env.v0)
finally:
`=destroy`(:env)
In =sink(:env.v0, NoCopy(v: 3)), it needs to destroy the old data of :env.v0
it needs to destroy the old data
semantically, this old data does not exist yet - ie we're initializing the environment for the first time.
The fact that an environment is being created doesn't really matter: either we're creating v on the stack or in the environment, but in both cases, there is no "prior instance".
proc test() =
debugEcho "1"
var v = NoCopy(v: 3)
debugEcho "2"
test2(v)
Compare to this snippet - in this case, the stack memory of v is the "environment" in which v lives, and yet we do not first destroy the fictional "left-hand-side" instance before placing NoCopy(v: 3) onto it.
When emulating this behavior and pulling out v into a heap location so that it can be passed to a closure, we are not introducing any additional NoCopy instances - we're merely changing the memory location of the one instance that's been declared - this is a failure of the environment lifting to recognise that there was nothing to destroy to begin with.