I have been writing some helper macros to assist in the coding of destructors, 
and I ran into the following problem.
    
    
    import macros
    
    macro dumpCompiledCode(compiledCode: typed): untyped =
      ## Simple macro to dump the final generated source code form of the 
argument,
      ## after all nested macros have been called, template code has been 
inserted, etc.
      echo "\n#### final generated code:"
      echo repr(compiledCode)
      # Return what was passed in so that compilation can continue
      result =  compiledCode
    
    macro refObjectDestructor(typeName: typed, bodyCode: untyped): untyped =
      ## Macro to construct a destructor proc definition for a ref object type
      ## The generated proc definition looks something like:
      ##    proc `=destroy`(x: typeof(<typeName>()[])) =
      ##      <bodyCode>
      assert typeName.kind == nnkSym
      assert typeName.symKind == nskType
      
      assert bodyCode.kind == nnkStmtList
      
      # destructor proc name definition ("`=destroy`")
      var procNameNode = newNimNode(nnkAccQuoted)
      procNameNode.add(newIdentNode("="))
      procNameNode.add(newIdentNode("destroy"))
      
      # destructor proc return type & parameters
      var paramNodes = newSeq[NimNode]()
      # Return type - none
      let returnTypeNode = newEmptyNode()
      paramNodes.add(returnTypeNode)
      # First (and only) parameter definition
      var param1Node = newNimNode(nnkIdentDefs)
      param1Node.add(newIdentNode("x"))     # parameter name
      
      # Need to build the AST for the parameter type ("typeof <typeName>()[]")
      # Make an instance of the ref object: "<typeName>()"
      let refInstantiationNode = newCall(newIdentNode(typeName.strval))
      # Dereference the ref instance to get the object: "<typeName>()[]"
      var derefedObjectNode = newNimnode(nnkBracketExpr)
      derefedObjectNode.add(refInstantiationNode)
      # Get the (anonymous) type of the object: "typeof(<typeName>()[])"
      let objectTypeNode = newCall("typeof", derefedObjectNode)
      # Now add the type to the parameter definition
      param1Node.add(objectTypeNode)   # parameter type
      
      param1Node.add(newEmptyNode())
      # Add the parameter definition to the proc parameters node
      paramNodes.add(param1Node)
      
      # Finally, generate the proc definition
      result = newProc(procNameNode, paramNodes, bodyCode)
    
    when isMainModule:
      type
        TestRef = ref object of RootRef
          name: string
        
        Marker = ref object
          tref: TestRef
      
      proc `=destroy`(x: typeof(TestRef()[])) =
        `=destroy`(x.name)
      
      # The compiler crashes after the final generated code has been printed
      # If *either* of the noted changes is made, then compilation succeeds
      dumpCompiledCode: refObjectDestructor(Marker):  # (1) Compiles okay if 
"dumpCompiledCode: " is removed
        `=destroy`(x.tref)    # (2) Compiles okay if a defererence is added to 
the argument, i.e. "x.tref[]"
      
      static: echo "##### Done"
    
    
    Run

When I compile the above code the compiler crashes - _after_ the 
`dumpCompiledCode` macro has printed the generated code (which looks okay). The 
output is:
    
    
    Hint: used config file '/opt/nim-2.0.4/config/nim.cfg' [Conf]
    Hint: used config file '/opt/nim-2.0.4/config/config.nims' [Conf]
    ........................................................................
    
    #### final generated code:
    
    proc `=destroy`(x: typeof(Marker()[])) {.raises: [].} =
      `=destroy`(x.tref)
    
    SIGSEGV: Illegal storage access. (Attempt to read from nil?)
    Segmentation fault (core dumped)
    
    
    Run

Applying code change (1) above causes the compilation to succeed, but then of 
course I don't get to see the generated code.

Applying code change (2) also causes the compilation to succeed, and the 
generated code is printed. But I strongly suspect that the object's reference 
count would not be decremented by the destructor call - negating the whole 
point of the call.

If the the field `x.tref` is replaced by a non-ref entity (`object`, `string`, 
`seq`, even a `seq` of `ref`) this issue does not occur.

So what's going on? And how do I get past this? 

Reply via email to