Files
Nim/tests/iter/titer_issues.nim
ringabout f959a02037 fixes #25725; environment misses: s with iterator (#25828)
fixes #25725

This pull request makes significant improvements to symbol handling
during transformation passes in the compiler, particularly for routines
(procedures, iterators) and their parameters. The changes ensure that
when routines are copied (for inlining, closure generation, etc.), all
relevant symbols and type headers are also freshly copied and correctly
owned, preventing subtle bugs from symbol reuse. Additionally, new
regression tests are added to cover previously problematic iterator
cases.

**Improvements to symbol copying and ownership:**

* Introduced `freshOwnedSym` to create a fresh copy of a symbol with a
specified owner, ensuring that transformed routines and their parameters
do not share symbols with the originals, which prevents accidental
aliasing and ownership issues.
* Refactored `freshVar` to use `freshOwnedSym`, centralizing fresh
symbol creation logic.
* Added `introduceNewRoutineHeaderSyms` and `copyRoutineTypeHeader` to
ensure that when routines are copied, all parameter/result symbols and
their types are also freshly copied and mapped, avoiding shared state
between original and transformed routines.
* Updated `introduceNewLocalVars` to use `freshOwnedSym` for routine
symbols and to invoke the new header/type copying procedures, ensuring
correctness in routine transformation.

**Testing and regression coverage:**

