Hi I am trying to make the following short snippet compile without crashing or 
leaking memory with `--gc:arc`.
    
    
    import macros
    
    type
       Edge = object
          neighbor: Node
       
       Node = ref NodeObj
       NodeObj = object
          neighbors: seq[Edge]
          label: string
          visited: bool
       
       Graph = object
          nodes: seq[Node]
    
    proc `=destroy`(e: var Edge) =
       if e.neighbor != nil:
          echo "ref ", e.neighbor.label
          dispose(e.neighbor)
       else: echo "nil ref"
    proc `=`(dest: var Edge; source: Edge) =
       if dest.neighbor != source.neighbor:
          `=destroy`(dest)
          dest = source
    
    proc `=destroy`(n: var NodeObj) =
       echo "destroy ", n.label
       `=destroy`(n.neighbors)
       `=destroy`(n.label)
    proc `=`(dest: var NodeObj; source: NodeObj) = # never called
       echo "copying ", dest.label
    
    # proc `=destroy`(g: var Graph) =
    #    for i in 0 ..< g.nodes.len:
    #       echo "destroying ", g.nodes[i].label
    #       `=destroy`(g.nodes[i])
    
    proc addNode(self: var Graph; label: string): lent Node =
       let node = Node(label: label)
       self.nodes.add(node)
       result = self.nodes[^1]
    
    proc addEdge(self: Graph; source, neighbor: Node) {.nodestroy.} =
       let edge = Edge(neighbor: neighbor)
       source.neighbors.add(edge)
    
    macro operateOn*(x: typed; calls: untyped) =
       result = copyNimNode(calls)
       expectKind calls, {nnkStmtList, nnkStmtListExpr}
       # non-recursive processing because that's exactly what we need here:
       for y in calls:
          expectKind y, nnkCallKinds
          var call = newTree(y.kind)
          call.add y[0]
          call.add x
          for j in 1 ..< y.len: call.add y[j]
          result.add call
    
    proc main =
       var graph: Graph
       
       let nodeA {.cursor.} = graph.addNode("a")
       let nodeB {.cursor.} = graph.addNode("b")
       let nodeC {.cursor.} = graph.addNode("c")
       let nodeD {.cursor.} = graph.addNode("d")
       let nodeE {.cursor.} = graph.addNode("e")
       let nodeF {.cursor.} = graph.addNode("f")
       let nodeG {.cursor.} = graph.addNode("g")
       let nodeH {.cursor.} = graph.addNode("h")
       
       operateOn(graph):
          addEdge(nodeA, neighbor = nodeB)
          addEdge(nodeA, neighbor = nodeC)
          addEdge(nodeB, neighbor = nodeD)
          addEdge(nodeB, neighbor = nodeE)
          addEdge(nodeC, neighbor = nodeF)
          addEdge(nodeC, neighbor = nodeG)
          addEdge(nodeE, neighbor = nodeH)
          addEdge(nodeE, neighbor = nodeF)
          addEdge(nodeF, neighbor = nodeG)
    #       addEdge(nodeF, neighbor = nodeF) # cycle
    #       addEdge(nodeG, neighbor = nodeF) # more cycles
       
       for n in graph.nodes:
          echo n.label
    
    main()
    echo getAllocStats() # does it always reports (0, 0) !?
    assert getOccupiedMem() == 0
    
    
    Run

The output I get is: 
    
    
    nil ref # lots of these
    ...
    a
    b
    c
    d
    e
    f
    g
    h
    destroy a
    ref b
    ref c
    destroy b
    ref d
    ref e
    destroy c
    ref f
    ref g
    SIGSEGV: Illegal storage access. (Attempt to read from nil?)
    
    
    Run

I have tried many things (including the `acyclic` pragma) but I can't seem to 
make it work without leaking. However I'm not sure if there 's a bug hiding or 
I'm simply doing it wrong, any suggestions?

Reply via email to