discard """
  valgrind: true
  cmd: "nim c --gc:arc -d:useMalloc $file"
  output: '''myobj destroyed
myobj destroyed
myobj destroyed
A
B
begin
end
prevented
(ok: true, value: "ok")
@[(kind: P, pChildren: @[])]
myobj destroyed
'''
"""

# bug #13102

type
  D = ref object
  R = object
    case o: bool
    of false:
      discard
    of true:
      field: D

iterator things(): R =
  when true:
    var
      unit = D()
    while true:
      yield R(o: true, field: unit)
  else:
    while true:
      var
        unit = D()
      yield R(o: true, field: unit)

proc main =
  var i = 0
  for item in things():
    discard item.field
    inc i
    if i == 2: break

main()

# bug #13149

type
  TMyObj = object
    p: pointer
    len: int

proc `=destroy`(o: var TMyObj) =
  if o.p != nil:
    dealloc o.p
    o.p = nil
    echo "myobj destroyed"

proc `=copy`(dst: var TMyObj, src: TMyObj) =
  `=destroy`(dst)
  dst.p = alloc(src.len)
  dst.len = src.len

proc `=sink`(dst: var TMyObj, src: TMyObj) =
  `=destroy`(dst)
  dst.p = src.p
  dst.len = src.len

type
  TObjKind = enum Z, A, B
  TCaseObj = object
    case kind: TObjKind
    of Z: discard
    of A:
      x1: int # this int plays important role
      x2: TMyObj
    of B:
      y: TMyObj

proc testSinks: TCaseObj =
  result = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
  result = TCaseObj(kind: B, y: TMyObj(len: 3, p: alloc(3)))

proc use(x: TCaseObj) = discard

proc testCopies(i: int) =
  var a: array[2, TCaseObj]
  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
  a[i+1] = a[i] # copy, cannot move
  use(a[i])

let x1 = testSinks()
testCopies(0)

# bug #12957

type
  PegKind* = enum
    pkCharChoice,
    pkSequence
  Peg* = object ## type that represents a PEG
    case kind: PegKind
    of pkCharChoice: charChoice: ref set[char]
    else: discard
    sons: seq[Peg]

proc charSet*(s: set[char]): Peg =
  ## constructs a PEG from a character set `s`
  result = Peg(kind: pkCharChoice)
  new(result.charChoice)
  result.charChoice[] = s

proc len(a: Peg): int {.inline.} = return a.sons.len
proc myadd(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)

proc sequence*(a: openArray[Peg]): Peg =
  result = Peg(kind: pkSequence, sons: @[])
  when false:
    #works too:
    result.myadd(a[0])
    result.myadd(a[1])
  for x in items(a):
    # works:
    #result.sons.add(x)
    # fails:
    result.myadd x
  if result.len == 1:
    result = result.sons[0] # this must not move!

when true:
  # bug #12957

  proc p =
    echo "A"
    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'}),
              charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})])
    echo "B"
  p()

  proc testSubObjAssignment =
    echo "begin"
    # There must be extactly one element in the array constructor!
    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})])
    echo "end"
  testSubObjAssignment()


#------------------------------------------------

type
  MyObject = object
    x1: string
    case kind1: bool
      of false: y1: string
      of true:
          y2: seq[string]
          case kind2: bool
              of true: z1: string
              of false:
                z2: seq[string]
                flag: bool
    x2: string

proc test_myobject =
  var x: MyObject = MyObject()
  x.x1 = "x1"
  x.x2 = "x2"
  x.y1 = "ljhkjhkjh"
  {.cast(uncheckedAssign).}:
    x.kind1 = true
  x.y2 = @["1", "2"]
  {.cast(uncheckedAssign).}:
    x.kind2 = true
  x.z1 = "yes"
  {.cast(uncheckedAssign).}:
    x.kind2 = false
  x.z2 = @["1", "2"]
  {.cast(uncheckedAssign).}:
    x.kind2 = true
  x.z1 = "yes"
  x.kind2 = true # should be no effect
  doAssert(x.z1 == "yes")
  {.cast(uncheckedAssign).}:
    x.kind2 = false
  {.cast(uncheckedAssign).}:
    x.kind1 = x.kind2 # support self assignment with effect

  try:
    x.kind1 = x.flag # flag is not accesible
  except FieldDefect:
    echo "prevented"

  doAssert(x.x1 == "x1")
  doAssert(x.x2 == "x2")


