Files
Nim/tests/iter/tyieldintry.nim
2025-10-28 12:48:22 +01:00

801 lines
15 KiB
Nim

discard """
matrix: "; --experimental:strictdefs"
targets: "c cpp"
"""
var closureIterResult = newSeq[int]()
proc checkpoint(arg: int) =
closureIterResult.add(arg)
type
TestError = object of CatchableError
AnotherError = object of CatchableError
proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) =
closureIterResult.setLen(0)
var exceptionCaught = false
try:
for i in it():
closureIterResult.add(i)
except TestError:
exceptionCaught = true
if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected:
if closureIterResult != @expectedResults:
echo "Expected: ", @expectedResults
echo "Actual: ", closureIterResult
if exceptionCaught != exceptionExpected:
echo "Expected exception: ", exceptionExpected
echo "Got exception: ", exceptionCaught
doAssert(false)
proc test(it: iterator(): int, expectedResults: varargs[int]) =
testClosureIterAux(it, false, expectedResults)
proc testExc(it: iterator(): int, expectedResults: varargs[int]) =
testClosureIterAux(it, true, expectedResults)
proc raiseTestError() =
raise newException(TestError, "Test exception!")
block:
iterator it(): int {.closure.} =
var i = 5
while i != 0:
yield i
if i == 3:
yield 123
dec i
test(it, 5, 4, 3, 123, 2, 1)
block:
iterator it(): int {.closure.} =
yield 0
try:
checkpoint(1)
raiseTestError()
except TestError:
checkpoint(2)
yield 3
checkpoint(4)
finally:
checkpoint(5)
checkpoint(6)
test(it, 0, 1, 2, 3, 4, 5, 6)
block:
iterator it(): int {.closure.} =
yield 0
try:
yield 1
checkpoint(2)
finally:
checkpoint(3)
yield 4
checkpoint(5)
yield 6
test(it, 0, 1, 2, 3, 4, 5, 6)
block:
iterator it(): int {.closure.} =
yield 0
try:
yield 1
raiseTestError()
yield 2
finally:
checkpoint(3)
yield 4
checkpoint(5)
yield 6
testExc(it, 0, 1, 3, 4, 5, 6)
block:
iterator it(): int {.closure.} =
try:
try:
raiseTestError()
except AnotherError:
yield 123
finally:
checkpoint(3)
finally:
checkpoint(4)
testExc(it, 3, 4)
block:
iterator it(): int {.closure.} =
try:
yield 1
raiseTestError()
except AnotherError:
checkpoint(123)
finally:
checkpoint(2)
checkpoint(3)
testExc(it, 1, 2)
block:
iterator it(): int {.closure.} =
try:
yield 0
try:
yield 1
try:
yield 2
raiseTestError()
except AnotherError:
yield 123
finally:
yield 3
except AnotherError:
yield 124
finally:
yield 4
checkpoint(1234)
except:
yield 5
checkpoint(6)
finally:
checkpoint(7)
yield 8
checkpoint(9)
test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
block:
iterator it(): int {.closure.} =
try:
yield 0
return 2
finally:
checkpoint(1)
checkpoint(123)
test(it, 0, 1)
block:
iterator it(): int {.closure.} =
try:
try:
yield 0
raiseTestError()
finally:
checkpoint(1)
except TestError:
yield 2
return
finally:
yield 3
checkpoint(123)
test(it, 0, 1, 2, 3)
block: # Wrong except
iterator it(): int {.closure.} =
try:
try:
yield 0
raiseTestError()
except ValueError:
doAssert(false, "Unreachable")
finally:
checkpoint(1)
except ValueError:
yield 123
return
checkpoint(123)
testExc(it, 0, 1)
block: # Nested except without finally
iterator it(): int {.closure.} =
try:
try:
yield 0
raiseTestError()
except ValueError:
doAssert(false, "Unreachable")
except ValueError:
yield 123
checkpoint(123)
testExc(it, 0)
block: # Return in except with no finallies around
iterator it(): int {.closure.} =
try:
try:
yield 0
raiseTestError()
except ValueError:
doAssert(false, "Unreachable")
finally:
checkpoint(1)
except TestError:
yield 2
return
checkpoint(123)
test(it, 0, 1, 2)
block:
iterator it(): int {.closure.} =
try:
try:
yield 0
raiseTestError()
finally:
return # Return in finally should stop exception propagation
except AnotherError:
yield 2
return
finally:
yield 3
checkpoint(123)
test(it, 0, 3)
block: # Yield in yield
iterator it(): int {.closure.} =
template foo(): int =
yield 1
2
for i in 0 .. 2:
checkpoint(0)
yield foo()
test(it, 0, 1, 2, 0, 1, 2, 0, 1, 2)
block:
iterator it(): int {.closure.} =
let i = if true:
yield 0
1
else:
2
yield i
test(it, 0, 1)
block:
iterator it(): int {.closure.} =
var foo = 123
let i = try:
yield 0
raiseTestError()
1
except TestError as e:
assert(e.msg == "Test exception!")
case foo
of 1:
yield 123
2
of 123:
yield 5
6
else:
7
yield i
test(it, 0, 5, 6)
block:
iterator it(): int {.closure.} =
proc voidFoo(i1, i2, i3: int) =
checkpoint(i1)
checkpoint(i2)
checkpoint(i3)
proc foo(i1, i2, i3: int): int =
voidFoo(i1, i2, i3)
i3
proc bar(i1: int): int =
checkpoint(i1)
template tryexcept: int =
try:
yield 1
raiseTestError()
123
except TestError:
yield 2
checkpoint(3)
4
let e1 = true
template ifelse1: int =
if e1:
yield 10
11
else:
12
template ifelse2: int =
if ifelse1() == 12:
yield 20
21
else:
yield 22
23
let i = foo(bar(0), tryexcept, ifelse2)
discard foo(bar(0), tryexcept, ifelse2)
voidFoo(bar(0), tryexcept, ifelse2)
yield i
test(it,
# let i = foo(bar(0), tryexcept, ifelse2)
0, # bar(0)
1, 2, 3, # tryexcept
10, # ifelse1
22, # ifelse22
0, 4, 23, # foo
# discard foo(bar(0), tryexcept, ifelse2)
0, # bar(0)
1, 2, 3, # tryexcept
10, # ifelse1
22, # ifelse22
0, 4, 23, # foo
# voidFoo(bar(0), tryexcept, ifelse2)
0, # bar(0)
1, 2, 3, # tryexcept
10, # ifelse1
22, # ifelse22
0, 4, 23, # foo
23 # i
)
block:
iterator it(): int {.closure.} =
checkpoint(0)
for i in 0 .. 1:
try:
yield 1
raiseTestError()
except TestError as e:
doAssert(e.msg == "Test exception!")
yield 2
except AnotherError:
yield 123
except:
yield 1234
finally:
yield 3
checkpoint(4)
yield 5
test(it, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
block:
iterator it(): int {.closure.} =
var i = 5
template foo(): bool =
yield i
true
while foo():
dec i
if i == 0:
break
test(it, 5, 4, 3, 2, 1)
block: # Short cirquits
iterator it(): int {.closure.} =
template trueYield: bool =
yield 1
true
template falseYield: bool =
yield 0
false
if trueYield or falseYield:
discard falseYield and trueYield
if falseYield and trueYield:
checkpoint(123)
test(it, 1, 0, 0)
block: #7969
type
SomeObj = object
id: int
iterator it(): int {.closure.} =
template yieldAndSomeObj: SomeObj =
var s: SomeObj
s.id = 2
yield 1
s
checkpoint(yieldAndSomeObj().id)
var i = 5
case i
of 0:
checkpoint(123)
of 1, 2, 5:
checkpoint(3)
else:
checkpoint(123)
test(it, 1, 2, 3)
block: # yield in blockexpr
iterator it(): int {.closure.} =
yield(block:
checkpoint(1)
yield 2
3
)
test(it, 1, 2, 3)
block: #8851
type
Foo = ref object of RootObj
template someFoo(): Foo =
var f: Foo
yield 1
f
iterator it(): int {.closure.} =
var o: RootRef
o = someFoo()
test(it, 1)
block: # 8243
iterator it(): int {.closure.} =
template yieldAndSeq: seq[int] =
yield 1
@[123, 5, 123]
checkpoint(yieldAndSeq[1])
test(it, 1, 5)
block:
iterator it(): int {.closure.} =
template yieldAndSeq: seq[int] =
yield 1
@[123, 5, 123]
template yieldAndNum: int =
yield 2
1
checkpoint(yieldAndSeq[yieldAndNum])
test(it, 1, 2, 5)
block: #9694 - yield in ObjConstr
type Foo = object
a, b: int
template yieldAndNum: int =
yield 1
2
iterator it(): int {.closure.} =
let a = Foo(a: 5, b: yieldAndNum())
checkpoint(a.b)
test(it, 1, 2)
block: #9716
iterator it(): int {.closure.} =
var a = 0
for i in 1 .. 3:
var a: int # Make sure the "local" var is reset
var b: string # ditto
yield 1
a += 5
b &= "hello"
doAssert(a == 5)
doAssert(b == "hello")
test(it, 1, 1, 1)
block: # nnkChckRange
type Foo = distinct uint64
template yieldDistinct: Foo =
yield 2
Foo(0)
iterator it(): int {.closure.} =
yield 1
var a: int
a = int(yieldDistinct())
yield 3
test(it, 1, 2, 3)
block: #17849 - yield in case subject
template yieldInCase: int =
yield 2
3
iterator it(): int {.closure.} =
yield 1
case yieldInCase()
of 1: checkpoint(11)
of 3: checkpoint(13)
else: checkpoint(14)
yield 5
test(it, 1, 2, 13, 5)
block: # void iterator
iterator it() {.closure.} =
try:
yield
except:
discard
var a = it
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")
iterator it(): int {.closure.} =
var a = 1
var b = 2
var c {.liftLocals.} = 3
checkOnStack(addr a, true)
checkOnStack(addr b, false)
checkOnStack(addr c, false)
yield a
yield b
test(it, 1, 2)
block: # Complex finallies (#24978)
iterator it(): int {.closure.} =
try:
for i in 1..2:
try:
yield i + 10
except:
doAssert(false, "Should not get here")
checkpoint(i + 20)
raiseTestError()
finally:
for i in 3..4:
try:
yield i + 30
except:
doAssert(false, "Should not get here")
finally:
checkpoint(i + 40)
checkpoint(i + 50)
checkpoint(100)
testExc(it, 11, 21, 12, 22, 33, 43, 53, 34, 44, 54, 100)
block: # break
iterator it(): int {.closure.} =
while true:
try:
yield 1
while true:
yield 2
if true:
break
break
finally:
var localHere = 3
checkpoint(localHere)
test(it, 1, 2, 3)
block: # break
iterator it(): int {.closure.} =
while true:
try:
try:
yield 1
while true:
yield 2
break
break
finally:
var localHere = 4
yield 3
checkpoint(localHere)
doAssert(false, "Should not get here")
finally:
yield 5
checkpoint(6)
doAssert(false, "Should not reach here")
test(it, 1, 2, 3, 4, 5, 6)
block: # continue
iterator it(): int {.closure.} =
for i in 1 .. 3:
try:
try:
yield i + 10
while true:
yield i + 20
break
if i == 2:
continue
checkpoint(i + 30)
finally:
yield 3
checkpoint(4)
checkpoint(5)
finally:
yield 6
checkpoint(7)
test(it, 11, 21, 31, 3, 4, 5, 6, 7, 12, 22, 3, 4, 6, 7, 13, 23, 33, 3, 4, 5, 6, 7)
block: # return without finally
iterator it(): int {.closure.} =
try:
yield 1
if true:
return
except:
doAssert(false, "Unreachable")
yield 2
test(it, 1)
block: # return in finally
iterator it(): int {.closure.} =
try:
yield 1
except:
doAssert(false, "Unreachable")
finally:
return
yield 2
test(it, 1)
block: # launch iter with current exception
iterator it(): int {.closure.} =
try:
yield 1
finally:
discard
try:
raise newException(ValueError, "")
except:
test(it, 1)
block: #21235
proc myFunc() =
iterator myFuncIter(): int {.closure.} =
if false:
try:
yield 5
except:
discard
var nameIterVar = myFuncIter
discard nameIterVar()
var ok = false
try:
try:
raise ValueError.newException("foo")
finally:
myFunc()
except ValueError:
ok = true
doAssert(ok)
block: # break in for without yield in try
iterator it(): int {.closure.} =
try:
block:
checkpoint(1)
for i in 0 .. 10:
checkpoint(2)
break
checkpoint(3)
try:
yield 4
except:
checkpoint(123)
except:
discard
finally:
checkpoint(5)
test(it, 1, 2, 3, 4, 5)
block: #25038
template m(w: untyped): untyped =
var g: typeof(w)
g
iterator d(): int {.closure.} =
discard m:
for _ in [0]:
continue
0
test(d)
block: #25202
proc p() =
iterator p_1073741828(checkpoints: var seq[int]): int {.
closure, raises: [].} =
var closureSucceeded_1073741827 = true
try:
try:
try:
yield 0
raise newException(ValueError, "value error")
except ValueError:
checkpoints.add(1)
raise newException(IOError, "io error")
finally:
yield 2
except IOError as exc:
closureSucceeded_1073741827 = false
checkpoints.add(3)
finally:
checkpoints.add(4)
if closureSucceeded_1073741827:
discard
var internalClosure = p_1073741828
var internalClosure2 = p_1073741828
var checkpoints1 = newSeq[int]()
var checkpoints2 = newSeq[int]()
while true:
if not internalClosure.finished():
checkpoints1.add internalClosure(checkpoints1)
doAssert(getCurrentException() == nil)
if not internalClosure2.finished():
checkpoints2.add internalClosure2(checkpoints2)
doAssert(getCurrentException() == nil)
if internalClosure.finished() and internalClosure2.finished():
break
if checkpoints1[^1] == 0: checkpoints1.del(checkpoints1.high)
if checkpoints2[^1] == 0: checkpoints2.del(checkpoints2.high)
doAssert(checkpoints1 == @[0, 1, 2, 3, 4])
doAssert(checkpoints1 == checkpoints2)
p()