From 1f7fc7f279a4a1c5f4854f7c7a39a58bb2cca805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Mon, 29 May 2017 17:53:20 +0200 Subject: [PATCH 01/17] arrays can now be printed --- compiler/rodutils.nim | 3 +- lib/pure/os.nim | 2 +- lib/system.nim | 64 ++++++++++++++++++++++++++++----------- lib/system/repr.nim | 4 +-- tests/system/toString.nim | 33 ++++++++++++++++++-- 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 77f7c844f..03d5c95eb 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -23,7 +23,7 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = else: var buf: array[0..80, char] c_sprintf(buf, "%#.16e" & literalPostfix, f) - result = $buf + result = newString(buf) proc encodeStr*(s: string, result: var string) = for i in countup(0, len(s) - 1): @@ -133,4 +133,3 @@ iterator decodeStrArray*(s: cstring): string = while s[i] != '\0': yield decodeStr(s, i) if s[i] == ' ': inc i - diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 2195f6327..5211bc00c 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1010,7 +1010,7 @@ 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 + var y = newString(x.d_name) if y != "." and y != "..": var s: Stat if not relative: diff --git a/lib/system.nim b/lib/system.nim index d62c0424e..3dc892c44 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1852,7 +1852,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. @@ -1872,6 +1872,27 @@ proc `$` *[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.} ## a ``$`` operator for a concrete enumeration is provided, this is ## used instead. (In other words: *Overwriting* is possible.) +proc `$`*[T: ref](arg: T): string = + ## The stringify operator to handle the nil value of all ref types. + ## Then it forwards to stringify operator of the underlying (value) type. + if arg.isNil: + "nil" + else: + "ref " & $arg[] + +proc `$`*[T: ptr](arg: T): string = + ## The stringify operator to handle the nil value of all ptr types. + ## Then it forwards to stringify operator of the underlying (value) type. + if arg.isNil: + "nil" + else: + "ptr " & $arg[] + +proc newString*[N](data: array[N, char]): string = + ## Construct a string from an array of characters. The `data` is + ## expected to be a null terminated string as it is often used in C. + $(cast[cstring](data[0].unsafeAddr)) + # undocumented: proc getRefcount*[T](x: ref T): int {.importc: "getRefcount", noSideEffect.} proc getRefcount*(x: string): int {.importc: "getRefcount", noSideEffect.} @@ -2410,20 +2431,25 @@ 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) 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 @@ -2431,7 +2457,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 @@ -2439,13 +2465,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 --------------------------------------------- @@ -3301,6 +3324,11 @@ 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/repr.nim b/lib/system/repr.nim index ab02c58a2..fcf65dd0e 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -18,7 +18,7 @@ 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 + return newString(buf) proc `$`(x: uint64): string = if x == 0: @@ -36,7 +36,7 @@ proc `$`(x: uint64): string = let half = i div 2 # Reverse for t in 0 .. < half: swap(buf[t], buf[i-t-1]) - result = $buf + result = newString(buf) proc reprStrAux(result: var string, s: cstring; len: int) = if cast[pointer](s) == nil: diff --git a/tests/system/toString.nim b/tests/system/toString.nim index a2337f5dd..f9697ec75 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -12,14 +12,16 @@ inf -inf nan nil -nil''' +nil +(a: 0, b: nil) +nil +ptr (a: 0, b: nil)''' """ echo($(@[23, 45])) echo($(@["", "foo", "bar"])) #echo($(["", "foo", "bar"])) #echo($([23, 45])) - # bug #2395 let alphaSet: set[char] = {'a'..'c'} @@ -40,3 +42,30 @@ var x: seq[string] var y: string echo(x) echo(y) + +type + Foo = object + a: int + b: string + +var foo1: Foo +var foo2: ref Foo +var foo3: ptr Foo = foo1.addr + +echo foo1 +echo foo2 +echo foo3 + +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 startsWith($arr, "[H, e, l, l, o, , W, o, r, l, d, !,") +doAssert newString(arr) == "Hello World!" From 0852be2dec72406c8d9c93e094a8907b52d1aa5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Wed, 31 May 2017 14:05:45 +0200 Subject: [PATCH 02/17] remove $ for ptr/ref, prefer using string over array of char --- compiler/rodutils.nim | 8 ++++---- lib/system.nim | 30 ++++++++++++------------------ lib/system/repr.nim | 14 +++++++------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 03d5c95eb..1352afadb 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: "", nodecl, varargs.} +proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "", nodecl, varargs.} proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = if f != f: @@ -21,9 +21,9 @@ 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 = newString(buf) + result = newString(80) + let newLen = c_snprintf(result[0].addr, 81, "%#.16e" & literalPostfix, f) + result.setLen(newLen) proc encodeStr*(s: string, result: var string) = for i in countup(0, len(s) - 1): diff --git a/lib/system.nim b/lib/system.nim index 3dc892c44..b5008129d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1872,26 +1872,20 @@ proc `$` *[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.} ## a ``$`` operator for a concrete enumeration is provided, this is ## used instead. (In other words: *Overwriting* is possible.) -proc `$`*[T: ref](arg: T): string = - ## The stringify operator to handle the nil value of all ref types. - ## Then it forwards to stringify operator of the underlying (value) type. - if arg.isNil: - "nil" - else: - "ref " & $arg[] - -proc `$`*[T: ptr](arg: T): string = - ## The stringify operator to handle the nil value of all ptr types. - ## Then it forwards to stringify operator of the underlying (value) type. - if arg.isNil: - "nil" - else: - "ptr " & $arg[] - -proc newString*[N](data: array[N, char]): string = +proc newString*[N](data: array[N, char]): string {.noSideEffect.} = ## Construct a string from an array of characters. The `data` is ## expected to be a null terminated string as it is often used in C. - $(cast[cstring](data[0].unsafeAddr)) + when nimvm: + # cannot cast on the vm + # not recommended to use this procedure on the vm at all, but at least it doesn't fail. + result = "" + for c in data: + if c == '\0': + return + else: + result.add c + else: + result = $(cast[cstring](data[0].unsafeAddr)) # undocumented: proc getRefcount*[T](x: ref T): int {.importc: "getRefcount", noSideEffect.} diff --git a/lib/system/repr.nim b/lib/system/repr.nim index fcf65dd0e..2775b1b3e 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -16,27 +16,27 @@ 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 newString(buf) + result = newString(60) + let newLen = c_sprintf(result[0].addr, "%p", x) + result.setLen newLen 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 = newString(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: From ddea990a70606aaa19513510ae4b47ada7e49fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Tue, 6 Jun 2017 21:19:50 +0200 Subject: [PATCH 03/17] removed newString proc again, reverted some unnecesary changes --- compiler/rodutils.nim | 6 +++--- lib/pure/nativesockets.nim | 4 ++-- lib/pure/os.nim | 2 +- lib/system.nim | 15 --------------- lib/system/repr.nim | 6 +++--- tests/async/tnewasyncudp.nim | 6 +++--- tests/misc/treadx.nim | 3 +-- tests/system/toString.nim | 12 +++--------- 8 files changed, 16 insertions(+), 38 deletions(-) diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 1352afadb..3a90a207c 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -21,9 +21,9 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = if f > 0.0: result = "INF" else: result = "-INF" else: - result = newString(80) - let newLen = c_snprintf(result[0].addr, 81, "%#.16e" & literalPostfix, f) - result.setLen(newLen) + var buf: array[0..80, char] + let newLen = 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): diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 7568408a6..a33836458 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -500,7 +500,7 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = if inet_ntop(name.sin6_family.cint, addr name, buf.cstring, sizeof(buf).int32).isNil: raiseOSError(osLastError()) - result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) + result = ($buf.cstring, Port(nativesockets.ntohs(name.sin6_port))) else: raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") @@ -536,7 +536,7 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = if inet_ntop(name.sin6_family.cint, addr name, buf.cstring, sizeof(buf).int32).isNil: raiseOSError(osLastError()) - result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) + result = ($buf.cstring, 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 5211bc00c..a227e9f32 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1010,7 +1010,7 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: while true: var x = readdir(d) if x == nil: break - var y = newString(x.d_name) + var y = $x.d_name.cstring if y != "." and y != "..": var s: Stat if not relative: diff --git a/lib/system.nim b/lib/system.nim index b5008129d..014538098 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1872,21 +1872,6 @@ proc `$` *[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.} ## a ``$`` operator for a concrete enumeration is provided, this is ## used instead. (In other words: *Overwriting* is possible.) -proc newString*[N](data: array[N, char]): string {.noSideEffect.} = - ## Construct a string from an array of characters. The `data` is - ## expected to be a null terminated string as it is often used in C. - when nimvm: - # cannot cast on the vm - # not recommended to use this procedure on the vm at all, but at least it doesn't fail. - result = "" - for c in data: - if c == '\0': - return - else: - result.add c - else: - result = $(cast[cstring](data[0].unsafeAddr)) - # undocumented: proc getRefcount*[T](x: ref T): int {.importc: "getRefcount", noSideEffect.} proc getRefcount*(x: string): int {.importc: "getRefcount", noSideEffect.} diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 2775b1b3e..172b4c08c 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -16,9 +16,9 @@ proc reprInt(x: int64): string {.compilerproc.} = return $x proc reprFloat(x: float): string {.compilerproc.} = return $x proc reprPointer(x: pointer): string {.compilerproc.} = - result = newString(60) - let newLen = c_sprintf(result[0].addr, "%p", x) - result.setLen newLen + var buf: array[60, char] + discard c_sprintf(result[0].addr, "%p", x) + result = $buf.cstring proc `$`(x: uint64): string = if x == 0: diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim index bf54c0d06..da731f4b8 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 = $buffer.cstring 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 = $buffer.cstring if grammString.startswith("Message ") and saddr.sin_addr.s_addr == 0x100007F: await sendTo(server, addr grammString[0], len(grammString), diff --git a/tests/misc/treadx.nim b/tests/misc/treadx.nim index 49b6ad691..2e3904cd1 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, $buf.cstring 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 f9697ec75..cd9a49272 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -13,9 +13,6 @@ inf nan nil nil -(a: 0, b: nil) -nil -ptr (a: 0, b: nil)''' """ echo($(@[23, 45])) @@ -49,12 +46,8 @@ type b: string var foo1: Foo -var foo2: ref Foo -var foo3: ptr Foo = foo1.addr -echo foo1 -echo foo2 -echo foo3 +doAssert $foo1 == "(a: 0, b: nil)" const data = @['a','b', '\0', 'c','d'] @@ -67,5 +60,6 @@ import strutils # array test let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0'] +# not sure if this is really a good idea doAssert startsWith($arr, "[H, e, l, l, o, , W, o, r, l, d, !,") -doAssert newString(arr) == "Hello World!" +doAssert $arr.cstring == "Hello World!" From c9a2acefc1f34801df4e57d8a2462bff4a0666d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Tue, 6 Jun 2017 22:33:52 +0200 Subject: [PATCH 04/17] fix --- lib/system/repr.nim | 2 +- tests/gc/gctest.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 172b4c08c..58c86b0db 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -17,7 +17,7 @@ proc reprFloat(x: float): string {.compilerproc.} = return $x proc reprPointer(x: pointer): string {.compilerproc.} = var buf: array[60, char] - discard c_sprintf(result[0].addr, "%p", x) + discard c_sprintf(buf.cstring, "%p", x) result = $buf.cstring proc `$`(x: uint64): string = 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 From a6e6b05565ad717c77d6d66b3c6fd865ad0f615d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Wed, 19 Jul 2017 00:08:00 +0200 Subject: [PATCH 05/17] made toString test assert based --- tests/system/toString.nim | 54 +++++++++++++++------------------------ 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/tests/system/toString.nim b/tests/system/toString.nim index cd9a49272..d002bb379 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -1,44 +1,32 @@ 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 From 11914a23bebe4499f4161edb9a421e37a3182abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Mon, 24 Jul 2017 23:55:30 +0200 Subject: [PATCH 06/17] prevent null characters in $ on collections of char --- lib/system.nim | 9 ++++++++- tests/system/toString.nim | 4 +--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 014538098..0bc0a0dbf 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2425,6 +2425,14 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string = result.add "nil" else: result.add($value) + # prevent temporary string allocation + elif compiles(result.add(value)): + # don't insert '\0' characters into the result string + when value is char: + if value != '\0': + result.add(value) + else: + result.add(value) else: result.add($value) @@ -3307,7 +3315,6 @@ 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/tests/system/toString.nim b/tests/system/toString.nim index d002bb379..0eed596ae 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -47,7 +47,5 @@ doAssert dataStr == $data import strutils # array test let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0'] - -# not sure if this is really a good idea -doAssert startsWith($arr, "[H, e, l, l, o, , W, o, r, l, d, !,") +doAssert $arr == "[H, e, l, l, o, , W, o, r, l, d, !, ]" doAssert $arr.cstring == "Hello World!" From 3bf1f019a76c6fee5553a7daf7d984cfd39cfb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Mon, 7 Aug 2017 17:54:05 +0200 Subject: [PATCH 07/17] improved genEcho --- compiler/ccgexprs.nim | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index de784acf9..d971c0bc2 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -953,23 +953,40 @@ proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. internalAssert n.kind == nkBracket - var args: Rope = nil + var args = newSeq[tuple[hasvalue,len,str: string]](0) var a: TLoc for i in countup(0, n.len-1): if n.sons[i].skipConv.kind == nkNilLit: - add(args, ", \"nil\"") + args.add((hasvalue:"0", len: "0", str: "\"\"")) else: initLocExpr(p, n.sons[i], a) - addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) + let ptrexpr: string = $rdLoc(a) + let len = format("($1)->Sup.len", ptrexpr) + let str = format("($1)->data", ptrexpr) + args.add((hasvalue: ptrexpr, len: len, str: str)) if platform.targetOS == osGenode: # bypass libc and print directly to the Genode LOG session p.module.includeHeader("") - linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) + # separated args + var rope: Rope + for arg in args: + rope.add ", " + rope.add arg.str + # TODO this is wrong, it does not properly handle '\0' characters, and locking is not done. + linefmt(p, cpsStmts, """Genode::log(""$1);$n""", rope) + for arg in args: + if arg.hasvalue != "0": # if we know statically that there is no string, it can be skipped + linefmt(p, cpsStmts, "if($1) Genode::log($2);$n", rope(arg.hasvalue), rope(arg.str)) else: p.module.includeHeader("") - linefmt(p, cpsStmts, "printf($1$2);$n", - makeCString(repeat("%s", n.len) & tnl), args) - linefmt(p, cpsStmts, "fflush(stdout);$n") + #linefmt(p, cpsStmts, "printf($1$2);$n", + # makeCString(repeat("%s", n.len) & tnl), args) + linefmt(p, cpsStmts, "flockfile(stdout);$n") + for arg in args: + if arg.hasvalue != "0": # if we know statically that there is no string, it can be skipped + linefmt(p, cpsStmts, "if($1) fwrite($2, 1, $3, stdout);$n", rope(arg.hasvalue), rope(arg.str), rope(arg.len)); + linefmt(p, cpsStmts, "putchar('\\n'); fflush(stdout);$n") + linefmt(p, cpsStmts, "funlockfile(stdout);$n") proc gcUsage(n: PNode) = if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) From 54808ab12fcbf8cc253129ed03f560fc6dd2648e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Mon, 7 Aug 2017 18:21:21 +0200 Subject: [PATCH 08/17] don't filter '\0' characters in string generation --- compiler/rodutils.nim | 2 +- lib/system.nim | 7 +------ tests/system/toString.nim | 3 ++- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 3a90a207c..6e77e6b8f 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -22,7 +22,7 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = else: result = "-INF" else: var buf: array[0..80, char] - let newLen = c_snprintf(buf.cstring, buf.len.uint, "%#.16e%s", f, literalPostfix.cstring) + discard c_snprintf(buf.cstring, buf.len.uint, "%#.16e%s", f, literalPostfix.cstring) result = $buf.cstring proc encodeStr*(s: string, result: var string) = diff --git a/lib/system.nim b/lib/system.nim index 0bc0a0dbf..d2bdeae0e 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2427,12 +2427,7 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string = result.add($value) # prevent temporary string allocation elif compiles(result.add(value)): - # don't insert '\0' characters into the result string - when value is char: - if value != '\0': - result.add(value) - else: - result.add(value) + result.add(value) else: result.add($value) diff --git a/tests/system/toString.nim b/tests/system/toString.nim index 0eed596ae..377336c90 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -46,6 +46,7 @@ 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, !, ]" +doAssert $arr == "[H, e, l, l, o, , W, o, r, l, d, !, \0]" doAssert $arr.cstring == "Hello World!" From 608cc18178ad8618767e50552fe51d1d179a6aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Tue, 8 Aug 2017 13:58:50 +0200 Subject: [PATCH 09/17] reverted genEcho --- compiler/ccgexprs.nim | 31 +++++++------------------------ tests/system/toString.nim | 3 ++- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index d971c0bc2..de784acf9 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -953,40 +953,23 @@ proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. internalAssert n.kind == nkBracket - var args = newSeq[tuple[hasvalue,len,str: string]](0) + var args: Rope = nil var a: TLoc for i in countup(0, n.len-1): if n.sons[i].skipConv.kind == nkNilLit: - args.add((hasvalue:"0", len: "0", str: "\"\"")) + add(args, ", \"nil\"") else: initLocExpr(p, n.sons[i], a) - let ptrexpr: string = $rdLoc(a) - let len = format("($1)->Sup.len", ptrexpr) - let str = format("($1)->data", ptrexpr) - args.add((hasvalue: ptrexpr, len: len, str: str)) + addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) if platform.targetOS == osGenode: # bypass libc and print directly to the Genode LOG session p.module.includeHeader("") - # separated args - var rope: Rope - for arg in args: - rope.add ", " - rope.add arg.str - # TODO this is wrong, it does not properly handle '\0' characters, and locking is not done. - linefmt(p, cpsStmts, """Genode::log(""$1);$n""", rope) - for arg in args: - if arg.hasvalue != "0": # if we know statically that there is no string, it can be skipped - linefmt(p, cpsStmts, "if($1) Genode::log($2);$n", rope(arg.hasvalue), rope(arg.str)) + linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) else: p.module.includeHeader("") - #linefmt(p, cpsStmts, "printf($1$2);$n", - # makeCString(repeat("%s", n.len) & tnl), args) - linefmt(p, cpsStmts, "flockfile(stdout);$n") - for arg in args: - if arg.hasvalue != "0": # if we know statically that there is no string, it can be skipped - linefmt(p, cpsStmts, "if($1) fwrite($2, 1, $3, stdout);$n", rope(arg.hasvalue), rope(arg.str), rope(arg.len)); - linefmt(p, cpsStmts, "putchar('\\n'); fflush(stdout);$n") - linefmt(p, cpsStmts, "funlockfile(stdout);$n") + linefmt(p, cpsStmts, "printf($1$2);$n", + makeCString(repeat("%s", n.len) & tnl), args) + linefmt(p, cpsStmts, "fflush(stdout);$n") proc gcUsage(n: PNode) = if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) diff --git a/tests/system/toString.nim b/tests/system/toString.nim index 377336c90..bc8720b55 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -35,7 +35,8 @@ type var foo1: Foo -doAssert $foo1 == "(a: 0, b: nil)" +# 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'] From 3308d265816b342a842e7cabef492a54ea722e74 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 10 Oct 2017 00:17:20 +0200 Subject: [PATCH 10/17] breaking change: arrays of char do not convert to cstring; ptr to array of char does --- changelog.md | 3 +++ compiler/condsyms.nim | 1 + compiler/rodutils.nim | 11 ++++++++--- compiler/sigmatch.nim | 13 +++++++------ compiler/types.nim | 8 -------- lib/pure/nativesockets.nim | 14 ++++++++------ lib/pure/os.nim | 5 ++++- lib/pure/strutils.nim | 10 ++++++++-- lib/system/dyncalls.nim | 5 ++++- lib/system/excpt.nim | 8 ++++++-- lib/system/repr.nim | 11 ++++++++--- lib/system/sysstr.nim | 20 ++++++++++++++++---- 12 files changed, 73 insertions(+), 36 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/rodutils.nim b/compiler/rodutils.nim index 6e77e6b8f..0456e9349 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -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] - discard c_snprintf(buf.cstring, buf.len.uint, "%#.16e%s", f, literalPostfix.cstring) - result = $buf.cstring + 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): 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/pure/nativesockets.nim b/lib/pure/nativesockets.nim index c8fb041d8..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.cstring, 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.cstring, 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 e6ef96ba3..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.cstring + 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/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/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 58c86b0db..19fa564fb 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -16,9 +16,14 @@ 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[60, char] - discard c_sprintf(buf.cstring, "%p", x) - result = $buf.cstring + 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: 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) From 7016a8f57d819f2d7f7f7275b93ec358c29a268f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 10 Oct 2017 01:04:09 +0200 Subject: [PATCH 11/17] make openssl.nim compile again --- lib/wrappers/openssl.nim | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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) From 55cb959a68f00e4702bf1f80207d574dd1888f46 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 10 Oct 2017 09:18:31 +0200 Subject: [PATCH 12/17] make tests green again; closes #5861 --- lib/system/endb.nim | 30 +++++++++++++++--------------- tests/async/tnewasyncudp.nim | 4 ++-- tests/float/tfloat4.nim | 2 +- tests/system/toString.nim | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) 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/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim index da731f4b8..b56cdc71b 100644 --- a/tests/async/tnewasyncudp.nim +++ b/tests/async/tnewasyncudp.nim @@ -62,7 +62,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} = 16384, cast[ptr SockAddr](addr saddr), addr slen) size = 0 - var grammString = $buffer.cstring + 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.cstring + 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: "", 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/system/toString.nim b/tests/system/toString.nim index bc8720b55..1279897a7 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -50,4 +50,4 @@ import strutils 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 $arr.cstring == "Hello World!" +doAssert $cstring(unsafeAddr arr) == "Hello World!" From 8cdd89f45647eb134316f3d4bd6668ce69e01875 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 10 Oct 2017 09:34:03 +0200 Subject: [PATCH 13/17] make tests green again --- tests/misc/treadx.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misc/treadx.nim b/tests/misc/treadx.nim index 2e3904cd1..e68b8933d 100644 --- a/tests/misc/treadx.nim +++ b/tests/misc/treadx.nim @@ -6,7 +6,7 @@ when not defined(windows): var buf: array[0..10, char] while true: var r = read(0, addr(buf), sizeof(buf)-1) - add inp, $buf.cstring + add inp, $cstring(addr buf) if r != sizeof(buf)-1: break echo inp From ac3e3cf2b000ad30fad81c957cb9d73d887e01d2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 10 Oct 2017 11:15:18 +0200 Subject: [PATCH 14/17] fixes a renderer regression that affects c2nim --- compiler/renderer.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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, "(") From 083cb00e8d5746abe34a4844334d7d777f504fec Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 10 Oct 2017 11:26:29 +0200 Subject: [PATCH 15/17] added a minor note to destroyer.nim --- compiler/destroyer.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 2ccef1724..95997e3c8 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() From 503248efdebf725de9ac0d5de3758bbdc6aafc16 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 10 Oct 2017 11:26:40 +0200 Subject: [PATCH 16/17] another attempt to make tests green again --- lib/impure/ssl.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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) = From 91981c07bd9335dbd32afb636bd1437a588f39eb Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 10 Oct 2017 13:17:35 +0200 Subject: [PATCH 17/17] some progress on destroyer.nim --- compiler/destroyer.nim | 59 +++++++++++++++++++++++++++++++++++++++++- todo.txt | 1 - 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 95997e3c8..e7ff00bb9 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -186,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..