summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/vm.nim27
-rw-r--r--compiler/vmdef.nim2
-rw-r--r--compiler/vmgen.nim22
-rw-r--r--tests/vm/taddrof.nim110
-rw-r--r--tests/vm/tanonproc.nim2
-rw-r--r--tests/vm/tmitems_vm.nim4
6 files changed, 156 insertions, 11 deletions
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 52faeaf38..ce473c12a 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -606,6 +606,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node = src.sons[idx]
       else:
         stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1))
+    of opcLdArrAddr:
+      # a = addr(b[c])
+      decodeBC(rkNodeAddr)
+      if regs[rc].intVal > high(int):
+        stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
+      let idx = regs[rc].intVal.int
+      let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
+      if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len:
+        regs[ra].nodeAddr = addr src.sons[idx]
+      else:
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1))
     of opcLdStrIdx:
       decodeBC(rkInt)
       let idx = regs[rc].intVal.int
@@ -643,9 +654,25 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       else:
         let n = src.sons[rc]
         regs[ra].node = n
+    of opcLdObjAddr:
+      # a = addr(b.c)
+      decodeBC(rkNodeAddr)
+      let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
+      case src.kind
+      of nkEmpty..nkNilLit:
+        stackTrace(c, tos, pc, errNilAccess)
+      of nkObjConstr:
+        let n = src.sons[rc + 1]
+        if n.kind == nkExprColonExpr:
+          regs[ra].nodeAddr = addr n.sons[1]
+        else:
+          regs[ra].nodeAddr = addr src.sons[rc + 1]
+      else:
+        regs[ra].nodeAddr = addr src.sons[rc]
     of opcWrObj:
       # a.b = c
       decodeBC(rkNode)
+      assert regs[ra].node != nil
       let shiftedRb = rb + ord(regs[ra].node.kind == nkObjConstr)
       let dest = regs[ra].node
       if dest.kind == nkNilLit:
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 54c18b03a..06e309678 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -42,8 +42,10 @@ type
     opcNodeToReg,
 
     opcLdArr,  # a = b[c]
+    opcLdArrAddr, # a = addr(b[c])
     opcWrArr,  # a[b] = c
     opcLdObj,  # a = b.c
+    opcLdObjAddr, # a = addr(b.c)
     opcWrObj,  # a.b = c
     opcAddrReg,
     opcAddrNode,
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index cd66e3301..668c496c6 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1417,12 +1417,11 @@ proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
     gen(c, m, dest, flags)
     return
 
-  let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfNode}
-           else: {gfNodeAddr}
-  let newflags = flags-{gfNode, gfNodeAddr}+af
+  let newflags = flags-{gfNode}+{gfNodeAddr}
 
-  if isGlobal(n.sons[0]):
-    gen(c, n.sons[0], dest, flags+af)
+  if isGlobal(n.sons[0]) or n[0].kind in {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}:
+    # checking for this pattern:  addr(obj.field) / addr(array[i])
+    gen(c, n.sons[0], dest, newflags)
   else:
     let tmp = c.genx(n.sons[0], newflags)
     if dest < 0: dest = c.getTemp(n.typ)
@@ -1663,7 +1662,9 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
   let a = c.genx(n.sons[0], flags)
   let b = c.genIndex(n.sons[1], n.sons[0].typ)
   if dest < 0: dest = c.getTemp(n.typ)
-  if needsRegLoad():
+  if opc == opcLdArr and {gfNodeAddr} * flags != {}:
+    c.gABC(n, opcLdArrAddr, dest, a, b)
+  elif needsRegLoad():
     var cc = c.getTemp(n.typ)
     c.gABC(n, opc, cc, a, b)
     c.gABC(n, opcNodeToReg, dest, cc)
@@ -1679,7 +1680,9 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   let a = c.genx(n.sons[0], flags)
   let b = genField(c, n.sons[1])
   if dest < 0: dest = c.getTemp(n.typ)
-  if needsRegLoad():
+  if {gfNodeAddr} * flags != {}:
+    c.gABC(n, opcLdObjAddr, dest, a, b)
+  elif needsRegLoad():
     var cc = c.getTemp(n.typ)
     c.gABC(n, opcLdObj, cc, a, b)
     c.gABC(n, opcNodeToReg, dest, cc)
@@ -1733,7 +1736,10 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   # Load the content now
   if dest < 0: dest = c.getTemp(n.typ)
   let fieldPos = genField(c, field)
-  if needsRegLoad():
+
+  if {gfNodeAddr} * flags != {}:
+    c.gABC(n, opcLdObjAddr, dest, objR, fieldPos)
+  elif needsRegLoad():
     var cc = c.getTemp(accessExpr.typ)
     c.gABC(n, opcLdObj, cc, objR, fieldPos)
     c.gABC(n, opcNodeToReg, dest, cc)
