summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md3
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/destroyer.nim62
-rw-r--r--compiler/renderer.nim5
-rw-r--r--compiler/rodutils.nim14
-rw-r--r--compiler/sigmatch.nim13
-rw-r--r--compiler/types.nim8
-rw-r--r--lib/impure/ssl.nim6
-rw-r--r--lib/pure/nativesockets.nim14
-rw-r--r--lib/pure/os.nim5
-rw-r--r--lib/pure/strutils.nim10
-rw-r--r--lib/system.nim45
-rw-r--r--lib/system/dyncalls.nim5
-rw-r--r--lib/system/endb.nim30
-rw-r--r--lib/system/excpt.nim8
-rw-r--r--lib/system/repr.nim19
-rw-r--r--lib/system/sysstr.nim20
-rw-r--r--lib/wrappers/openssl.nim18
-rw-r--r--tests/async/tnewasyncudp.nim6
-rw-r--r--tests/float/tfloat4.nim2
-rw-r--r--tests/gc/gctest.nim2
-rw-r--r--tests/misc/treadx.nim3
-rw-r--r--tests/system/toString.nim77
-rw-r--r--todo.txt1
24 files changed, 247 insertions, 130 deletions
diff --git a/changelog.md b/changelog.md
index 9f6a83a37..22d763f53 100644
--- a/changelog.md
+++ b/changelog.md
@@ -5,3 +5,6 @@
 - Removed basic2d/basic3d out of the stdlib and into Nimble packages.
   These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo``
   or another package.
+- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
+  arrays of char can! This means ``$`` for arrays can finally exist
+  in ``system.nim`` and do the right thing.
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 9863e90bb..02c31163a 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -108,3 +108,4 @@ proc initDefines*() =
   defineSymbol("nimHasCppDefine")
   defineSymbol("nimGenericInOutFlags")
   when false: defineSymbol("nimHasOpt")
+  defineSymbol("nimNoArrayToCstringConversion")
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index 2ccef1724..e7ff00bb9 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -39,6 +39,9 @@
 ## x = y where y is read only once
 ## is the same as:  move(x, y)
 ##
+## Actually the more general rule is: The *last* read of ``y``
+## can become a move if ``y`` is the result of a construction.
+##
 ## We also need to keep in mind here that the number of reads is
 ## control flow dependent:
 ## let x = foo()
@@ -183,10 +186,67 @@ when false:
     of nkVarSection, nkLetSection: collectVarSection(c, n)
     else: discard
 
+type
+  Con = object
+    owner: PSym
+    g: ControlFlowGraph
+    tmps: PType
+
+proc isHarmlessVar*(s: PSym; c: Con): bool =
+  # 's' is harmless if it used only once and its
+  # definition/usage are not split by any labels:
+  #
+  # let s = foo()
+  # while true:
+  #   a[i] = s
+  #
+  # produces:
+  #
+  # def s
+  # L1:
+  #   use s
+  # goto L1
+  #
+  # let s = foo()
+  # if cond:
+  #   a[i] = s
+  # else:
+  #   a[j] = s
+  #
+  # produces:
+  #
+  # def s
+  # fork L2
+  # use s
+  # goto L3
+  # L2:
+  # use s
+  # L3
+  #
+  # So this analysis is for now overly conservative, but correct.
+  discard
+
+template interestingSym(s: PSym): bool =
+  s.owner == owner and s.kind in InterestingSyms and hasDestructor(s.typ)
+
+proc p(n, parent: PNode; c: var Con) =
+  case n.kind
+  of nkVarSection, nkLetSection:
+    discard "transform; var x = y to  var x; x op y  where op is a move or copy"
+  of nkCallKinds:
+    if n.typ != nil and hasDestructor(n.typ):
+      discard "produce temp creation"
+  of nkAsgn, nkFastAsgn:
+    if n[0].kind == nkSym and interestingSym(n[0].sym):
+      discard "use move or assignment"
+  else:
+    for i in 0..<n.len:
+      p(n[i], n, c)
+
 proc injectDestructorCalls*(owner: PSym; n: PNode;
                             disableExceptions = false): PNode =
   when false:
     var c = Con(t: initTable[int, VarInfo](), owner: owner)
     collectData(c, n)
   var allTemps = createObj(owner, n.info)