* Added new blocks to `tests/iter/titer_issues.nim` to test iterator
transformation edge cases, including scenarios that previously led to
symbol reuse bugs (e.g., bugs #25724 and #25725).
2026-06-08 08:53:10 +02:00

489 lines
8.3 KiB
Nim

discard """
target: "c js"
output: '''
0
1
2
3
4
1
start
false
0
1
2
end
@[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 18, 20, 21, 24, 27, 30, 36, 40, 42]
1002
0
1
2
7
9002
9004
9006
9008
9010
9012
9014
9016
9018
@[1, 2]
@[1, 2, 3]
1
nested finally
outer finally
nested finally
outer finally
nested finally
outer finally
nested finally
outer finally
In defer
trying
exception caught
finally block
'''
"""
import sequtils, strutils
block t338:
proc moo(): iterator (): int =
iterator fooGen: int {.closure.} =
while true:
yield result
result.inc
return fooGen
var foo = moo()
for i in 0 .. 4:
echo foo()
block t8041:
iterator xy[T](a: T, b: set[T]): T =
if a in b:
yield a
for a in xy(1'i8, {}):
for b in xy(a, {}):
echo a
block t3837_chained:
iterator t1(): int {.closure.} =
yield 1
iterator t2(): int {.closure.} =
for i in t1():
yield i
for i in t2():
echo $i
proc iter1(): (iterator: int) =
let coll = [0,1,2]
result = iterator: int {.closure.} =
for i in coll:
yield i
proc iter2(it: (iterator: int)): (iterator: int) =
result = iterator: int {.closure.} =
echo finished(it)
for i in it():
yield i
echo "start"
let myiter1 = iter1()
let myiter2 = iter2(myiter1)
for i in myiter2():
echo i
echo "end"
type Iterable[T] = (iterator: T) | Slice[T]
## Everything that can be iterated over, iterators and slices so far.
proc toIter[T](s: Slice[T]): iterator: T =
## Iterate over a slice.
iterator it: T {.closure.} =
for x in s.a..s.b:
yield x
return it
proc toIter[T](i: iterator: T): iterator: T =
## Nop
i
iterator map[T,S](i: Iterable[T], f: proc(x: T): S): S =
let i = toIter(i)
for x in i():
yield f(x)
proc filter[T](i: Iterable[T], f: proc(x: T): bool): iterator: T =
let i = toIter(i)
iterator it: T {.closure.} =
for x in i():
if f(x):
yield x
result = it
iterator filter[T](i: Iterable[T], f: proc(x: T): bool): T =
let i = toIter(i)
for x in i():
if f(x):
yield x
var it = toSeq(filter(2..10, proc(x: int): bool = x mod 2 == 0))
doAssert it == @[2, 4, 6, 8, 10]
it = toSeq(map(filter(2..10, proc(x: int): bool = x mod 2 == 0), proc(x: int): int = x * 2))
doAssert it == @[4, 8, 12, 16, 20]
block t3221_complex:
iterator permutations[T](ys: openArray[T]): seq[T] =
var
d = 1
c = newSeq[int](ys.len)
xs = newSeq[T](ys.len)
for i, y in ys: xs[i] = y
yield xs
block outer:
while true:
while d > 1:
dec d
c[d] = 0
while c[d] >= d:
inc d
if d >= ys.len: break outer
let i = if (d and 1) == 1: c[d] else: 0
swap xs[i], xs[d]
yield xs
inc c[d]
proc dig_vectors(): void =
var v_nums: seq[int]
v_nums = newSeq[int](1)
for perm in permutations(toSeq(0 .. 1)):
v_nums[0] = 1
dig_vectors()
block t3499_keepstate:
proc slice[T](iter: iterator(): T {.closure.}, sl: auto): seq[T] =
var res: seq[int64] = @[]
var i = 0
for n in iter():
if i > sl.b:
break
if i >= sl.a:
res.add(n)
inc i
res
iterator harshad(): int64 {.closure.} =
for n in 1 ..< int64.high:
var sum = 0
for ch in string($n):
sum += parseInt("" & ch)
if n mod sum == 0:
yield n
echo harshad.slice 0 ..< 20
for n in harshad():
if n > 1000:
echo n
break
# bug #3499 last snippet fixed
# bug #705 last snippet fixed
block t1725_nested:
iterator factory(): int {.closure.} =
iterator bar(): int {.closure.} =
yield 0
yield 1
yield 2
for x in bar(): yield x
for x in factory():
echo x
block t2023_objiter:
type
Obj = object
iter: iterator (): int8 {.closure.}
iterator test(): int8 {.closure.} =
yield 7
proc init():Obj=
result.iter = test
var o = init()
echo(o.iter())
block:
# bug #13739
iterator myIter(arg: openArray[int]): int =
var tmp = 0
let len = arg.len
while tmp < len:
yield arg[tmp] * 2
inc tmp
proc someProc() =
var data = [4501,4502,4503,4504,4505,4506,4507,4508,4509]
# StmtListExpr should not get special treatment.
for x in myIter((discard;data)):
echo x
someProc()
block:
# bug #12576
iterator ff(sq: varargs[seq[int]]): int =
for x in sq:
echo x
for x in ff(@[1, 2], @[1, 2, 3]):
echo x
# bug #19575
iterator bb() {.closure.} =
while true:
try: discard
except: break
finally: break
var a = bb
iterator cc() {.closure.} =
while true:
try: discard
except:
if true:
break
finally:
if true:
break
var a2 = cc
# bug #16876
block:
iterator a(num: int): int {.closure.} =
if num == 1:
yield num
else:
for i in a(num - 1):
yield i
for i in a(5):
echo i
block:
# bug #19911 (return in nested try)
# try yield -> try
iterator p1: int {.closure.} =
try:
yield 0
try:
return
finally:
echo "nested finally"
echo "shouldn't run"
finally:
echo "outer finally"
echo "shouldn't run"
for _ in p1():
discard
# try -> try yield
iterator p2: int {.closure.} =
try:
try:
yield 0
return
finally:
echo "nested finally"
echo "shouldn't run"
finally:
echo "outer finally"
echo "shouldn't run"
for _ in p2():
discard
# try yield -> try yield
iterator p3: int {.closure.} =
try:
yield 0
try:
yield 0
return
finally:
echo "nested finally"
echo "shouldn't run"
finally:
echo "outer finally"
echo "shouldn't run"
for _ in p3():
discard
# try -> try
iterator p4: int {.closure.} =
try:
try:
return
finally:
echo "nested finally"
echo "shouldn't run"
finally:
echo "outer finally"
echo "shouldn't run"
for _ in p4():
discard
# bug #18824
iterator poc_iterator: int {.closure.} =
block bug18824:
try:
break bug18824
finally:
echo "In defer"
for _ in poc_iterator():
discard
# bug #20624
iterator tryFinally() {.closure.} =
block route:
try:
echo "trying"
raise
except ReraiseDefect:
echo "exception caught"
break route
except:
echo "exception caught"
break route
finally:
echo "finally block"
var x = tryFinally
x()
block: # bug #24033
type Query = ref object
iterator pairs(query: Query): (int, (string, float32)) =
var output: (int, (string, float32)) = (0, ("foo", 3.14))
for id in @[0, 1, 2]:
output[0] = id
yield output
var collections: seq[(int, string, string)]
for id, (str, num) in Query():
collections.add (id, str, $num)
doAssert collections[1] == (1, "foo", "3.14")
block: # bug #25121
iterator k(): int =
when nimvm:
yield 0
else:
yield 0
for _ in k():
(proc() = (; let _ = block: 0))()
let aaa = new array[1000, byte]
block:
for _ in cast[typeof(aaa)](aaa)[]:
discard
block:
let x = cast[typeof(aaa)](aaa) # not even var
for _ in x[]:
discard
import std/[tables, unicode, sequtils]
const
myTable = {
"en": "abcdefghijklmnopqrstuvwxyz",
}.toTable
proc buggyVersion(locale: string): seq[Rune] =
result = toSeq(runes(myTable[locale]))
proc workingVersion(locale: string): seq[Rune] =
# string lifetime is extended
let str = myTable[locale]
result = toSeq(runes(str))
# echo "Testing working version..."
let runes2 = workingVersion("en")
# echo "Got ", runes2.len, " runes"
# echo "Testing buggy version..."
let runes1 = buggyVersion("en") # <-- CRASHES HERE
doAssert runes1.len == runes2.len
# echo "Got ", runes1.len, " runes"
block: # bug #25724
iterator c(): int =
when nimvm: yield 0
else: yield 1
for w in c():
let n = w
(proc() = discard n)()
block:
iterator c(): int =
yield 1
yield 1
for w in c():
proc p(s: int) =
let sap = s
p(0)
block: # bug #25725
iterator c(): int =
when nimvm: yield 0
else: yield 1
for w in c():
let n = w
proc p(s: int) =
let s = s; discard n
p(0)