fixes #25697; {.borrow.} on iterator for distinct seq triggers internal error (#25709)

fixes #25697

This pull request improves the handling of borrowed routines in the
compiler transformation phase, making the code more robust and
maintainable. The main change is the introduction of a helper function
to properly resolve borrowed routine symbols, which is then used in
multiple places to ensure correct symbol resolution. Additionally, a new
test case is added to cover a previously reported bug related to
borrowed iterators on distinct types.

**Compiler improvements:**

* Added `resolveBorrowedRoutineSym` helper function to follow borrow
aliases and retrieve the underlying implementation symbol for borrowed
routines. This centralizes and clarifies the logic for resolving
borrowed symbols.
* Updated `transformSymAux` and `transformFor` to use the new helper
function, replacing duplicated logic and improving correctness when
handling borrowed routines.
[[1]](diffhunk://#diff-c7b80f51fb685eb22c5b56ee2f320d6c708706f3ae7293478ecd104a2b5b8096L139-R154)
[[2]](diffhunk://#diff-c7b80f51fb685eb22c5b56ee2f320d6c708706f3ae7293478ecd104a2b5b8096L788-R795)

**Testing:**

* Added a test case for bug #25697 to `tests/distinct/tborrow.nim`,
ensuring that iteration over a distinct type with a borrowed iterator
works as expected.
This commit is contained in:
ringabout
2026-04-09 17:08:03 +08:00
committed by GitHub
parent c8e6b059a4
commit 9a2b0dd045
2 changed files with 33 additions and 12 deletions

View File

@@ -118,6 +118,24 @@ proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite
le.flags.incl nfFirstWrite
result[1] = ri
proc resolveBorrowedRoutineSym(c: PTransf; s: PSym; info: TLineInfo): PSym =
# Follow borrow aliases to the underlying implementation symbol.
var s = s
while true:
# Skips over all borrowed procs getting the last proc symbol without an implementation
let body = getBody(c.graph, s)
if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
s = body.sym
else:
break
let body = getBody(c.graph, s)
if body.kind == nkSym:
result = body.sym
else:
result = nil
internalError(c.graph.config, info, "wrong AST for borrowed symbol")
proc transformSymAux(c: PTransf, n: PNode): PNode =
let s = n.sym
if s.typ != nil and s.typ.callConv == ccClosure:
@@ -136,17 +154,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
var tc = c.transCon
if sfBorrow in s.flags and s.kind in routineKinds:
# simply exchange the symbol:
var s = s
while true:
# Skips over all borrowed procs getting the last proc symbol without an implementation
let body = getBody(c.graph, s)
if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
s = body.sym
else:
break
b = getBody(c.graph, s)
if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym, n.info)
b = newSymNode(resolveBorrowedRoutineSym(c, s, n.info), n.info)
elif c.inlining > 0:
# see bug #13596: we use ref-based equality in the DFA for destruction
# injections so we need to ensure unique nodes after iterator inlining
@@ -785,7 +793,9 @@ proc transformFor(c: PTransf, n: PNode): PNode =
discard c.breakSyms.pop
let iter = call[0].sym
var iter = call[0].sym
if sfBorrow in iter.flags and iter.kind in routineKinds:
iter = resolveBorrowedRoutineSym(c, iter, n.info)
var v = newNodeI(nkVarSection, n.info)
for i in 0..<n.len - 2:

View File

@@ -130,3 +130,14 @@ block: # issue #22646
var x: Vec[3, float]
let y = Color(x)
doAssert Vec3[float](y) == x
block: # bug #25697
type MyList = distinct seq[int]
iterator items(x: MyList): lent int {.borrow.}
let s = MyList(@[1, 2, 3])
var count = 0
for item in s:
count += 1
doAssert count == 3, "Expected 3 items, got " & $count