-
+  let cfg = constructCfg(owner, n)
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index fba4dc9ea..4fbac45ab 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -1011,7 +1011,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkPrefix:
     gsub(g, n, 0)
     if n.len > 1:
-      if n[1].kind == nkPrefix:
+      let opr = if n[0].kind == nkIdent: n[0].ident
+                elif n[0].kind == nkSym: n[0].sym.name
+                else: nil
+      if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
         put(g, tkSpaces, Space)
       if n.sons[1].kind == nkInfix:
         put(g, tkParLe, "(")
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index 77f7c844f..0456e9349 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -10,7 +10,7 @@
 ## Serialization utilities for the compiler.
 import strutils
 
-proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.}
+proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
 
 proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
   if f != f:
@@ -21,9 +21,14 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
     if f > 0.0: result = "INF"
     else: result = "-INF"
   else:
-    var buf: array[0..80, char]
-    c_sprintf(buf, "%#.16e" & literalPostfix, f)
-    result = $buf
+    when defined(nimNoArrayToCstringConversion):
+      result = newString(81)
+      let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring)
+      setLen(result, n)
+    else:
+      var buf: array[0..80, char]
+      discard c_snprintf(buf.cstring, buf.len.uint, "%#.16e%s", f, literalPostfix.cstring)
+      result = $buf.cstring
 
 proc encodeStr*(s: string, result: var string) =
   for i in countup(0, len(s) - 1):
@@ -133,4 +138,3 @@ iterator decodeStrArray*(s: cstring): string =
   while s[i] != '\0':
     yield decodeStr(s, i)
     if s[i] == ' ': inc i
-
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index f2bc24399..50d4178b6 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -1288,12 +1288,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     of tyString: result = isConvertible
     of tyPtr:
       # ptr[Tag, char] is not convertible to 'cstring' for now:
-      if a.len == 1 and a.sons[0].kind == tyChar: result = isConvertible
-    of tyArray:
-      if (firstOrd(a.sons[0]) == 0) and
-          (skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
-          (a.sons[1].kind == tyChar):
-        result = isConvertible
+      if a.len == 1:
+        let pointsTo = a.sons[0].skipTypes(abstractInst)
+        if pointsTo.kind == tyChar: result = isConvertible
+        elif pointsTo.kind == tyArray and firstOrd(pointsTo.sons[0]) == 0 and
+            skipTypes(pointsTo.sons[0], {tyRange}).kind in {tyInt..tyInt64} and
+            pointsTo.sons[1].kind == tyChar:
+          result = isConvertible
     else: discard
 
   of tyEmpty, tyVoid:
diff --git a/compiler/types.nim b/compiler/types.nim
index 3dbf6fd18..f32b0da18 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -667,14 +667,6 @@ proc lengthOrd*(t: PType): BiggestInt =
     else:
       result = lastOrd(t) - firstOrd(t) + 1
 
-proc isCompatibleToCString*(a: PType): bool =
-  if a.kind == tyArray:
-    if (firstOrd(a.sons[0]) == 0) and
-        (skipTypes(a.sons[0], {tyRange, tyGenericInst, tyAlias}).kind in
-            {tyInt..tyInt64, tyUInt..tyUInt64}) and
-        (a.sons[1].kind == tyChar):
-      result = true
-
 # -------------- type equality -----------------------------------------------
 
 type
diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim
index e3312d792..5b0e899f6 100644
--- a/lib/impure/ssl.nim
+++ b/lib/impure/ssl.nim
@@ -63,16 +63,16 @@ proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
   setLen(line.string, 0)
   while true:
     var c: array[0..0, char]
-    var n = BIO_read(sock.bio, c, c.len.cint)
+    var n = BIO_read(sock.bio, addr c, c.len.cint)
     if n <= 0: return false
     if c[0] == '\r':
-      n = BIO_read(sock.bio, c, c.len.cint)
+      n = BIO_read(sock.bio, addr c, c.len.cint)
       if n > 0 and c[0] == '\L':
         return true
       elif n <= 0:
         return false
     elif c[0] == '\L': return true
-    add(line.string, c)
+    add(line.string, c[0])
 
 
 proc send*(sock: SecureSocket, data: string) =
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 98fc62a3b..6c8701843 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -496,11 +496,12 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
                    addr(namelen)) == -1'i32:
       raiseOSError(osLastError())
     # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
