summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorParashurama <Rhagdamaziel@ymail.com>2017-09-02 22:53:22 +0200
committerAndreas Rumpf <rumpf_a@web.de>2017-09-02 22:53:22 +0200
commit0861249de75adabc5d9761c77f7f057644014fd2 (patch)
tree314d6863b0c8fa50afd83b30ff1d6ccf6c3abf89
parent6ce6883fad6a31f92f71fe271c3b2c82a17d0562 (diff)
downloadNim-0861249de75adabc5d9761c77f7f057644014fd2.tar.gz
Fix seq.setLen initialisation in VM (#6224)
-rw-r--r--compiler/ccgexprs.nim9
-rw-r--r--compiler/vm.nim31
-rw-r--r--tests/vm/tseq_badinit.nim58
3 files changed, 90 insertions, 8 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 74f8f90cd..926f30985 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -2315,6 +2315,15 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
     var t = skipTypes(n.typ, abstractInst)
     if t.kind == tySequence:
       result = genConstSeq(p, n, n.typ)
+    elif t.kind == tyProc and t.callConv == ccClosure and not n.sons.isNil and
+         n.sons[0].kind == nkNilLit and n.sons[1].kind == nkNilLit:
+      # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL}
+      # this behaviour is needed since closure_var = nil must be
+      # expanded to {NIM_NIL,NIM_NIL}
+      # in VM closures are initialized with nkPar(nkNilLit, nkNilLit)
+      # leading to duplicate code like this:
+      # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}"
+      result = ~"{NIM_NIL,NIM_NIL}"
     else:
       result = genConstSimpleList(p, n)
   of nkObjConstr:
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 2dd6d6a9c..93cf66c05 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -409,6 +409,28 @@ proc recSetFlagIsRef(arg: PNode) =
   for i in 0 ..< arg.safeLen:
     arg.sons[i].recSetFlagIsRef
 
+proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
+  # FIXME: this doesn't attempt to solve incomplete
+  # support of tyPtr, tyRef in VM.
+  let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
+  let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
+  let typeKind = case typeEntry.kind
+  of tyUInt..tyUInt64: nkUIntLit
+  of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit
+  of tyFloat..tyFloat128: nkFloatLit
+  of tyString: nkStrLit
+  of tyObject: nkObjConstr
+  of tySequence: nkNilLit
+  of tyProc, tyTuple: nkPar
+  else: nkEmpty
+
+  let oldLen = node.len
+  setLen(node.sons, newLen)
+  if oldLen < newLen:
+    # TODO: This is still not correct for tyPtr, tyRef default value
+    for i in oldLen .. <newLen:
+      node.sons[i] = newNodeI(typeKind, info)
+
 proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
   var pc = start
   var tos = tos
@@ -1118,14 +1140,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       let newLen = regs[rb].intVal.int
       if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
-      else:
-        let oldLen = regs[ra].node.len
-        setLen(regs[ra].node.sons, newLen)
-        if oldLen < newLen:
-          # XXX This is still not entirely correct
-          # set to default value:
-          for i in oldLen .. <newLen:
-            regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc])
+      else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
     of opcReset:
       internalError(c.debug[pc], "too implement")
     of opcNarrowS:
diff --git a/tests/vm/tseq_badinit.nim b/tests/vm/tseq_badinit.nim
new file mode 100644
index 000000000..15889d60e
--- /dev/null
+++ b/tests/vm/tseq_badinit.nim
@@ -0,0 +1,58 @@
+
+type
+  AObj = object
+    i: int
+    d: float
+  ATup = tuple
+    i: int
+    d: float
+  MyEnum = enum
+    E01, E02, E03
+  Myrange = range[0..10]
+
+  MyProc = proc (x: int): bool
+  MyInt = distinct int
+  MyAlias = MyInt
+  MySet = set[char]
+  MyArray = array[4, char]
+  MySeq = seq[string]
+
+template test(typename, default: untyped) =
+  proc `abc typename`(): seq[typename] =
+    result = newSeq[typename]()
+    result.add(default)
+    result.setLen(3)
+    for i in 0 .. <2:
+      result[i] = default
+
+  const constval = `abc typename`()
+  doAssert(constval == `abc typename`())
+
+  proc `arr typename`(): array[4, typename] =
+    for i in 0 .. <2:
+      result[i] = default
+  const constarr = `arr typename`()
+  doAssert(constarr == `arr typename`())
+
+proc even(x: int): bool = x mod 2 == 0
+proc `==`(x, y: MyInt): bool = ord(x) == ord(y)
+proc `$`(x: MyInt): string = $ord(x)
+proc `$`(x: proc): string =
+  if x.isNil: "(nil)" else: "funcptr"
+
+test(int, 0)
+test(uint, 0)
+test(float, 0.1)
+test(char, '0')
+test(bool, false)
+test(uint8, 2)
+test(string, "data")
+test(MyProc, even)
+test(MyEnum, E02)
+test(AObj, AObj())
+test(ATup, (i:11, d:9.99))
+test(Myrange, 4)
+test(MyInt, MyInt(4))
+test(MyAlias, MyAlias(4))
+test(MyArray, ['0','1','2','3'])
+test(MySeq, @["data"])