-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Description
A modified version of #24579 (comment) results in a double-free with ARC in test1
. The second test using variables appears to be fine.
It's not clear if =destroy(x.fref)
shouldn't be called in =destroy*(x: var Baz)
in Nim 2.x but it does work with 1.6.20. This occurs on both Mac/arm64 and Linux/arm64.
My take it that it looks like the discarded variables have lots of extra copies and destructions occuring. Also of note is that adding proc =copy(x: var Bar) {.error.}
doesn't error. Similarly =wasMoved
doesn't appear to be called either.
Run with:
nim c -d:debug -d:useMalloc --mm:arc -d:traceArc -d:nimArcDebug -r "destroytest.nim"
import std/strutils
# destroytest.nim
type
Foo = object
id: int
Bar = object
id: int
foo: Foo
Baz = object
id: int
fref: ref Foo
var depth = 1
template printDestroying(x) =
depth.inc
echo "\t".repeat(depth), "Destroying: ", x.repr, " addr: 0x", addr(x).pointer.repr
template printDestroyed(x) =
echo "\t".repeat(depth), "Destroyed: ", x.repr, " addr: 0x", addr(x).pointer.repr
depth.dec
proc `=destroy`*(x: var Foo) =
printDestroying(x)
addr(x)[].id = -1
printDestroyed(x)
proc `=destroy`*(x: var Bar) =
printDestroying(x)
`=destroy`(x.foo)
addr(x)[].id = -1
printDestroyed(x)
proc `=destroy`*(x: var Baz) =
printDestroying(x)
if not x.fref.isNil:
`=destroy`(x.fref) # no issue if this is commented
addr(x)[].id = -1
printDestroyed(x)
proc test1() =
echo "\n=== Test1 ==="
discard Bar(id: 2, foo: Foo(id: 1))
discard Baz(id: 4, fref: (ref Foo)(id: 3))
proc test2() =
echo "\n=== Test2 ==="
let x1 = Foo(id: 1)
let x2 = Bar(id: 2, foo: x1)
let x3 = (ref Foo)(id: 3)
let x4 = Baz(id: 4, fref: x3)
echo "addr x1: ", $(x1), " ", x1.unsafeAddr.pointer.repr
echo "addr x2: ", $(x2), " ", x2.unsafeAddr.pointer.repr
echo "addr x2.foo: ", $(x2.foo), " ", x2.foo.unsafeAddr.pointer.repr
echo "addr x3: ref Foo: ", $(x3[]), " ", cast[pointer](x3).repr
echo "addr x4: Baz #", $(x4), " ", x4.unsafeAddr.pointer.repr
echo "addr x4.fref: ", $(x4.fref[]), " ", cast[pointer](x4.fref).repr
echo ""
test1()
test2()
Nim Version
Nim Compiler Version 2.0.14 [Linux: arm64]
Compiled at 2024-12-23
Copyright (c) 2006-2023 by Andreas Rumpf
git hash: bf4de6a
active boot switches: -d:release -d:danger
Current Output
## Note that `[Freed] 0xc42bba8e36b0` occurs twice, without -d:nimArcDebug it segfaults
=== Test1 ===
[Allocated] 0xc42bba8e36b0 result: 0xc42bba8e36c0
Destroying: Baz(id: 4, fref: ref Foo(id: 3)) addr: 0x0000FFFFFF4D98F8
[ABOUT TO DESTROY] 0xc42bba8e36b0
Destroying: Foo(id: 3) addr: 0x0000C42BBA8E36C0
Destroyed: Foo(id: -1) addr: 0x0000C42BBA8E36C0
[Freed] 0xc42bba8e36b0
Destroyed: Baz(id: -1, fref: ref Foo(id: -1)) addr: 0x0000FFFFFF4D98F8
[ABOUT TO DESTROY] 0xc42bba8e36b0
Destroying: Foo(id: -1) addr: 0x0000C42BBA8E36C0
Destroyed: Foo(id: -1) addr: 0x0000C42BBA8E36C0
[Freed] 0xc42bba8e36b0
Destroying: Bar(id: 2, foo: Foo(id: 1)) addr: 0x0000FFFFFF4D98E8
Destroying: Foo(id: 1) addr: 0x0000FFFFFF4D98F0
Destroyed: Foo(id: -1) addr: 0x0000FFFFFF4D98F0
Destroyed: Bar(id: -1, foo: Foo(id: -1)) addr: 0x0000FFFFFF4D98E8
Destroying: Foo(id: 1) addr: 0x0000FFFFFF4D98C8
Destroyed: Foo(id: -1) addr: 0x0000FFFFFF4D98C8
=== Test2 ===
[Allocated] 0xc42bba8e36d0 result: 0xc42bba8e36e0
[INCREF] 0xc42bba8e36d0
addr x1: (id: 1) 0000FFFFFF4D9678
addr x2: (id: 2, foo: (id: 1)) 0000FFFFFF4D96A8
addr x2.foo: (id: 1) 0000FFFFFF4D96B0
addr x3: ref Foo: (id: 3) 0000C42BBA8E36E0
addr x4: Baz #(id: 4, fref: ...) 0000FFFFFF4D96B8
addr x4.fref: (id: 3) 0000C42BBA8E36E0
Destroying: Baz(id: 4, fref: ref Foo(id: 3)) addr: 0x0000FFFFFF4D96B8
[DECREF] 0xc42bba8e36d0
Destroyed: Baz(id: -1, fref: ref Foo(id: 3)) addr: 0x0000FFFFFF4D96B8
[ABOUT TO DESTROY] 0xc42bba8e36d0
Destroying: Foo(id: 3) addr: 0x0000C42BBA8E36E0
Destroyed: Foo(id: -1) addr: 0x0000C42BBA8E36E0
[Freed] 0xc42bba8e36d0
Destroying: Bar(id: 2, foo: Foo(id: 1)) addr: 0x0000FFFFFF4D96A8
Destroying: Foo(id: 1) addr: 0x0000FFFFFF4D96B0
Destroyed: Foo(id: -1) addr: 0x0000FFFFFF4D96B0
Destroyed: Bar(id: -1, foo: Foo(id: -1)) addr: 0x0000FFFFFF4D96A8
Destroying: Foo(id: 1) addr: 0x0000FFFFFF4D9678
Destroyed: Foo(id: -1) addr: 0x0000FFFFFF4D9678
Expected Output
# Run with Nim 1.6.20
=== Test1 ===
[Allocated] 0x144605f10 result: 0x144605f20
[ABOUT TO DESTROY] 0x144605f10
Destroying: Foo(id: 3) addr: 0x0000000144605F20
Destroyed: Foo(id: -1) addr: 0x0000000144605F20
[Freed] 0x144605f10
=== Test2 ===
[Allocated] 0x144606080 result: 0x144606090
[INCREF] 0x144606080
addr x1: (id: 1) 000000016DAD6DE8
addr x2: (id: 2, foo: (id: 1)) 000000016DAD6DD8
addr x2.foo: (id: 1) 000000016DAD6DE0
addr x3: ref Foo: (id: 3) 0000000144606090
addr x4: Baz #(id: 4, fref: ...) 000000016DAD6DB8
addr x4.fref: (id: 3) 0000000144606090
Destroying: Baz(id: 4, fref: ref Foo(id: 3)) addr: 0x000000016DAD6DB8
[DeCREF] 0x144606080
Destroyed: Baz(id: -1, fref: ref Foo(id: 3)) addr: 0x000000016DAD6DB8
[ABOUT TO DESTROY] 0x144606080
Destroying: Foo(id: 3) addr: 0x0000000144606090
Destroyed: Foo(id: -1) addr: 0x0000000144606090
[Freed] 0x144606080
Destroying: Bar(id: 2, foo: Foo(id: 1)) addr: 0x000000016DAD6DD8
Destroying: Foo(id: 1) addr: 0x000000016DAD6DE0
Destroyed: Foo(id: -1) addr: 0x000000016DAD6DE0
Destroyed: Bar(id: -1, foo: Foo(id: -1)) addr: 0x000000016DAD6DD8
Destroying: Foo(id: 1) addr: 0x000000016DAD6DE8
Destroyed: Foo(id: -1) addr: 0x000000016DAD6DE8
Known Workarounds
No response
Additional Information
Spawns from #24579