-    var buf: array[64, char]
+    result[0] = newString(64)
     if inet_ntop(name.sin6_family.cint,
-                 addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
+                 addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
       raiseOSError(osLastError())
-    result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
+    setLen(result[0], result[0].cstring.len)
+    result[1] = Port(nativesockets.ntohs(name.sin6_port))
   else:
     raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
 
@@ -532,11 +533,12 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
                    addr(namelen)) == -1'i32:
       raiseOSError(osLastError())
     # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
-    var buf: array[64, char]
+    result[0] = newString(64)
     if inet_ntop(name.sin6_family.cint,
-                 addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
+                 addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
       raiseOSError(osLastError())
-    result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
+    setLen(result[0], result[0].cstring.len)
+    result[1] = Port(nativesockets.ntohs(name.sin6_port))
   else:
     raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
 
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index b85181edf..a1ae4e250 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -790,7 +790,10 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
         while true:
           var x = readdir(d)
           if x == nil: break
-          var y = $x.d_name
+          when defined(nimNoArrayToCstringConversion):
+            var y = $cstring(addr x.d_name)
+          else:
+            var y = $x.d_name.cstring
           if y != "." and y != "..":
             var s: Stat
             if not relative:
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 910d09afd..cc0f474f4 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -1890,11 +1890,17 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
       frmtstr[3] = '*'
       frmtstr[4] = floatFormatToChar[format]
       frmtstr[5] = '\0'
-      L = c_sprintf(buf, frmtstr, precision, f)
+      when defined(nimNoArrayToCstringConversion):
+        L = c_sprintf(addr buf, addr frmtstr, precision, f)
+      else:
+        L = c_sprintf(buf, frmtstr, precision, f)
     else:
       frmtstr[1] = floatFormatToChar[format]
       frmtstr[2] = '\0'
-      L = c_sprintf(buf, frmtstr, f)
+      when defined(nimNoArrayToCstringConversion):
+        L = c_sprintf(addr buf, addr frmtstr, f)
+      else:
+        L = c_sprintf(buf, frmtstr, f)
     result = newString(L)
     for i in 0 ..< L:
       # Depending on the locale either dot or comma is produced,
diff --git a/lib/system.nim b/lib/system.nim
index 207d616d2..d19a406cb 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1875,7 +1875,7 @@ proc `$` *(x: float): string {.magic: "FloatToStr", noSideEffect.}
 proc `$` *(x: bool): string {.magic: "BoolToStr", noSideEffect.}
   ## The stringify operator for a boolean argument. Returns `x`
   ## converted to the string "false" or "true".
-
+#
 proc `$` *(x: char): string {.magic: "CharToStr", noSideEffect.}
   ## The stringify operator for a character argument. Returns `x`
   ## converted to a string.
@@ -2437,20 +2437,28 @@ proc `$`*[T: tuple|object](x: T): string =
       result.add("...")
   result.add(")")
 
-proc collectionToString[T: set | seq](x: T, b, e: string): string =
-  when x is seq:
-    if x.isNil: return "nil"
-  result = b
+proc collectionToString[T](x: T, prefix, separator, suffix: string): string =
+  result = prefix
   var firstElement = true
   for value in items(x):
-    if not firstElement: result.add(", ")
+    if firstElement:
+      firstElement = false
+    else:
+      result.add(separator)
+
     when compiles(value.isNil):
-      if value.isNil: result.add "nil"
-      else: result.add($value)
+      # this branch should not be necessary
+      if value.isNil:
+        result.add "nil"
+      else:
+        result.add($value)
+    # prevent temporary string allocation
+    elif compiles(result.add(value)):
+      result.add(value)
     else:
       result.add($value)
-    firstElement = false
-  result.add(e)
+
+  result.add(suffix)
 
 proc `$`*[T](x: set[T]): string =
   ## generic ``$`` operator for sets that is lifted from the components
@@ -2458,7 +2466,7 @@ proc `$`*[T](x: set[T]): string =
   ##
   ## .. code-block:: nim
   ##   ${23, 45} == "{23, 45}"
-  collectionToString(x, "{", "}")
+  collectionToString(x, "{", ", ", "}")
 
 proc `$`*[T](x: seq[T]): string =
   ## generic ``$`` operator for seqs that is lifted from the components
@@ -2466,13 +2474,10 @@ proc `$`*[T](x: seq[T]): string =
   ##
   ## .. code-block:: nim
   ##   $(@[23, 45]) == "@[23, 45]"
-  collectionToString(x, "@[", "]")
-
-when false:
-  # causes bootstrapping to fail as we use array of chars and cstring should
-  # match better ...
-  proc `$`*[T, IDX](x: array[IDX, T]): string =
-    collectionToString(x, "[", "]")
+  if x.isNil:
+    "nil"
+  else:
+    collectionToString(x, "@[", ", ", "]")
 
 # ----------------- GC interface ---------------------------------------------
 
@@ -3329,6 +3334,10 @@ elif defined(JS):
     include "system/sysio"
 
 
+proc `$`*[T, IDX](x: array[IDX, T]): string =
+  ## generic ``$`` operator for arrays that is lifted from the components
+  collectionToString(x, "[", ", ", "]")
+
 proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
   ## a shorthand for ``echo(errormsg); quit(errorcode)``.
   echo(errormsg)
diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim
index 2b86ddf25..c8e251d1e 100644
--- a/lib/system/dyncalls.nim
+++ b/lib/system/dyncalls.nim
@@ -142,7 +142,10 @@ elif defined(windows) or defined(dos):
         dec(m)
         k = k div 10
         if k == 0: break
-      result = getProcAddress(cast[THINSTANCE](lib), decorated)
+      when defined(nimNoArrayToCstringConversion):
+        result = getProcAddress(cast[THINSTANCE](lib), addr decorated)
+      else:
+        result = getProcAddress(cast[THINSTANCE](lib), decorated)
       if result != nil: return
     procAddrError(name)
 
diff --git a/lib/system/endb.nim b/lib/system/endb.nim
index d4d10a52c..35d8f25c4 100644
--- a/lib/system/endb.nim
+++ b/lib/system/endb.nim
@@ -76,10 +76,10 @@ proc `==`(a, b: StaticStr): bool =
     return true
 
 proc `==`(a: StaticStr, b: cstring): bool =
-  result = c_strcmp(a.data, b) == 0
+  result = c_strcmp(unsafeAddr a.data, b) == 0
 
 proc write(f: File, s: StaticStr) =
-  write(f, cstring(s.data))
+  write(f, cstring(unsafeAddr s.data))
 
 proc listBreakPoints() =
   write(stdout, EndbBeg)
@@ -260,8 +260,8 @@ proc parseBreakpoint(s: cstring, start: int): Breakpoint =
   if result.high == 0: result.high = result.low
   i = scanFilename(s, dbgTemp, i)
   if dbgTemp.len != 0:
-    if not hasExt(dbgTemp.data): add(dbgTemp, ".nim")
-    result.filename = canonFilename(dbgTemp.data.cstring)
+    if not hasExt(addr dbgTemp.data): add(dbgTemp, ".nim")
+    result.filename = canonFilename(addr dbgTemp.data)
     if result.filename.isNil:
       debugOut("[Warning] no breakpoint could be set; unknown filename ")
       return
@@ -292,12 +292,12 @@ proc dbgEvaluate(stream: File, s: cstring, start: int, f: PFrame) =
     i = scanAndAppendWord(s, dbgTemp, i)
     for i in 0 .. getGlobalLen()-1:
       let v = getGlobal(i)
-      if c_strcmp(v.name, dbgTemp.data) == 0:
+      if c_strcmp(v.name, addr dbgTemp.data) == 0:
         writeVariable(stream, v)
   else:
     for i in 0 .. f.len-1:
       let v = getLocal(f, i)
-      if c_strcmp(v.name, dbgTemp.data) == 0:
+      if c_strcmp(v.name, addr dbgTemp.data) == 0:
         writeVariable(stream, v)
 
 proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
@@ -306,7 +306,7 @@ proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
   if dbgTemp.len == 0:
     invalidCommand()
     return
-  var stream = openAppend(dbgTemp.data)
+  var stream = openAppend(addr dbgTemp.data)
   if stream == nil:
     debugOut("[Warning] could not open or create file ")
     return
@@ -320,7 +320,7 @@ proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
     # just write it to stdout:
     listFrame(stdout, currFrame)
   else:
-    var stream = openAppend(dbgTemp.data)
+    var stream = openAppend(addr dbgTemp.data)
     if stream == nil:
       debugOut("[Warning] could not open or create file ")
       return
@@ -369,7 +369,7 @@ proc commandPrompt() =
     if not readLine(stdin, dbgUser): break
     if dbgUser.len == 0: dbgUser.len = oldLen
     # now look what we have to do:
-    var i = scanWord(dbgUser.data, dbgTemp, 0)
+    var i = scanWord(addr dbgUser.data, dbgTemp, 0)
     template `?`(x: expr): expr = dbgTemp == cstring(x)
     if ?"s" or ?"step":
       dbgState = dbStepInto
@@ -400,13 +400,13 @@ proc commandPrompt() =
         prevState = dbgState
         prevSkipFrame = dbgSkipToFrame
       dbgState = dbSkipCurrent
-      dbgEvaluate(stdout, dbgUser.data, i, dbgFramePtr)
+      dbgEvaluate(stdout, addr dbgUser.data, i, dbgFramePtr)
       dbgState = prevState
       dbgSkipToFrame = prevSkipFrame
     elif ?"o" or ?"out":
-      dbgOut(dbgUser.data, i, dbgFramePtr)
+      dbgOut(addr dbgUser.data, i, dbgFramePtr)
     elif ?"stackframe":
-      dbgStackFrame(dbgUser.data, i, dbgFramePtr)
+      dbgStackFrame(addr dbgUser.data, i, dbgFramePtr)
     elif ?"w" or ?"where":
       dbgShowExecutionPoint()
     elif ?"l" or ?"locals":
@@ -444,16 +444,16 @@ proc commandPrompt() =
     elif ?"bt" or ?"backtrace":
       dbgWriteStackTrace(framePtr)
     elif ?"b" or ?"break":
-      createBreakPoint(dbgUser.data, i)
+      createBreakPoint(addr dbgUser.data, i)
     elif ?"breakpoints":
       listBreakPoints()
     elif ?"toggle":
-      breakpointToggle(dbgUser.data, i)
+      breakpointToggle(addr dbgUser.data, i)
     elif ?"filenames":
       listFilenames()
     elif ?"maxdisplay":
       var parsed: int
-      i = scanNumber(dbgUser.data, parsed, i)
+      i = scanNumber(addr dbgUser.data, parsed, i)
       if dbgUser.data[i-1] in {'0'..'9'}:
         if parsed == 0: maxDisplayRecDepth = -1
         else: maxDisplayRecDepth = parsed
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index cee4e33a5..950981227 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -289,8 +289,12 @@ proc raiseExceptionAux(e: ref Exception) =
         add(buf, " [")
         xadd(buf, e.name, e.name.len)
         add(buf, "]\n")
-        unhandled(buf):
-          showErrorMessage(buf)
+        when defined(nimNoArrayToCstringConversion):
+          template tbuf(): untyped = addr buf
+        else:
+          template tbuf(): untyped = buf
+        unhandled(tbuf()):
+          showErrorMessage(tbuf())
           quitOrDebug()
 
 proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index ab02c58a2..19fa564fb 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -16,27 +16,32 @@ proc reprInt(x: int64): string {.compilerproc.} = return $x
 proc reprFloat(x: float): string {.compilerproc.} = return $x
 
 proc reprPointer(x: pointer): string {.compilerproc.} =
-  var buf: array[0..59, char]
-  discard c_sprintf(buf, "%p", x)
-  return $buf
+  when defined(nimNoArrayToCstringConversion):
+    result = newString(60)
+    let n = c_sprintf(addr result[0], "%p", x)
+    setLen(result, n)
+  else:
+    var buf: array[0..59, char]
+    discard c_sprintf(buf, "%p", x)
+    return $buf
 
 proc `$`(x: uint64): string =
   if x == 0:
     result = "0"
   else:
-    var buf: array[60, char]
+    result = newString(60)
     var i = 0
     var n = x
     while n != 0:
       let nn = n div 10'u64
-      buf[i] = char(n - 10'u64 * nn + ord('0'))
+      result[i] = char(n - 10'u64 * nn + ord('0'))
       inc i
       n = nn
+    result.setLen i
 
     let half = i div 2
     # Reverse
-    for t in 0 .. < half: swap(buf[t], buf[i-t-1])
-    result = $buf
+    for t in 0 .. < half: swap(result[t], result[i-t-1])
 
 proc reprStrAux(result: var string, s: cstring; len: int) =
   if cast[pointer](s) == nil:
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 90201202c..43b5a0292 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -24,7 +24,10 @@ proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
   if a == b: return 0
   if a == nil: return -1
   if b == nil: return 1
-  return c_strcmp(a.data, b.data)
+  when defined(nimNoArrayToCstringConversion):
+    return c_strcmp(addr a.data, addr b.data)
+  else:
+    return c_strcmp(a.data, b.data)
 
 proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
   if a == b: return true
@@ -320,7 +323,10 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
 
 proc add*(result: var string; x: float) =
   var buf: array[0..64, char]
-  var n: int = c_sprintf(buf, "%.16g", x)
+  when defined(nimNoArrayToCstringConversion):
+    var n: int = c_sprintf(addr buf, "%.16g", x)
+  else:
+    var n: int = c_sprintf(buf, "%.16g", x)
   var hasDot = false
   for i in 0..n-1:
     if buf[i] == ',':
@@ -342,7 +348,10 @@ proc add*(result: var string; x: float) =
     else:
       result.add "inf"
   else:
-    result.add buf
+    var i = 0
+    while buf[i] != '\0':
+      result.add buf[i]
+      inc i
 
 proc nimFloatToStr(f: float): string {.compilerproc.} =
   result = newStringOfCap(8)
@@ -507,7 +516,10 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
   t[ti-2] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
   t[ti-3] = ('0'.ord + abs_exponent mod 10).char
 
-  number = c_strtod(t, nil)
+  when defined(nimNoArrayToCstringConversion):
+    number = c_strtod(addr t, nil)
+  else:
+    number = c_strtod(t, nil)
 
 proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
   result = newStringOfCap(sizeof(x)*4)
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index 40b54b08d..5cbe2887c 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -590,13 +590,13 @@ proc md5_Transform*(c: var MD5_CTX; b: ptr cuchar){.importc: "MD5_Transform".}
 
 from strutils import toHex, toLowerAscii
 
-proc hexStr (buf:cstring): string =
+proc hexStr(buf: cstring): string =
   # turn md5s output into a nice hex str
   result = newStringOfCap(32)
   for i in 0 .. <16:
     result.add toHex(buf[i].ord, 2).toLowerAscii
 
-proc md5_File* (file: string): string {.raises: [IOError,Exception].} =
+proc md5_File*(file: string): string {.raises: [IOError,Exception].} =
   ## Generate MD5 hash for a file. Result is a 32 character
   # hex string with lowercase characters (like the output
   # of `md5sum`
@@ -611,14 +611,14 @@ proc md5_File* (file: string): string {.raises: [IOError,Exception].} =
   while(let bytes = f.readChars(buf, 0, sz); bytes > 0):
     discard md5_update(ctx, buf[0].addr, bytes)
 
-  discard md5_final( buf[0].addr, ctx )
+  discard md5_final(buf[0].addr, ctx)
   f.close
 
-  result = hexStr(buf)
+  result = hexStr(addr buf)
 
-proc md5_Str*(str:string): string =
-  ##Generate MD5 hash for a string. Result is a 32 character
-  #hex string with lowercase characters
+proc md5_Str*(str: string): string =
+  ## Generate MD5 hash for a string. Result is a 32 character
+  ## hex string with lowercase characters
   var
     ctx: MD5_CTX
     res: array[MD5_DIGEST_LENGTH,char]
@@ -631,5 +631,5 @@ proc md5_Str*(str:string): string =
     discard md5_update(ctx, input[i].addr, L)
     i += L
 
-  discard md5_final(res,ctx)
-  result = hexStr(res)
+  discard md5_final(addr res, ctx)
+  result = hexStr(addr res)
diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim
index bf54c0d06..b56cdc71b 100644
--- a/tests/async/tnewasyncudp.nim
+++ b/tests/async/tnewasyncudp.nim
@@ -54,7 +54,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} =
     k = 0
     while k < messagesToSend:
       zeroMem(addr(buffer[0]), 16384)
-      zeroMem(cast[pointer](addr(saddr)), sizeof(Sockaddr_in))      
+      zeroMem(cast[pointer](addr(saddr)), sizeof(Sockaddr_in))
       var message = "Message " & $(i * messagesToSend + k)
       await sendTo(sock, addr message[0], len(message),
                    name, sizeof(Sockaddr_in).SockLen)
@@ -62,7 +62,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} =
                                     16384, cast[ptr SockAddr](addr saddr),
                                     addr slen)
       size = 0
