tmovebug.nim 14 KB

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