tmovebug.nim 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. discard """
  2. cmd: "nim c --gc:arc $file"
  3. output: '''5
  4. (w: 5)
  5. (w: -5)
  6. c.text = hello
  7. c.text = hello
  8. p.text = hello
  9. p.toks = @["hello"]
  10. c.text = hello
  11. c[].text = hello
  12. pA.text = hello
  13. pA.toks = @["hello"]
  14. c.text = hello
  15. c.text = hello
  16. pD.text = hello
  17. pD.toks = @["hello"]
  18. c.text = hello
  19. c.text = hello
  20. pOD.text = hello
  21. pOD.toks = @["hello"]
  22. fff
  23. fff
  24. 2
  25. fff
  26. fff
  27. 2
  28. fff
  29. fff
  30. 2
  31. mmm
  32. fff
  33. fff
  34. fff
  35. 3
  36. mmm
  37. sink me (sink)
  38. assign me (not sink)
  39. sink me (not sink)
  40. sinked and not optimized to a bitcopy
  41. sinked and not optimized to a bitcopy
  42. sinked and not optimized to a bitcopy
  43. (data: @[0, 0])
  44. (data: @[0, 0])
  45. (data: @[0, 0])
  46. (data: @[0, 0])
  47. (data: @[0, 0])
  48. (data: @[0, 0])
  49. (data: @[0, 0])
  50. 100
  51. hey
  52. hey
  53. (a: "a", b: 2)
  54. ho
  55. (a: "b", b: 3)
  56. (b: "b", a: 2)
  57. ho
  58. (b: "a", a: 3)
  59. hey
  60. break
  61. break
  62. hey
  63. ho
  64. hey
  65. ho
  66. ho
  67. king
  68. live long; long live
  69. king
  70. hi
  71. try
  72. bye
  73. ()
  74. ()
  75. ()
  76. 1
  77. destroy
  78. 1
  79. destroy
  80. 1
  81. destroy
  82. copy (self-assign)
  83. 1
  84. destroy
  85. 1
  86. destroy
  87. 1
  88. destroy
  89. destroy
  90. copy
  91. @[(f: 2), (f: 2), (f: 3)]
  92. destroy
  93. destroy
  94. destroy
  95. sink
  96. sink
  97. destroy
  98. copy
  99. (f: 1)
  100. destroy
  101. destroy
  102. part-to-whole assigment:
  103. sink
  104. (children: @[])
  105. destroy
  106. sink
  107. (children: @[])
  108. destroy
  109. copy
  110. destroy
  111. (f: 1)
  112. destroy
  113. '''
  114. """
  115. # move bug
  116. type
  117. TMyObj = object
  118. p: pointer
  119. len: int
  120. var destroyCounter = 0
  121. proc `=destroy`(o: var TMyObj) =
  122. if o.p != nil:
  123. dealloc o.p
  124. o.p = nil
  125. inc destroyCounter
  126. proc `=`(dst: var TMyObj, src: TMyObj) =
  127. `=destroy`(dst)
  128. dst.p = alloc(src.len)
  129. dst.len = src.len
  130. proc `=sink`(dst: var TMyObj, src: TMyObj) =
  131. `=destroy`(dst)
  132. dst.p = src.p
  133. dst.len = src.len
  134. type
  135. TObjKind = enum Z, A, B
  136. TCaseObj = object
  137. case kind: TObjKind
  138. of Z: discard
  139. of A:
  140. x1: int # this int plays important role
  141. x2: TMyObj
  142. of B:
  143. y: TMyObj
  144. proc use(a: TCaseObj) = discard
  145. proc moveBug(i: var int) =
  146. var a: array[2, TCaseObj]
  147. a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 1
  148. a[i+1] = a[i] # 2
  149. inc i
  150. use(a[i-1])
  151. var x = 0
  152. moveBug(x)
  153. proc moveBug2(): (TCaseObj, TCaseObj) =
  154. var a: array[2, TCaseObj]
  155. a[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
  156. a[1] = a[0] # can move 3
  157. result[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 4
  158. result[1] = result[0] # 5
  159. proc main =
  160. discard moveBug2()
  161. main()
  162. echo destroyCounter
  163. # bug #13314
  164. type
  165. O = object
  166. v: int
  167. R = ref object
  168. w: int
  169. proc `$`(r: R): string = $r[]
  170. proc tbug13314 =
  171. var t5 = R(w: 5)
  172. var execute = proc () =
  173. echo t5
  174. execute()
  175. t5.w = -5
  176. execute()
  177. tbug13314()
  178. #-------------------------------------------------------------------------
  179. # bug #13368
  180. import strutils
  181. proc procStat() =
  182. for line in @["a b", "c d", "e f"]:
  183. let cols = line.splitWhitespace(maxSplit=1)
  184. let x = cols[0]
  185. let (nm, rest) = (cols[0], cols[1])
  186. procStat()
  187. # bug #14269
  188. import sugar, strutils
  189. type
  190. Cursor = object
  191. text: string
  192. Parsed = object
  193. text: string
  194. toks: seq[string]
  195. proc tokenize(c: var Cursor): seq[string] =
  196. dump c.text
  197. return c.text.splitWhitespace()
  198. proc parse(): Parsed =
  199. var c = Cursor(text: "hello")
  200. dump c.text
  201. return Parsed(text: c.text, toks: c.tokenize) # note: c.tokenized uses c.text
  202. let p = parse()
  203. dump p.text
  204. dump p.toks
  205. proc tokenizeA(c: ptr Cursor): seq[string] =
  206. dump c[].text
  207. return c[].text.splitWhitespace()
  208. proc parseA(): Parsed =
  209. var c = Cursor(text: "hello")
  210. dump c.text
  211. return Parsed(text: c.text, toks: c.addr.tokenizeA) # note: c.tokenized uses c.text
  212. let pA = parseA()
  213. dump pA.text
  214. dump pA.toks
  215. proc tokenizeD(c: Cursor): seq[string] =
  216. dump c.text
  217. return c.text.splitWhitespace()
  218. proc parseD(): Parsed =
  219. var c = cast[ptr Cursor](alloc0(sizeof(Cursor)))
  220. c[] = Cursor(text: "hello")
  221. dump c.text
  222. return Parsed(text: c.text, toks: c[].tokenizeD) # note: c.tokenized uses c.text
  223. let pD = parseD()
  224. dump pD.text
  225. dump pD.toks
  226. # Bug would only pop up with owned refs
  227. proc tokenizeOD(c: Cursor): seq[string] =
  228. dump c.text
  229. return c.text.splitWhitespace()
  230. proc parseOD(): Parsed =
  231. var c = new Cursor
  232. c[] = Cursor(text: "hello")
  233. dump c.text
  234. return Parsed(text: c.text, toks: c[].tokenizeOD) # note: c.tokenized uses c.text
  235. let pOD = parseOD()
  236. dump pOD.text
  237. dump pOD.toks
  238. when false:
  239. # Bug would only pop up with owned refs and implicit derefs, but since they don't work together..
  240. {.experimental: "implicitDeref".}
  241. proc tokenizeOHD(c: Cursor): seq[string] =
  242. dump c.text
  243. return c.text.splitWhitespace()
  244. proc parseOHD(): Parsed =
  245. var c = new Cursor
  246. c[] = Cursor(text: "hello")
  247. dump c.text
  248. return Parsed(text: c.text, toks: c.tokenizeOHD) # note: c.tokenized uses c.text
  249. let pOHD = parseOHD()
  250. dump pOHD.text
  251. dump pOHD.toks
  252. # bug #13456
  253. iterator combinations[T](s: openArray[T], k: int): seq[T] =
  254. let n = len(s)
  255. assert k >= 0 and k <= n
  256. var pos = newSeq[int](k)
  257. var current = newSeq[T](k)
  258. for i in 0..k-1:
  259. pos[k-i-1] = i
  260. var done = false
  261. while not done:
  262. for i in 0..k-1:
  263. current[i] = s[pos[k-i-1]]
  264. yield current
  265. var i = 0
  266. while i < k:
  267. pos[i] += 1
  268. if pos[i] < n-i:
  269. for j in 0..i-1:
  270. pos[j] = pos[i] + i - j
  271. break
  272. i += 1
  273. if i >= k:
  274. break
  275. type
  276. UndefEx = object of ValueError
  277. proc main2 =
  278. var delayedSyms = @[1, 2, 3]
  279. var unp: seq[int]
  280. block myb:
  281. for a in 1 .. 2:
  282. if delayedSyms.len > a:
  283. unp = delayedSyms
  284. for t in unp.combinations(a + 1):
  285. try:
  286. var h = false
  287. for k in t:
  288. echo "fff"
  289. if h: continue
  290. if true:
  291. raise newException(UndefEx, "forward declaration")
  292. break myb
  293. except UndefEx:
  294. echo t.len
  295. echo "mmm"
  296. main2()
  297. type ME = object
  298. who: string
  299. proc `=`(x: var ME, y: ME) =
  300. if y.who.len > 0: echo "assign ",y.who
  301. proc `=sink`(x: var ME, y: ME) =
  302. if y.who.len > 0: echo "sink ",y.who
  303. var dump: ME
  304. template use(x) = dump = x
  305. template def(x) = x = dump
  306. var c = true
  307. proc shouldSink() =
  308. var x = ME(who: "me (sink)")
  309. use(x) # we analyse this
  310. if c: def(x)
  311. else: def(x)
  312. use(x) # ok, with the [else] part.
  313. shouldSink()
  314. dump = ME()
  315. proc shouldNotSink() =
  316. var x = ME(who: "me (not sink)")
  317. use(x) # we analyse this
  318. if c: def(x)
  319. use(x) # Not ok without the '[else]'
  320. shouldNotSink()
  321. # bug #14568
  322. import os
  323. type O2 = object
  324. s: seq[int]
  325. proc `=sink`(dest: var O2, src: O2) =
  326. echo "sinked and not optimized to a bitcopy"
  327. var testSeq: O2
  328. proc update() =
  329. # testSeq.add(0) # uncommenting this line fixes the leak
  330. testSeq = O2(s: @[])
  331. testSeq.s.add(0)
  332. for i in 1..3:
  333. update()
  334. # bug #14961
  335. type
  336. Foo = object
  337. data: seq[int]
  338. proc initFoo(len: int): Foo =
  339. result = (let s = newSeq[int](len); Foo(data: s) )
  340. var f = initFoo(2)
  341. echo initFoo(2)
  342. proc initFoo2(len: int) =
  343. echo if true:
  344. let s = newSeq[int](len); Foo(data: s)
  345. else:
  346. let s = newSeq[int](len); Foo(data: s)
  347. initFoo2(2)
  348. proc initFoo3(len: int) =
  349. echo (block:
  350. let s = newSeq[int](len); Foo(data: s))
  351. initFoo3(2)
  352. proc initFoo4(len: int) =
  353. echo (let s = newSeq[int](len); Foo(data: s))
  354. initFoo4(2)
  355. proc initFoo5(len: int) =
  356. echo (case true
  357. of true:
  358. let s = newSeq[int](len); Foo(data: s)
  359. of false:
  360. let s = newSeq[int](len); Foo(data: s))
  361. initFoo5(2)
  362. proc initFoo6(len: int) =
  363. echo (block:
  364. try:
  365. let s = newSeq[int](len); Foo(data: s)
  366. finally: discard)
  367. initFoo6(2)
  368. proc initFoo7(len: int) =
  369. echo (block:
  370. try:
  371. raise newException(CatchableError, "sup")
  372. let s = newSeq[int](len); Foo(data: s)
  373. except CatchableError:
  374. let s = newSeq[int](len); Foo(data: s) )
  375. initFoo7(2)
  376. # bug #14902
  377. iterator zip[T](s: openArray[T]): (T, T) =
  378. var i = 0
  379. while i < 10:
  380. yield (s[i mod 2], s[i mod 2 + 1])
  381. inc i
  382. var lastMem = int.high
  383. proc leak =
  384. const len = 10
  385. var x = @[newString(len), newString(len), newString(len)]
  386. var c = 0
  387. for (a, b) in zip(x):
  388. let newMem = getOccupiedMem()
  389. assert newMem <= lastMem
  390. lastMem = newMem
  391. c += a.len
  392. echo c
  393. leak()
  394. proc consume(a: sink string) = echo a
  395. proc weirdScopes =
  396. if (let a = "hey"; a.len > 0):
  397. echo a
  398. while (let a = "hey"; a.len > 0):
  399. echo a
  400. break
  401. var a = block: (a: "a", b: 2)
  402. echo a
  403. (discard; a) = (echo "ho"; (a: "b", b: 3))
  404. echo a
  405. var b = try: (b: "b", a: 2)
  406. except: raise
  407. echo b
  408. (discard; b) = (echo "ho"; (b: "a", a: 3))
  409. echo b
  410. var s = "break"
  411. consume((echo "hey"; s))
  412. echo s
  413. echo (block:
  414. var a = "hey"
  415. (echo "hey"; "ho"))
  416. var b2 = "ho"
  417. echo (block:
  418. var a = "hey"
  419. (echo "hey"; b2))
  420. echo b2
  421. type status = enum
  422. alive
  423. var king = "king"
  424. echo (block:
  425. var a = "a"
  426. when true:
  427. var b = "b"
  428. case alive
  429. of alive:
  430. try:
  431. var c = "c"
  432. if true:
  433. king
  434. else:
  435. "the abyss"
  436. except:
  437. echo "he ded"
  438. "dead king")
  439. echo "live long; long live"
  440. echo king
  441. weirdScopes()
  442. # bug #14985
  443. proc getScope(): string =
  444. if true:
  445. return "hi"
  446. else:
  447. "else"
  448. echo getScope()
  449. proc getScope3(): string =
  450. try:
  451. "try"
  452. except:
  453. return "except"
  454. echo getScope3()
  455. proc getScope2(): string =
  456. case true
  457. of true:
  458. return "bye"
  459. else:
  460. "else"
  461. echo getScope2()
  462. #--------------------------------------------------------------------
  463. #bug #15609
  464. type
  465. Wrapper = object
  466. discard
  467. proc newWrapper(): ref Wrapper =
  468. new(result)
  469. result
  470. proc newWrapper2(a: int): ref Wrapper =
  471. new(result)
  472. if a > 0:
  473. result
  474. else:
  475. new(Wrapper)
  476. let w1 = newWrapper()
  477. echo $w1[]
  478. let w2 = newWrapper2(1)
  479. echo $w2[]
  480. let w3 = newWrapper2(-1)
  481. echo $w3[]
  482. #--------------------------------------------------------------------
  483. #self-assignments
  484. # Self-assignments that are not statically determinable will get
  485. # turned into `=copy` calls as caseBracketExprCopy demonstrates.
  486. # (`=copy` handles self-assignments at runtime)
  487. type
  488. OO = object
  489. f: int
  490. W = object
  491. o: OO
  492. proc `=destroy`(x: var OO) =
  493. if x.f != 0:
  494. echo "destroy"
  495. x.f = 0
  496. proc `=sink`(x: var OO, y: OO) =
  497. `=destroy`(x)
  498. echo "sink"
  499. x.f = y.f
  500. proc `=copy`(x: var OO, y: OO) =
  501. if x.f != y.f:
  502. `=destroy`(x)
  503. echo "copy"
  504. x.f = y.f
  505. else:
  506. echo "copy (self-assign)"
  507. proc caseSym =
  508. var o = OO(f: 1)
  509. o = o # NOOP
  510. echo o.f # "1"
  511. # "destroy"
  512. caseSym()
  513. proc caseDotExpr =
  514. var w = W(o: OO(f: 1))
  515. w.o = w.o # NOOP
  516. echo w.o.f # "1"
  517. # "destroy"
  518. caseDotExpr()
  519. proc caseBracketExpr =
  520. var w = [0: OO(f: 1)]
  521. w[0] = w[0] # NOOP
  522. echo w[0].f # "1"
  523. # "destroy"
  524. caseBracketExpr()
  525. proc caseBracketExprCopy =
  526. var w = [0: OO(f: 1)]
  527. let i = 0
  528. w[i] = w[0] # "copy (self-assign)"
  529. echo w[0].f # "1"
  530. # "destroy"
  531. caseBracketExprCopy()
  532. proc caseDotExprAddr =
  533. var w = W(o: OO(f: 1))
  534. w.o = addr(w.o)[] # NOOP
  535. echo w.o.f # "1"
  536. # "destroy"
  537. caseDotExprAddr()
  538. proc caseBracketExprAddr =
  539. var w = [0: OO(f: 1)]
  540. addr(w[0])[] = addr(addr(w[0])[])[] # NOOP
  541. echo w[0].f # "1"
  542. # "destroy"
  543. caseBracketExprAddr()
  544. proc caseNotAConstant =
  545. var i = 0
  546. proc rand: int =
  547. result = i
  548. inc i
  549. var s = @[OO(f: 1), OO(f: 2), OO(f: 3)]
  550. s[rand()] = s[rand()] # "destroy" "copy"
  551. echo s # @[(f: 2), (f: 2), (f: 3)]
  552. caseNotAConstant()
  553. proc potentialSelfAssign(i: var int) =
  554. var a: array[2, OO]
  555. a[i] = OO(f: 1) # turned into a memcopy
  556. a[1] = OO(f: 2)
  557. a[i+1] = a[i] # This must not =sink, but =copy
  558. inc i
  559. echo a[i-1] # (f: 1)
  560. potentialSelfAssign (var xi = 0; xi)
  561. #--------------------------------------------------------------------
  562. echo "part-to-whole assigment:"
  563. type
  564. Tree = object
  565. children: seq[Tree]
  566. TreeDefaultHooks = object
  567. children: seq[TreeDefaultHooks]
  568. proc `=destroy`(x: var Tree) = echo "destroy"
  569. proc `=sink`(x: var Tree, y: Tree) = echo "sink"
  570. proc `=copy`(x: var Tree, y: Tree) = echo "copy"
  571. proc partToWholeSeq =
  572. var t = Tree(children: @[Tree()])
  573. t = t.children[0] # This should be sunk, but with the special transform (tmp = t.children[0]; wasMoved(0); `=sink`(t, tmp))
  574. var tc = TreeDefaultHooks(children: @[TreeDefaultHooks()])
  575. tc = tc.children[0] # Ditto; if this were sunk with the normal transform (`=sink`(t, t.children[0]); wasMoved(t.children[0]))
  576. echo tc # then it would crash because t.children[0] does not exist after the call to `=sink`
  577. partToWholeSeq()
  578. proc partToWholeSeqRTIndex =
  579. var i = 0
  580. var t = Tree(children: @[Tree()])
  581. t = t.children[i] # See comment in partToWholeSeq
  582. var tc = TreeDefaultHooks(children: @[TreeDefaultHooks()])
  583. tc = tc.children[i] # See comment in partToWholeSeq
  584. echo tc
  585. partToWholeSeqRTIndex()
  586. type List = object
  587. next: ref List
  588. proc `=destroy`(x: var List) = echo "destroy"
  589. proc `=sink`(x: var List, y: List) = echo "sink"
  590. proc `=copy`(x: var List, y: List) = echo "copy"
  591. proc partToWholeUnownedRef =
  592. var t = List(next: new List)
  593. t = t.next[] # Copy because t.next is not an owned ref, and thus t.next[] cannot be moved
  594. partToWholeUnownedRef()
  595. #--------------------------------------------------------------------
  596. # test that nodes that get copied during the transformation
  597. # (like dot exprs) don't loose their firstWrite/lastRead property
  598. type
  599. OOO = object
  600. initialized: bool
  601. C = object
  602. o: OOO
  603. proc `=destroy`(o: var OOO) =
  604. doAssert o.initialized, "OOO was destroyed before initialization!"
  605. proc initO(): OOO =
  606. OOO(initialized: true)
  607. proc initC(): C =
  608. C(o: initO())
  609. proc pair(): tuple[a: C, b: C] =
  610. result = (a: initC(), b: initC())# <- when firstWrite tries to find this node to start its analysis it fails, because injectdestructors uses copyTree/shallowCopy
  611. discard pair()
  612. # bug #17450
  613. proc noConsume(x: OO) {.nosinks.} = echo x
  614. proc main3 =
  615. var i = 1
  616. noConsume:
  617. block:
  618. OO(f: i)
  619. main3()
  620. # misc
  621. proc smoltest(x: bool): bool =
  622. while true:
  623. if true: return x
  624. discard smoltest(true)
  625. # bug #18002
  626. type
  627. TTypeAttachedOp = enum
  628. attachedAsgn
  629. attachedSink
  630. attachedTrace
  631. PNode = ref object
  632. discard
  633. proc genAddrOf(n: PNode) =
  634. assert n != nil, "moved?!"
  635. proc atomicClosureOp =
  636. let x = PNode()
  637. genAddrOf:
  638. block:
  639. x
  640. case attachedTrace
  641. of attachedSink: discard
  642. of attachedAsgn: discard
  643. of attachedTrace: genAddrOf(x)
  644. atomicClosureOp()
  645. template assertEq(a, b: untyped): untyped =
  646. block:
  647. let
  648. aval = a
  649. bval = b
  650. if aval != bval:
  651. quit "bug!"
  652. proc convoluted =
  653. let _ = (;
  654. var val1: string;
  655. if true: val1 = "22"
  656. true
  657. )
  658. assertEq val1, "22"
  659. assertEq val1, "22"
  660. convoluted()