-      var grammString = $buffer
+      var grammString = $cstring(addr buffer)
       if grammString == message:
         saveSendingPort(sockport)
         inc(recvCount)
@@ -84,7 +84,7 @@ proc readMessages(server: AsyncFD) {.async.} =
                                   16384, cast[ptr SockAddr](addr(saddr)),
                                   addr(slen))
     size = 0
-    var grammString = $buffer
+    var grammString = $cstring(addr buffer)
     if grammString.startswith("Message ") and
        saddr.sin_addr.s_addr == 0x100007F:
       await sendTo(server, addr grammString[0], len(grammString),
diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim
index d7783ce26..559c8aaca 100644
--- a/tests/float/tfloat4.nim
+++ b/tests/float/tfloat4.nim
@@ -9,7 +9,7 @@ proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "<stdio.h>", vara
 
 proc floatToStr(f: float64): string =
   var buffer: array[128, char]
-  c_sprintf(buffer, "%.16e", f)
+  c_sprintf(addr buffer, "%.16e", f)
   result = ""
   for ch in buffer:
     if ch == '\0':
diff --git a/tests/gc/gctest.nim b/tests/gc/gctest.nim
index b3b9af608..f5c81f033 100644
--- a/tests/gc/gctest.nim
+++ b/tests/gc/gctest.nim
@@ -31,7 +31,7 @@ type
     of nkList: sons: seq[PCaseNode]
     else: unused: seq[string]
 
-  TIdObj* = object of TObject
+  TIdObj* = object of RootObj
     id*: int  # unique id; use this for comparisons and not the pointers
 
   PIdObj* = ref TIdObj
diff --git a/tests/misc/treadx.nim b/tests/misc/treadx.nim
index 49b6ad691..e68b8933d 100644
--- a/tests/misc/treadx.nim
+++ b/tests/misc/treadx.nim
@@ -6,9 +6,8 @@ when not defined(windows):
   var buf: array[0..10, char]
   while true:
     var r = read(0, addr(buf), sizeof(buf)-1)
-    add inp, $buf
+    add inp, $cstring(addr buf)
     if r != sizeof(buf)-1: break
 
   echo inp
   #dafkladskölklödsaf ölksdakölfölksfklwe4iojr389wr 89uweokf sdlkf jweklr jweflksdj fioewjfsdlfsd
-
diff --git a/tests/system/toString.nim b/tests/system/toString.nim
index a2337f5dd..1279897a7 100644
--- a/tests/system/toString.nim
+++ b/tests/system/toString.nim
@@ -1,42 +1,53 @@
 discard """
-  output:'''@[23, 45]
-@[, foo, bar]
-{a, b, c}
-2.3242
-2.982
-123912.1
-123912.1823
-5.0
-1e+100
-inf
--inf
-nan
-nil
-nil'''
+  output:""
 """
 
-echo($(@[23, 45]))
-echo($(@["", "foo", "bar"]))
-#echo($(["", "foo", "bar"]))
-#echo($([23, 45]))
+doAssert "@[23, 45]" == $(@[23, 45])
+doAssert  "[32, 45]" == $([32, 45])
+doAssert "@[, foo, bar]" == $(@["", "foo", "bar"])
+doAssert  "[, foo, bar]" ==  $(["", "foo", "bar"])
 
 # bug #2395
-
 let alphaSet: set[char] = {'a'..'c'}
-echo alphaSet
-
-echo($(2.3242))
-echo($(2.982))
-echo($(123912.1))
-echo($(123912.1823))
-echo($(5.0))
-echo($(1e100))
-echo($(1e1000000))
-echo($(-1e1000000))
-echo($(0.0/0.0))
+doAssert "{a, b, c}" == $alphaSet
+doAssert "2.3242" == $(2.3242)
+doAssert "2.982" == $(2.982)
+doAssert "123912.1" == $(123912.1)
+doAssert "123912.1823" == $(123912.1823)
+doAssert "5.0" == $(5.0)
+doAssert "1e+100" == $(1e100)
+doAssert "inf" == $(1e1000000)
+doAssert "-inf" == $(-1e1000000)
+doAssert "nan" == $(0.0/0.0)
 
 # nil tests
+# maybe a bit inconsistent in types
 var x: seq[string]
-var y: string
-echo(x)
-echo(y)
+doAssert "nil" == $(x)
+
+var y: string = nil
+doAssert nil == $(y)
+
+type
+  Foo = object
+    a: int
+    b: string
+
+var foo1: Foo
+
+# nil string should be an some point in time equal to the empty string
+doAssert(($foo1)[0..9] == "(a: 0, b: ")
+
+const
+  data = @['a','b', '\0', 'c','d']
+  dataStr = $data
+
+# ensure same result when on VM or when at program execution
+doAssert dataStr == $data
+
+import strutils
+# array test
+
+let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0']
+doAssert $arr == "[H, e, l, l, o,  , W, o, r, l, d, !, \0]"
+doAssert $cstring(unsafeAddr arr) == "Hello World!"
diff --git a/todo.txt b/todo.txt
index f36a8a10c..87cb2b509 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,7 +1,6 @@
 version 1.0 battle plan
 =======================
 
-- disallow conversions from ``array`` to ``cstring``!
 - make 'not nil' the default (produce warnings instead of errors for
   a smooth migration path)
 - case objects needs to be safe and need to support pattern matching