template tests =
  block:
    var i = 0
    i = 2

    var y: ptr int
    doAssert y == nil
    doAssert isNil(y)
    y = i.addr
    y[] = 3
    doAssert i == 3
    doAssert i == y[]

    let z = i.addr
    z[] = 4
    doAssert i == 4
    doAssert i == y[] and y[] == z[]

    var hmm = (a: (b: z))
    var hmmptr = hmm.a.b.addr
    hmmptr[][] = 5

    doAssert i == 5
    doAssert y == z
    doAssert z == hmmptr[]
    doAssert 5 == y[] and 5 == z[] and 5 == hmmptr[][]

  block:
    var someint = 500

    let p: ptr int = someint.addr
    let tup = (f: p)
    let tcopy = tup
    var vtcopy = tcopy
    p[] = 654
    doAssert p[] == 654
    doAssert tup.f[] == 654
    doAssert tcopy.f[] == 654
    doAssert vtcopy.f[] == 654

  block:
    var someint = 500

    var p: ptr int = someint.addr
    let arr = [p]
    let arrc = arr
    p[] = 256
    doAssert someint == 256
    doAssert p[] == 256
    doAssert arr[0][] == 256
    doAssert arrc[0][] == 256

  block:
    var someref: ref int
    new(someref)
    var someref2 = someref

    var tup1 = (f: someref)
    tup1.f = someref
    let tup2 = tup1

    someref[] = 543

    proc passref(r: var ref int): var ref int = r
    new(passref(someref))

    doAssert someref[] == 0
    doAssert tup1.f[] == 543
    doAssert tup2.f[] == 543
    doAssert someref2[] == 543

  block:
    type Whatever = object
      i: ref int

    var someref: ref int
    new(someref)
    someref[] = 10

    let w = Whatever(i: someref)
    var wcopy = w

    someref[] = 20

    doAssert w.i[] == 20
    doAssert someref[] == 20
    doAssert wcopy.i[] == 20
    doAssert w.i == wcopy.i
    #echo w.i[], " ", someref[], " ", wcopy.i[]

  block:
    var oneseq: ref seq[ref int]
    new(oneseq)
    var aref: ref int
    new(aref)
    aref[] = 123
    let arefs = [aref]
    oneseq[] &= arefs[0]
    oneseq[] &= aref
    aref[] = 222
    new(aref)
    doAssert oneseq[0] == oneseq[1]
    doAssert oneseq[0][] == 222
    doAssert oneseq[1][] == 222
    doAssert aref[] == 0

  block:
    var seqs: ref seq[ref seq[ref int]]
    new(seqs)
    seqs[] = newSeq[ref seq[ref int]](1)
    new(seqs[0])
    seqs[0][] = newSeq[ref int](0)

    var aref: ref int
    new aref
    aref[] = 654

    let arefs = [aref]
    doAssert arefs[0] == aref
    seqs[0][] &= arefs[0]
    seqs[0][] &= aref
    seqs[0][1][] = 456
    let seqs2 = seqs
    let same = seqs2[0][0] == seqs2[0][1]
    doAssert arefs[0] == aref
    doAssert aref[] == 456
    doAssert seqs[].len == 1
    doAssert seqs[0][].len == 2
    doAssert seqs[0][0][] == 456
    doAssert seqs[0][1][] == 456
    doAssert same

  block:
    type Obj = object
      x, y: int

    var objrefs: seq[ref Obj] = @[(ref Obj)(nil), nil, nil]
    objrefs[2].new
    objrefs[2][] = Obj(x: 123, y: 321)
    objrefs[1] = objrefs[2]
    doAssert objrefs[0] == nil
    doAssert objrefs[1].y == 321
    doAssert objrefs[2].y == 321
    doAssert objrefs[1] == objrefs[2]

  block:
    var refs: seq[ref string] = @[(ref string)(nil), nil, nil]
    refs[1].new
    refs[1][] = "it's a ref!"
    refs[0] = refs[1]
    refs[2] = refs[1]
    new(refs[0])
    doAssert refs[0][] == ""
    doAssert refs[1][] == "it's a ref!"
    doAssert refs[2][] == "it's a ref!"
    doAssert refs[1] == refs[2]

  block:
    var retaddr_calls = 0
    proc retaddr(p: var int): var int =
      retaddr_calls += 1
      p

    var tfoo_calls = 0
    proc tfoo(x: var int) =
      tfoo_calls += 1
      x += 10
      var y = x.addr
      y[] += 20
      retaddr(x) += 30
      let z = retaddr(x).addr
      z[] += 40

    var ints = @[1, 2, 3]
    tfoo(ints[1])
    doAssert retaddr_calls == 2
    doAssert tfoo_calls == 1
    doAssert ints[1] == 102

    var tbar_calls = 0
    proc tbar(x: var int): var int =
      tbar_calls += 1
      x

    tbar(ints[2]) += 10
    tbar(ints[2]) *= 2
    doAssert tbar_calls == 2

    var tqux_calls = 0
    proc tqux(x: var int): ptr int =
      tqux_calls += 1
      x.addr

    discard tqux(ints[2]) == tqux(ints[2])
    doAssert tqux_calls == 2
    doAssert isNil(tqux(ints[2])) == false
    doAssert tqux_calls == 3

    var tseq_calls = 0
    proc tseq(x: var seq[int]): var seq[int] =
      tseq_calls += 1
      x

    tseq(ints) &= 999
    doAssert tseq_calls == 1
    doAssert ints == @[1, 102, 26, 999]

    var rawints = @[555]
    rawints &= 666
    doAssert rawints == @[555, 666]

    var resetints_calls = 0
    proc resetInts(): int =
      resetints_calls += 1
      ints = @[0, 0, 0]
      1

    proc incr(x: var int; b: int): var int =
      x = x + b
      x

    var q = 0
    var qp = q.addr
    qp[] += 123
    doAssert q == 123
    # check order of evaluation
    doAssert (resetInts() + incr(q, tqux(ints[2])[])) == 124

  block: # reset
    var calls = 0
    proc passsomething(x: var int): var int =
      calls += 1
      x

    var
      a = 123
      b = 500
      c = a.addr
    reset(passsomething(a))
    doAssert calls == 1
    reset(b)
    doAssert a == b
    reset(c)
    doAssert c == nil

  block: # strings
    var calls = 0
    proc stringtest(s: var string): var string =
      calls += 1
      s

    var somestr: string

    stringtest(somestr) &= 'a'
    stringtest(somestr) &= 'b'
    doAssert calls == 2
    doAssert somestr == "ab"
    stringtest(somestr) &= "woot!"
    doAssert somestr == "abwoot!"
    doAssert calls == 3

    doAssert stringtest(somestr).len == 7
    doAssert calls == 4
    doAssert high(stringtest(somestr)) == 6
    doAssert calls == 5

    var somestr2: string
    stringtest(somestr2).setLen(stringtest(somestr).len)
    doAssert calls == 7
    doAssert somestr2.len == somestr.len

    var somestr3: string
    doAssert (somestr3 & "foo") == "foo"

    block:
      var a, b, c, d: string
      d = a & b & c
      doAssert d == ""
      d = stringtest(a) & stringtest(b) & stringtest(c)
      doAssert calls == 10
      doAssert d == ""

  block: # seqs
    var calls = 0
    proc seqtest(s: var seq[int]): var seq[int] =
      calls += 1
      s

    var someseq: seq[int]

    seqtest(someseq) &= 1
    seqtest(someseq) &= 2
    doAssert calls == 2
    doAssert someseq == @[1, 2]
    seqtest(someseq) &= @[3, 4, 5]
    doAssert someseq == @[1, 2, 3, 4, 5]
    doAssert calls == 3

    doAssert seqtest(someseq).len == 5
    doAssert calls == 4
    doAssert high(seqtest(someseq)) == 4
    doAssert calls == 5

    # genArrayAddr
    doAssert seqtest(someseq)[2] == 3
    doAssert calls == 6

    seqtest(someseq).setLen(seqtest(someseq).len)
    doAssert calls == 8

    var somenilseq: seq[int]
    seqtest(somenilseq).setLen(3)
    doAssert calls == 9
    doAssert somenilseq[1] == 0

    someseq = @[1, 2, 3]
    doAssert (seqtest(someseq) & seqtest(someseq)) == @[1, 2, 3, 1, 2, 3]


  block: # mInc, mDec
    var calls = 0
    proc someint(x: var int): var int =
      calls += 1
      x

    var x = 10

    inc(someint(x))
    doAssert x == 11
    doAssert calls == 1

    dec(someint(x))
    doAssert x == 10
    doAssert calls == 2

  block: # uints
    var calls = 0
    proc passuint(x: var uint32): var uint32 =
      calls += 1
      x

    var u: uint32 = 5
    passuint(u) += 1
    doAssert u == 6
    doAssert calls == 1

    passuint(u) -= 1
    doAssert u == 5
    doAssert calls == 2

    passuint(u) *= 2
    doAssert u == 10
    doAssert calls == 3

  block: # objs
    type Thing = ref object
      x, y: int

    var a, b: Thing
    a = Thing()
    b = a

    doAssert a == b

    var calls = 0
    proc passobj(o: var Thing): var Thing =
      calls += 1
      o

    passobj(b) = Thing(x: 123)
    doAssert calls == 1
    doAssert a != b
    doAssert b.x == 123

    var passobjptr_calls = 0
    proc passobjptr(o: var Thing): ptr Thing =
      passobjptr_calls += 1
      o.addr

    passobjptr(b)[] = Thing(x: 234)
    doAssert passobjptr_calls == 1
    doAssert a != b
    doAssert b.x == 234
    passobjptr(b)[].x = 500
    doAssert b.x == 500

    var pptr = passobjptr(b)
    pptr.x += 100
    doAssert b.x == 600

    proc getuninitptr(): ptr int =
      return

    doAssert getuninitptr() == nil

  block: # pointer casting
    var obj = (x: 321, y: 543)
    var x = 500

    var objptr = obj.addr
    var xptr = x.addr

    var p1, p2: pointer
    p1 = cast[pointer](objptr)
    p2 = cast[pointer](xptr)
    doAssert p1 != p2

    p1 = cast[pointer](objptr)
    p2 = cast[pointer](objptr)
    doAssert p1 == p2

    let objptr2 = cast[type(objptr)](p2)
    doAssert objptr == objptr2

    p1 = cast[pointer](xptr)
    p2 = cast[pointer](xptr)
    doAssert p1 == p2

    let xptr2 = cast[type(xptr)](p2)
    doAssert xptr == xptr2
  
  block: # var types
    block t10202:
      type Point = object
        x: float
        y: float

      var points: seq[Point]

      points.add(Point(x:1, y:2))

      for i, p in points.mpairs:
        p.x += 1

      doAssert points[0].x == 2
    
    block:
      var ints = @[1, 2, 3]
      for i, val in mpairs ints:
        val *= 10
      doAssert ints == @[10, 20, 30]
    
    block:
      var seqOfSeqs = @[@[1, 2], @[3, 4]]
      for i, val in mpairs seqOfSeqs:
        val[0] *= 10
      doAssert seqOfSeqs == @[@[10, 2], @[30, 4]]

  when false:
    block: # openarray
          # Error: internal error: genAddr: nkStmtListExpr
      var calls = 0
      proc getvarint(x: var openarray[int]): var int =
        calls += 1
        if true:
          x[1]
        else:
          x[0]

      var arr = [1, 2, 3]
      getvarint(arr) += 5
      doAssert calls == 1
      doAssert arr[1] == 7

proc tests_in_proc =
  tests

# since pointers are handled differently in global/local contexts
# let's just run all of them twice
tests_in_proc()
tests
