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