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 var maxIterations = 10000 try: for i in it(): closureIterResult.add(i) dec maxIterations doAssert(maxIterations > 0, "Too many iterations in test. Infinite loop?") 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() block: #25261 iterator y(): int {.closure.} = try: try: raise newException(CatchableError, "Error") except CatchableError: return 123 yield 0 finally: discard let w = y doAssert(w() == 123) doAssert(getCurrentExceptionMsg() == "") try: raise newException(ValueError, "Outer error") except: doAssert(getCurrentExceptionMsg() == "Outer error") let w = y doAssert(w() == 123) doAssert(getCurrentExceptionMsg() == "Outer error") doAssert(getCurrentExceptionMsg() == "") block: # Looks almost like above, but last finally changed to except iterator y(): int {.closure.} = try: try: raise newException(CatchableError, "Error") except CatchableError: return 123 yield 0 except: discard let w = y doAssert(w() == 123) doAssert(getCurrentExceptionMsg() == "") try: raise newException(ValueError, "Outer error") except: doAssert(getCurrentExceptionMsg() == "Outer error") let w = y doAssert(w() == 123) doAssert(getCurrentExceptionMsg() == "Outer error") doAssert(getCurrentExceptionMsg() == "") block: #25330 (v1) iterator count1(): int {.closure.} = yield 1 raiseTestError() iterator count0(): int {.closure.} = try: var count = count1 while true: yield count() if finished(count): break finally: try: checkpoint(2) var count2 = count1 while true: yield count2() if finished(count2): break discard # removing this outputs "raise" except: checkpoint(3) raise testExc(count0, 1, 2, 1, 3) block: #25330 (v2) iterator count1(): int {.closure.} = yield 1 raiseTestError() iterator count0(): int {.closure.} = try: var count = count1 for x in 0 .. 10: yield count() finally: try: checkpoint(2) var count2 = count1 for x in 0 .. 10: yield count2() except: checkpoint(3) raise testExc(count0, 1, 2, 1, 3)