From d0919b6df8872f472fe02f7c4e7474ef49dfa9c4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 16 Mar 2026 16:56:18 +0100 Subject: [PATCH] fixes #25596 (#25609) --- compiler/closureiters.nim | 34 +++++++++++++++++++++++++--------- tests/iter/tyieldintry.nim | 24 ++++++++++++++---------- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 52f0bed2bb..96734ca8fa 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -139,7 +139,7 @@ import ast, msgs, idents, - renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos + renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos, trees import std/tables @@ -1390,18 +1390,34 @@ proc optimizeStates(ctx: var Ctx) = for i in 0 .. ctx.states.high: ctx.states[i].label.intVal = i +proc detectCapturedSym(c: var Ctx, s: PSym, stateIdx: int) = + if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym: + let vs = c.varStates.getOrDefault(s.itemId, localNotSeen) + if vs == localNotSeen: # First seing this variable + c.varStates[s.itemId] = stateIdx + elif vs == localRequiresLifting: + discard # Sym already marked + elif vs != stateIdx: + c.captureVar(s) + +proc isClosureIterLocal(c: Ctx, s: PSym): bool = + s.kind in {skResult, skVar, skLet, skForVar, skTemp} and + sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym + proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) = case n.kind of nkSym: let s = n.sym - if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym: - let vs = c.varStates.getOrDefault(s.itemId, localNotSeen) - if vs == localNotSeen: # First seing this variable - c.varStates[s.itemId] = stateIdx - elif vs == localRequiresLifting: - discard # Sym already marked - elif vs != stateIdx: - c.captureVar(s) + detectCapturedSym(c, s, stateIdx) + of nkAddr, nkHiddenAddr: + let s = getRoot(n) + if s != nil and isClosureIterLocal(c, s): + detectCapturedSym(c, s, stateIdx) + # bug #25596; lifetime extension for `addr`-taken locals as + # we claim ARC/ORC do destruction based on scopes, not on last-usages. + c.captureVar(s) + for i in 0 ..< n.safeLen: + detectCapturedVars(c, n[i], stateIdx) of nkReturnStmt: if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: # we have a `result = result` expression produced by the closure diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim index 983cae5408..da0d0b0996 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -559,17 +559,21 @@ block: # void iterator discard var a = it -block: # Locals present in only 1 state should be on the stack +block: + # Locals present in only 1 state should be on the stack proc checkOnStack(a: pointer, shouldBeOnStack: bool) = - # Quick and dirty way to check if a points to stack - var dummy = 0 - let dummyAddr = addr dummy - let distance = abs(cast[int](dummyAddr) - cast[int](a)) - const requiredDistance = 300 - if shouldBeOnStack: - doAssert(distance <= requiredDistance, "a is not on stack, but should") - else: - doAssert(distance > requiredDistance, "a is on stack, but should not") + # bug #25596: the very fact we take the address prevents the local + # from being on the stack + when false: + # Quick and dirty way to check if a points to stack + var dummy = 0 + let dummyAddr = addr dummy + let distance = abs(cast[int](dummyAddr) - cast[int](a)) + const requiredDistance = 300 + if shouldBeOnStack: + doAssert(distance <= requiredDistance, "a is not on stack, but should") + else: + doAssert(distance > requiredDistance, "a is on stack, but should not") iterator it(): int {.closure.} = var a = 1