mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-17 18:44:53 +00:00
93 lines
2.1 KiB
Nim
93 lines
2.1 KiB
Nim
discard """
|
|
matrix: "--mm:refc; --mm:arc; --mm:orc"
|
|
targets: "c cpp"
|
|
output: "ok"
|
|
"""
|
|
|
|
# bug #26041: a closure iterator local was released at the `yield` that
|
|
# suspended the iterator instead of at the end of its scope.
|
|
|
|
func firstAddr[T](x: openArray[T]): ptr T =
|
|
x[0].addr
|
|
|
|
proc churn() =
|
|
## Recycle enough memory that a freed payload would be reused.
|
|
var junk: seq[seq[int]]
|
|
for i in 0 ..< 100_000: junk.add @[-7]
|
|
|
|
block: # the local outlives the `yield`, so its payload must, too
|
|
var p: ptr int
|
|
|
|
iterator leak(): int {.closure.} =
|
|
var data = @[123]
|
|
p = firstAddr(data)
|
|
yield 1
|
|
yield 2 # `data`'s scope only ends here
|
|
|
|
let it = leak
|
|
discard it() # run to the first `yield` and suspend
|
|
GC_fullCollect()
|
|
churn()
|
|
doAssert p[] == 123, "suspended iterator's seq payload was recycled"
|
|
|
|
block: # same, but the local's scope is a nested one
|
|
var p: ptr int
|
|
|
|
iterator leak(): int {.closure.} =
|
|
while true:
|
|
var data = @[456]
|
|
p = firstAddr(data)
|
|
yield 1
|
|
break # `data` is never read again, but its scope reaches to here
|
|
|
|
let it = leak
|
|
discard it()
|
|
GC_fullCollect()
|
|
churn()
|
|
doAssert p[] == 456
|
|
|
|
block: # a `try` body is a scope as well
|
|
var p: ptr int
|
|
|
|
iterator leak(): int {.closure.} =
|
|
try:
|
|
var data = @[789]
|
|
p = firstAddr(data)
|
|
yield 1
|
|
finally:
|
|
discard
|
|
|
|
let it = leak
|
|
discard it()
|
|
GC_fullCollect()
|
|
churn()
|
|
doAssert p[] == 789
|
|
|
|
when defined(gcDestructors):
|
|
block: # the lifted local is destroyed exactly once, with the environment
|
|
var destroyed = 0
|
|
|
|
type Res = object
|
|
id: int
|
|
|
|
proc `=destroy`(x: Res) =
|
|
if x.id != 0: inc destroyed
|
|
|
|
proc run() =
|
|
iterator iter(): int {.closure.} =
|
|
var r = Res(id: 1) # never read again; the scope is what keeps it alive
|
|
yield 1
|
|
yield 2
|
|
|
|
var it = iter
|
|
doAssert it() == 1
|
|
doAssert destroyed == 0
|
|
doAssert it() == 2
|
|
doAssert destroyed == 0
|
|
discard it() # run the iterator to completion
|
|
|
|
run() # only now is the environment gone
|
|
doAssert destroyed == 1, $destroyed
|
|
|
|
echo "ok"
|