Here is a different proposal: We make `.writes` an effect as originally 
anticipated by the write tracking algorithm. However, instead of basing this 
analysis on parameters, we base it on _object fields_.

In the following examples I explicitly wrote the `.writes` effects that 
otherwise would have been inferred:
    
    
    type
      Node = ref object
         data: string
         le, ri: Node
    
    proc m(n: Node) {.writes: [Node.data].} =
      n.data = "xyz"
    
    proc depth(n: Node): int {.writes: [].} =
      (if n == nil: 0 else: max(depth(n.le), depth(n.ri)) + 1)
    
    proc find(n: Node; key: string): Node {.writes: [].} =
      var it = n # you can navigate through the tree iteratively just fine
      while it != nil:
        if it.data == key: return n
        if it.data < key: it = it.le
        else: it = it.ri
    
    proc select(a, b: Node): Node {.writes: [].} =
      result = if oracle(): a else: b
    
    proc construct(a, b: Node): Node {.writes: [].} =
      result = Node(data: "new", le: a, ri: b)
    
    proc harmless(a, b: Node) {.writes: [Node.data].} =
      var x = construct(a, b)
      x.data = "mutated"
    
    proc harmful(a, b: Node) {.writes: [Node.data].} =
      var x = select(a, b)
      x.data = "mutated"
    
    
    Run

Since it's based on object fields and not on parameters the aliasing problems 
do not apply, but it's a more conservative analysis as `harmless` also has the 
`{.writes: [Node.data].}` effect. However, there is no guessing involved and at 
least for the compiler it might work out much better. For example in Nim's 
backend we often set `PSym.loc` as the computed mangled name and location, but 
we don't update other parts of `PSym`.

Also, the most important effect, "writes nothing" is unaffected by the switch 
to object fields.

Reply via email to