diff --git a/tests/vm/taddrof.nim b/tests/vm/taddrof.nim
new file mode 100644
index 000000000..bbe9345d2
--- /dev/null
+++ b/tests/vm/taddrof.nim
@@ -0,0 +1,110 @@
+discard """
+nimout: '''
+true
+true
+[nil, nil, nil, nil]
+[MyObjectRef(123, 321), nil, nil, nil]
+['A', '\x00', '\x00', '\x00']
+MyObjectRef(123, 321)
+(key: 8, val: 0)
+'''
+output: '''
+true
+true
+[nil, nil, nil, nil]
+[MyObjectRef(123, 321), nil, nil, nil]
+['A', '\x00', '\x00', '\x00']
+MyObjectRef(123, 321)
+'''
+"""
+
+type
+  MyObjectRef = ref object
+    a,b: int
+
+  MyContainerObject = ref object
+    member: MyObjectRef
+
+  MySuperContainerObject = ref object
+    member: MyContainerObject
+    arr: array[4, MyObjectRef]
+
+  MyOtherObject = ref object
+    case kind: bool
+    of true:
+      member: MyObjectRef
+    else:
+      discard
+
+proc `$`(arg: MyObjectRef): string =
+  result = "MyObjectRef("
+  result.addInt arg.a
+  result.add ", "
+  result.addInt arg.b
+  result.add ")"
+
+proc foobar(dst: var MyObjectRef) =
+  dst = new(MyObjectRef)
+  dst.a = 123
+  dst.b = 321
+
+proc changeChar(c: var char) =
+  c = 'A'
+
+proc test() =
+  # when it comes from a var, it works
+  var y: MyObjectRef
+  foobar(y)
+  echo y != nil
+  # when it comes from a member, it fails on VM
+  var x = new(MyContainerObject)
+  foobar(x.member)
+  echo x.member != nil
+
+  # when it comes from an array, it fails on VM
+  var arr: array[4, MyObjectRef]
+  echo arr
+  foobar(arr[0])
+  echo arr
+
+  var arr2: array[4, char]
+  changeChar(arr2[0])
+  echo arr2
+
+
+  var z = MyOtherObject(kind: true)
+  foobar(z.member)
+  echo z.member
+
+  # this still doesn't work
+  # var sc = new(MySuperContainerObject)
+  # sc.member = new(MyContainerObject)
+  # foobar(sc.member.member)
+  # echo sc.member.member
+  # foobar(sc.arr[1])
+  # echo sc.arr
+
+  #var str = "---"
+  #changeChar(str[1])
+  #echo str
+
+test()
+static:
+  test()
+
+type T = object
+  f: seq[tuple[key, val: int]]
+
+proc foo(s: var seq[tuple[key, val: int]]; i: int) =
+  s[i].key = 4*i
+  # r4 = addr(s[i])
+  # r4[0] = 4*i
+
+proc bar() =
+  var s: T
+  s.f = newSeq[tuple[key, val: int]](3)
+  foo(s.f, 2)
+  echo s.f[2]
+
+static:
+  bar()
diff --git a/tests/vm/tanonproc.nim b/tests/vm/tanonproc.nim
index a89b53e83..1176c104e 100644
--- a/tests/vm/tanonproc.nim
+++ b/tests/vm/tanonproc.nim
@@ -42,7 +42,7 @@ proc getOrElse[T](o: Option[T], def: T): T =
 proc quoteStr(s: string): Option[string] =
   s.some.notEmpty.map(v => "`" & v & "`")
 
-macro str(s: string): typed =
+macro str(s: string): void =
   let x = s.strVal
   let y = quoteStr(x)
   let sn = newStrLitNode(y.getOrElse("NONE"))
diff --git a/tests/vm/tmitems_vm.nim b/tests/vm/tmitems_vm.nim
index 87835d1cd..787d52cc6 100644
--- a/tests/vm/tmitems_vm.nim
+++ b/tests/vm/tmitems_vm.nim
@@ -7,7 +7,7 @@ discard """
 # bug #3731
 var list {.compileTime.} = newSeq[int]()
 
-macro calc*(): typed =
+macro calc*(): void =
   list.add(1)
   for c in list.mitems:
     c = 13
@@ -19,7 +19,7 @@ calc()
 
 # bug #3859
 import macros
-macro m: typed =
+macro m: void =
   var s = newseq[NimNode](3)
   # var s: array[3,NimNode]                 # not working either
   for i in 0..<s.len: s[i] = newLit(3)    # works