tyieldintry.nim 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. discard """
  2. matrix: "; --experimental:strictdefs"
  3. targets: "c cpp"
  4. """
  5. var closureIterResult = newSeq[int]()
  6. proc checkpoint(arg: int) =
  7. closureIterResult.add(arg)
  8. type
  9. TestError = object of CatchableError
  10. AnotherError = object of CatchableError
  11. proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) =
  12. closureIterResult.setLen(0)
  13. var exceptionCaught = false
  14. try:
  15. for i in it():
  16. closureIterResult.add(i)
  17. except TestError:
  18. exceptionCaught = true
  19. if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected:
  20. if closureIterResult != @expectedResults:
  21. echo "Expected: ", @expectedResults
  22. echo "Actual: ", closureIterResult
  23. if exceptionCaught != exceptionExpected:
  24. echo "Expected exception: ", exceptionExpected
  25. echo "Got exception: ", exceptionCaught
  26. doAssert(false)
  27. proc test(it: iterator(): int, expectedResults: varargs[int]) =
  28. testClosureIterAux(it, false, expectedResults)
  29. proc testExc(it: iterator(): int, expectedResults: varargs[int]) =
  30. testClosureIterAux(it, true, expectedResults)
  31. proc raiseTestError() =
  32. raise newException(TestError, "Test exception!")
  33. block:
  34. iterator it(): int {.closure.} =
  35. var i = 5
  36. while i != 0:
  37. yield i
  38. if i == 3:
  39. yield 123
  40. dec i
  41. test(it, 5, 4, 3, 123, 2, 1)
  42. block:
  43. iterator it(): int {.closure.} =
  44. yield 0
  45. try:
  46. checkpoint(1)
  47. raiseTestError()
  48. except TestError:
  49. checkpoint(2)
  50. yield 3
  51. checkpoint(4)
  52. finally:
  53. checkpoint(5)
  54. checkpoint(6)
  55. test(it, 0, 1, 2, 3, 4, 5, 6)
  56. block:
  57. iterator it(): int {.closure.} =
  58. yield 0
  59. try:
  60. yield 1
  61. checkpoint(2)
  62. finally:
  63. checkpoint(3)
  64. yield 4
  65. checkpoint(5)
  66. yield 6
  67. test(it, 0, 1, 2, 3, 4, 5, 6)
  68. block:
  69. iterator it(): int {.closure.} =
  70. yield 0
  71. try:
  72. yield 1
  73. raiseTestError()
  74. yield 2
  75. finally:
  76. checkpoint(3)
  77. yield 4
  78. checkpoint(5)
  79. yield 6
  80. testExc(it, 0, 1, 3, 4, 5, 6)
  81. block:
  82. iterator it(): int {.closure.} =
  83. try:
  84. try:
  85. raiseTestError()
  86. except AnotherError:
  87. yield 123
  88. finally:
  89. checkpoint(3)
  90. finally:
  91. checkpoint(4)
  92. testExc(it, 3, 4)
  93. block:
  94. iterator it(): int {.closure.} =
  95. try:
  96. yield 1
  97. raiseTestError()
  98. except AnotherError:
  99. checkpoint(123)
  100. finally:
  101. checkpoint(2)
  102. checkpoint(3)
  103. testExc(it, 1, 2)
  104. block:
  105. iterator it(): int {.closure.} =
  106. try:
  107. yield 0
  108. try:
  109. yield 1
  110. try:
  111. yield 2
  112. raiseTestError()
  113. except AnotherError:
  114. yield 123
  115. finally:
  116. yield 3
  117. except AnotherError:
  118. yield 124
  119. finally:
  120. yield 4
  121. checkpoint(1234)
  122. except:
  123. yield 5
  124. checkpoint(6)
  125. finally:
  126. checkpoint(7)
  127. yield 8
  128. checkpoint(9)
  129. test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
  130. block:
  131. iterator it(): int {.closure.} =
  132. try:
  133. yield 0
  134. return 2
  135. finally:
  136. checkpoint(1)
  137. checkpoint(123)
  138. test(it, 0, 1)
  139. block:
  140. iterator it(): int {.closure.} =
  141. try:
  142. try:
  143. yield 0
  144. raiseTestError()
  145. finally:
  146. checkpoint(1)
  147. except TestError:
  148. yield 2
  149. return
  150. finally:
  151. yield 3
  152. checkpoint(123)
  153. test(it, 0, 1, 2, 3)
  154. block:
  155. iterator it(): int {.closure.} =
  156. try:
  157. try:
  158. yield 0
  159. raiseTestError()
  160. finally:
  161. return # Return in finally should stop exception propagation
  162. except AnotherError:
  163. yield 2
  164. return
  165. finally:
  166. yield 3
  167. checkpoint(123)
  168. test(it, 0, 3)
  169. block: # Yield in yield
  170. iterator it(): int {.closure.} =
  171. template foo(): int =
  172. yield 1
  173. 2
  174. for i in 0 .. 2:
  175. checkpoint(0)
  176. yield foo()
  177. test(it, 0, 1, 2, 0, 1, 2, 0, 1, 2)
  178. block:
  179. iterator it(): int {.closure.} =
  180. let i = if true:
  181. yield 0
  182. 1
  183. else:
  184. 2
  185. yield i
  186. test(it, 0, 1)
  187. block:
  188. iterator it(): int {.closure.} =
  189. var foo = 123
  190. let i = try:
  191. yield 0
  192. raiseTestError()
  193. 1
  194. except TestError as e:
  195. assert(e.msg == "Test exception!")
  196. case foo
  197. of 1:
  198. yield 123
  199. 2
  200. of 123:
  201. yield 5
  202. 6
  203. else:
  204. 7
  205. yield i
  206. test(it, 0, 5, 6)
  207. block:
  208. iterator it(): int {.closure.} =
  209. proc voidFoo(i1, i2, i3: int) =
  210. checkpoint(i1)
  211. checkpoint(i2)
  212. checkpoint(i3)
  213. proc foo(i1, i2, i3: int): int =
  214. voidFoo(i1, i2, i3)
  215. i3
  216. proc bar(i1: int): int =
  217. checkpoint(i1)
  218. template tryexcept: int =
  219. try:
  220. yield 1
  221. raiseTestError()
  222. 123
  223. except TestError:
  224. yield 2
  225. checkpoint(3)
  226. 4
  227. let e1 = true
  228. template ifelse1: int =
  229. if e1:
  230. yield 10
  231. 11
  232. else:
  233. 12
  234. template ifelse2: int =
  235. if ifelse1() == 12:
  236. yield 20
  237. 21
  238. else:
  239. yield 22
  240. 23
  241. let i = foo(bar(0), tryexcept, ifelse2)
  242. discard foo(bar(0), tryexcept, ifelse2)
  243. voidFoo(bar(0), tryexcept, ifelse2)
  244. yield i
  245. test(it,
  246. # let i = foo(bar(0), tryexcept, ifelse2)
  247. 0, # bar(0)
  248. 1, 2, 3, # tryexcept
  249. 10, # ifelse1
  250. 22, # ifelse22
  251. 0, 4, 23, # foo
  252. # discard foo(bar(0), tryexcept, ifelse2)
  253. 0, # bar(0)
  254. 1, 2, 3, # tryexcept
  255. 10, # ifelse1
  256. 22, # ifelse22
  257. 0, 4, 23, # foo
  258. # voidFoo(bar(0), tryexcept, ifelse2)
  259. 0, # bar(0)
  260. 1, 2, 3, # tryexcept
  261. 10, # ifelse1
  262. 22, # ifelse22
  263. 0, 4, 23, # foo
  264. 23 # i
  265. )
  266. block:
  267. iterator it(): int {.closure.} =
  268. checkpoint(0)
  269. for i in 0 .. 1:
  270. try:
  271. yield 1
  272. raiseTestError()
  273. except TestError as e:
  274. doAssert(e.msg == "Test exception!")
  275. yield 2
  276. except AnotherError:
  277. yield 123
  278. except:
  279. yield 1234
  280. finally:
  281. yield 3
  282. checkpoint(4)
  283. yield 5
  284. test(it, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
  285. block:
  286. iterator it(): int {.closure.} =
  287. var i = 5
  288. template foo(): bool =
  289. yield i
  290. true
  291. while foo():
  292. dec i
  293. if i == 0:
  294. break
  295. test(it, 5, 4, 3, 2, 1)
  296. block: # Short cirquits
  297. iterator it(): int {.closure.} =
  298. template trueYield: bool =
  299. yield 1
  300. true
  301. template falseYield: bool =
  302. yield 0
  303. false
  304. if trueYield or falseYield:
  305. discard falseYield and trueYield
  306. if falseYield and trueYield:
  307. checkpoint(123)
  308. test(it, 1, 0, 0)
  309. block: #7969
  310. type
  311. SomeObj = object
  312. id: int
  313. iterator it(): int {.closure.} =
  314. template yieldAndSomeObj: SomeObj =
  315. var s: SomeObj
  316. s.id = 2
  317. yield 1
  318. s
  319. checkpoint(yieldAndSomeObj().id)
  320. var i = 5
  321. case i
  322. of 0:
  323. checkpoint(123)
  324. of 1, 2, 5:
  325. checkpoint(3)
  326. else:
  327. checkpoint(123)
  328. test(it, 1, 2, 3)
  329. block: # yield in blockexpr
  330. iterator it(): int {.closure.} =
  331. yield(block:
  332. checkpoint(1)
  333. yield 2
  334. 3
  335. )
  336. test(it, 1, 2, 3)
  337. block: #8851
  338. type
  339. Foo = ref object of RootObj
  340. template someFoo(): Foo =
  341. var f: Foo
  342. yield 1
  343. f
  344. iterator it(): int {.closure.} =
  345. var o: RootRef
  346. o = someFoo()
  347. test(it, 1)
  348. block: # 8243
  349. iterator it(): int {.closure.} =
  350. template yieldAndSeq: seq[int] =
  351. yield 1
  352. @[123, 5, 123]
  353. checkpoint(yieldAndSeq[1])
  354. test(it, 1, 5)
  355. block:
  356. iterator it(): int {.closure.} =
  357. template yieldAndSeq: seq[int] =
  358. yield 1
  359. @[123, 5, 123]
  360. template yieldAndNum: int =
  361. yield 2
  362. 1
  363. checkpoint(yieldAndSeq[yieldAndNum])
  364. test(it, 1, 2, 5)
  365. block: #9694 - yield in ObjConstr
  366. type Foo = object
  367. a, b: int
  368. template yieldAndNum: int =
  369. yield 1
  370. 2
  371. iterator it(): int {.closure.} =
  372. let a = Foo(a: 5, b: yieldAndNum())
  373. checkpoint(a.b)
  374. test(it, 1, 2)
  375. block: #9716
  376. iterator it(): int {.closure.} =
  377. var a = 0
  378. for i in 1 .. 3:
  379. var a: int # Make sure the "local" var is reset
  380. var b: string # ditto
  381. yield 1
  382. a += 5
  383. b &= "hello"
  384. doAssert(a == 5)
  385. doAssert(b == "hello")
  386. test(it, 1, 1, 1)
  387. block: # nnkChckRange
  388. type Foo = distinct uint64
  389. template yieldDistinct: Foo =
  390. yield 2
  391. Foo(0)
  392. iterator it(): int {.closure.} =
  393. yield 1
  394. var a: int
  395. a = int(yieldDistinct())
  396. yield 3
  397. test(it, 1, 2, 3)
  398. block: #17849 - yield in case subject
  399. template yieldInCase: int =
  400. yield 2
  401. 3
  402. iterator it(): int {.closure.} =
  403. yield 1
  404. case yieldInCase()
  405. of 1: checkpoint(11)
  406. of 3: checkpoint(13)
  407. else: checkpoint(14)
  408. yield 5
  409. test(it, 1, 2, 13, 5)
  410. block: # void iterator
  411. iterator it() {.closure.} =
  412. try:
  413. yield
  414. except:
  415. discard
  416. var a = it
  417. block: # Locals present in only 1 state should be on the stack
  418. proc checkOnStack(a: pointer, shouldBeOnStack: bool) =
  419. # Quick and dirty way to check if a points to stack
  420. var dummy = 0
  421. let dummyAddr = addr dummy
  422. let distance = abs(cast[int](dummyAddr) - cast[int](a))
  423. const requiredDistance = 300
  424. if shouldBeOnStack:
  425. doAssert(distance <= requiredDistance, "a is not on stack, but should")
  426. else:
  427. doAssert(distance > requiredDistance, "a is on stack, but should not")
  428. iterator it(): int {.closure.} =
  429. var a = 1
  430. var b = 2
  431. var c {.liftLocals.} = 3
  432. checkOnStack(addr a, true)
  433. checkOnStack(addr b, false)
  434. checkOnStack(addr c, false)
  435. yield a
  436. yield b
  437. test(it, 1, 2)