summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorOscar Nihlgård <oscarnihlgard@gmail.com>2018-06-26 21:18:55 +0200
committerOscar Nihlgård <oscarnihlgard@gmail.com>2018-06-26 21:25:27 +0200
commit236bc06b5f1a1a1fc4a711007aed0b60b6656a4f (patch)
tree1a80153e55305dd23e8e01cda36971d12f4fac63
parentd08b9eb6731a70504be6d856723fbc94dc7bd506 (diff)
downloadNim-236bc06b5f1a1a1fc4a711007aed0b60b6656a4f.tar.gz
Improve vm support for ref types
-rw-r--r--compiler/vm.nim47
-rw-r--r--compiler/vmdef.nim1
-rw-r--r--tests/vm/tnilref.nim7
-rw-r--r--tests/vm/tref.nim47
4 files changed, 79 insertions, 23 deletions
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 3e33e8256..b16eb0fd4 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -204,22 +204,14 @@ proc asgnComplex(x: var TFullReg, y: TFullReg) =
   of rkRegisterAddr: x.regAddr = y.regAddr
   of rkNodeAddr: x.nodeAddr = y.nodeAddr
 
-proc putIntoNode(n: var PNode; x: TFullReg) =
+proc writeField(n: var PNode, x: TFullReg) =
   case x.kind
   of rkNone: discard
   of rkInt: n.intVal = x.intVal
   of rkFloat: n.floatVal = x.floatVal
-  of rkNode:
-    if nfIsRef in x.node.flags:
-      n = x.node
-    else:
-      let destIsRef = nfIsRef in n.flags
-      n[] = x.node[]
-      # Ref-ness must be kept for the destination
-      if destIsRef:
-        n.flags.incl nfIsRef
-  of rkRegisterAddr: putIntoNode(n, x.regAddr[])
-  of rkNodeAddr: n[] = x.nodeAddr[][]
+  of rkNode: n = x.node
+  of rkRegisterAddr: writeField(n, x.regAddr[])
+  of rkNodeAddr: n = x.nodeAddr[]
 
 proc putIntoReg(dest: var TFullReg; n: PNode) =
   case n.kind
@@ -498,9 +490,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       asgnComplex(regs[ra], regs[instr.regB])
     of opcAsgnRef:
       asgnRef(regs[ra], regs[instr.regB])
-    of opcRegToNode:
-      decodeB(rkNode)
-      putIntoNode(regs[ra].node, regs[rb])
     of opcNodeToReg:
       let ra = instr.regA
       let rb = instr.regB
@@ -559,7 +548,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         else:
           stackTrace(c, tos, pc, errIndexOutOfBounds)
       elif idx <% arr.len:
-        putIntoNode(arr.sons[idx], regs[rc])
+        writeField(arr.sons[idx], regs[rc])
       else:
         stackTrace(c, tos, pc, errIndexOutOfBounds)
     of opcLdObj:
@@ -579,9 +568,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if dest.kind == nkNilLit:
         stackTrace(c, tos, pc, errNilAccess)
       elif dest.sons[shiftedRb].kind == nkExprColonExpr:
-        putIntoNode(dest.sons[shiftedRb].sons[1], regs[rc])
+        writeField(dest.sons[shiftedRb].sons[1], regs[rc])
       else:
-        putIntoNode(dest.sons[shiftedRb], regs[rc])
+        writeField(dest.sons[shiftedRb], regs[rc])
     of opcWrStrIdx:
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
@@ -624,9 +613,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let ra = instr.regA
       let rc = instr.regC
       case regs[ra].kind
-      of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc])
+      of rkNodeAddr:
+        # XXX: Workaround for vmgen bug:
+        let n = regs[rc].regToNode
+        if (nfIsRef in regs[ra].nodeAddr[].flags or
+            regs[ra].nodeAddr[].kind == nkNilLit) and nfIsRef notin n.flags:
+          if regs[ra].nodeAddr[].kind == nkNilLit:
+            stackTrace(c, tos, pc, errNilAccess)
+          regs[ra].nodeAddr[][] = n[]
+          regs[ra].nodeAddr[].flags.incl nfIsRef
+        else:
+          regs[ra].nodeAddr[] = n
       of rkRegisterAddr: regs[ra].regAddr[] = regs[rc]
-      of rkNode: putIntoNode(regs[ra].node, regs[rc])
+      of rkNode:
+        if regs[ra].node.kind == nkNilLit:
+          stackTrace(c, tos, pc, errNilAccess)
+        assert nfIsRef in regs[ra].node.flags
+        regs[ra].node[] = regs[rc].regToNode[]
+        regs[ra].node.flags.incl nfIsRef
       else: stackTrace(c, tos, pc, errNilAccess)
     of opcAddInt:
       decodeBC(rkInt)
@@ -1211,6 +1215,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNBindSym:
       decodeBx(rkNode)
       regs[ra].node = copyTree(c.constants.sons[rbx])
+      regs[ra].node.flags.incl nfIsRef
     of opcNChild:
       decodeBC(rkNode)
       let idx = regs[rc].intVal.int
@@ -1572,7 +1577,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc])
       incl(sym.flags, sfGenSym)
       regs[ra].node = newSymNode(sym)
-
+      regs[ra].node.flags.incl nfIsRef
     of opcNccValue:
       decodeB(rkInt)
       let destKey = regs[rb].node.strVal
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index cec61ade5..f7466b392 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -36,7 +36,6 @@ type
     opcAsgnFloat,
     opcAsgnRef,
     opcAsgnComplex,
-    opcRegToNode,
     opcNodeToReg,
 
     opcLdArr,  # a = b[c]
diff --git a/tests/vm/tnilref.nim b/tests/vm/tnilref.nim
new file mode 100644
index 000000000..5e27cf0cb
--- /dev/null
+++ b/tests/vm/tnilref.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "attempt to access a nil address"
+"""
+
+static:
+    var s: ref int
+    s[] = 1
\ No newline at end of file
diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim
index 517a67fb0..27b7bf313 100644
--- a/tests/vm/tref.nim
+++ b/tests/vm/tref.nim
@@ -9,4 +9,49 @@ static:
 
   b[5] = 'c'
   doAssert a[] == "Hellocworld"
-  doAssert b[] == "Hellocworld"
\ No newline at end of file
+  doAssert b[] == "Hellocworld"
+
+  proc notGlobal() =
+    var
+      a: ref string
+      b: ref string
+    new a
+
+    a[] = "Hello world"
+    b = a
+
+    b[5] = 'c'
+    doAssert a[] == "Hellocworld"
+    doAssert b[] == "Hellocworld"
+  notGlobal()
+
+static: # bug 6081
+  block:
+    type Obj = object
+      field: ref int
+    var i: ref int
+    new(i)
+    var r = Obj(field: i)
+    var rr = r
+    r.field = nil
+    doAssert rr.field != nil
+
+  proc foo() = # Proc to avoid special global logic
+    var s: seq[ref int]
+    var i: ref int
+    new(i)
+    s.add(i)
+    var head = s[0]
+    s[0] = nil
+    doAssert head != nil
+
+  foo()
+
+static:
+
+  block: # global alias
+    var s: ref int
+    new(s)
+    var ss = s
+    s[] = 1
+    doAssert ss[] == 1
\ No newline at end of file