test_myobject()


#------------------------------------------------
# bug #14244

type
  RocksDBResult*[T] = object
    case ok*: bool
    of true:
      value*: T
    else:
      error*: string

proc init(): RocksDBResult[string] =
  result = default(RocksDBResult[string])
  {.cast(uncheckedAssign).}:
    result.ok = true
    result.value = "ok"

echo init()


#------------------------------------------------
# bug #14312

type MyObj = object
  case kind: bool
    of false: x0: int # would work with a type like seq[int]; value would be reset
    of true: x1: string

var a = MyObj(kind: false, x0: 1234)
{.cast(uncheckedAssign).}:
  a.kind = true
doAssert(a.x1 == "")

block:
  # bug #15532
  type Kind = enum
    k0, k1

  type Foo = object
    y: int
    case kind: Kind
    of k0: x0: int
    of k1: x1: int

  const j0 = Foo(y: 1, kind: k0, x0: 2)
  const j1 = Foo(y: 1, kind: k1, x1: 2)

  doAssert j0.y == 1
  doAssert j0.kind == k0
  doAssert j1.kind == k1

  doAssert j1.x1 == 2
  doAssert j0.x0 == 2

# ------------------------------------
# bug #20305

type
  ContentNodeKind = enum
    P, Br, Text
  ContentNode = object
    case kind: ContentNodeKind
    of P: pChildren: seq[ContentNode]
    of Br: discard
    of Text: textStr: string

proc bug20305 =
  var x = ContentNode(kind: P, pChildren: @[
    ContentNode(kind: P, pChildren: @[ContentNode(kind: Text, textStr: "brrr")])
  ])
  x.pChildren.add ContentNode(kind: Br)
  x.pChildren.del(0)
  {.cast(uncheckedAssign).}:
    x.pChildren[0].kind = P
  echo x.pChildren

bug20305()

# bug #21023
block:
  block:
    type
      MGErrorKind = enum
        mgeUnexpected, mgeNotFound

    type Foo = object
      kind: MGErrorKind
      ex: Exception

    type Boo = object
      a: seq[int]

    type
      Result2 = object
        case o: bool
        of false:
          e: Foo
        of true:
          v: Boo

    proc startSessionSync(): Result2 =
      return Result2(o: true)

    proc mainSync =
      let ff = startSessionSync()
      doAssert ff.o == true

    mainSync()

  block:
    type
      MGErrorKind = enum
        mgeUnexpected, mgeNotFound

    type Foo = object
      kind: MGErrorKind
      ex: Exception

    type Boo = object
      a: seq[int]

    type
      Result2 = object
        case o: bool
        of false:
          e: Foo
        of true:
          v: Boo
          s: int

    proc startSessionSync(): Result2 =
      return Result2(o: true, s: 12)

    proc mainSync =
      let ff = startSessionSync()
      doAssert ff.s == 12

    mainSync()

import std/sequtils

# bug #23690
type
  SomeObj* = object of RootObj

  Item* = object
    case kind*: 0..1
    of 0:
      a*: int
      b*: SomeObj
    of 1:
      c*: string

  ItemExt* = object
    a*: Item
    b*: string

proc do1(x: int): seq[(string, Item)] =
  result = @[("zero", Item(kind: 1, c: "first"))]

proc do2(x: int, e: ItemExt): seq[(string, ItemExt)] =
  do1(x).map(proc(v: (string, Item)): auto = (v[0], ItemExt(a: v[1], b: e.b)))

doAssert $do2(0, ItemExt(a: Item(kind: 1, c: "second"), b: "third")) == """@[("zero", (a: (kind: 1, c: "first"), b: "third"))]"""

block:
  type RangeCrash = object
    case x: range[0..7]
    of 0..2:
      a: string
    else:
      b: string

  var rangeCrash = RangeCrash()
  {.cast(uncheckedAssign).}:
    rangeCrash.x = 5

block:
  type Discrim = distinct uint8
  type DistinctCrash = object
    case x: Discrim
    of Discrim(0)..Discrim(2):
      a: string
    else:
      b: string

  var distinctCrash = DistinctCrash()
  {.cast(uncheckedAssign).}:
    distinctCrash.x = Discrim(5)
