diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 59b98306070a..d648edd4078a 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -863,7 +863,7 @@ proc getSymbol(L: var Lexer, tok: var Token) = else: break tokenEnd(tok, pos-1) h = !$h - tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) + tok.ident = L.cache.getIdent(L.buf[L.bufpos].addr.toCstring, pos - L.bufpos, h) if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or (tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): tok.tokType = tkSymbol @@ -877,7 +877,7 @@ proc getSymbol(L: var Lexer, tok: var Token) = proc endOperator(L: var Lexer, tok: var Token, pos: int, hash: Hash) {.inline.} = var h = !$hash - tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) + tok.ident = L.cache.getIdent(L.buf[L.bufpos].addr.toCstring, pos - L.bufpos, h) if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon)) L.bufpos = pos diff --git a/compiler/options.nim b/compiler/options.nim index 9ba4d62e4bc1..cc704adcd659 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -172,7 +172,10 @@ type ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, ideHighlight, ideOutline, ideKnown, ideMsg, ideProject - Feature* = enum ## experimental features; DO NOT RENAME THESE! + Feature* = enum + ## experimental featuress + ## do not rename these since they're exposed to users via `--experimental:foo` + ## deprecated features start with `legacy`. implicitDeref, dotOperators, callOperator, @@ -190,7 +193,8 @@ type vmopsDanger, strictFuncs, views, - strictNotNil + strictNotNil, + legacyImplicitCstringConv, LegacyFeature* = enum allowSemcheckedAstModification, @@ -369,6 +373,14 @@ template setErrorMaxHighMaybe*(conf: ConfigRef) = ## do not stop after first error (but honor --errorMax if provided) assignIfDefault(conf.errorMax, high(int)) +template toSet(a: typedesc[enum]): untyped = {a.low..a.high} + +const + FeatureDepr* = {legacyImplicitCstringConv} + # can't use `LegacyFeature` because we want to be able to localize + # to a context, eg {.push experimental: "legacyImplicitCstringConv".} .. {.pop.} + FeatureExp* = Feature.toSet - FeatureDepr + proc setNoteDefaults*(conf: ConfigRef, note: TNoteKind, enabled = true) = template fun(op) = conf.notes.op note diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index f6df422e620b..749353e91dec 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -14,7 +14,7 @@ from hashes import Hash import types proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len) -proc `&=`(c: var MD5Context, ch: char) = md5Update(c, unsafeAddr ch, 1) +proc `&=`(c: var MD5Context, ch: char) = md5Update(c, unsafeAddr(ch).toCstring, 1) proc `&=`(c: var MD5Context, r: Rope) = for l in leaves(r): md5Update(c, l, l.len) proc `&=`(c: var MD5Context, i: BiggestInt) = diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 767f34ddadd5..af30d4467698 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1405,16 +1405,24 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyNil: result = f.allowsNil of tyString: result = isConvertible of tyPtr: - # ptr[Tag, char] is not convertible to 'cstring' for now: - if a.len == 1: - let pointsTo = a[0].skipTypes(abstractInst) - if pointsTo.kind == tyChar: result = isConvertible - elif pointsTo.kind == tyUncheckedArray and pointsTo[0].kind == tyChar: - result = isConvertible - elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo[0]) == 0 and - skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} and - pointsTo[1].kind == tyChar: - result = isConvertible + if legacyImplicitCstringConv in c.c.features: + # issue #13790 + # ptr[Tag, char] is not convertible to 'cstring' for now: + # xxx: aren't memory regions not used anymore? + if a.len == 1: + let pointsTo = a[0].skipTypes(abstractInst) + if pointsTo.kind == tyChar: result = isConvertible + elif pointsTo.kind == tyUncheckedArray and pointsTo[0].kind == tyChar: + result = isConvertible + elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo[0]) == 0 and + skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} and + pointsTo[1].kind == tyChar: + result = isConvertible + # if result == isConvertible: + # message(c.config, a.info, warnOldImplicitCstringConv) + # warnProveInit + else: + result = isNone else: discard of tyEmpty, tyVoid: diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 1e4032b34811..7e870a9eab80 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -91,8 +91,8 @@ import strutils, odbcsql import db_common export db_common - import std/private/since +from system/dollars import toString0 type OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt] @@ -133,7 +133,7 @@ proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {. 511.TSqlSmallInt, retSz.addr) except: discard - return (res.int, $(addr sqlState), $(addr nativeErr), $(addr errMsg)) + return (res.int, sqlState.toString0, nativeErr.toString0, errMsg.toString0) proc dbError*(db: var DbConn) {. tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} = @@ -183,7 +183,7 @@ proc sqlGetDBMS(db: var DbConn): string {. db.sqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr)) except: discard - return $(addr buf) + result = toString0(buf) proc dbQuote*(s: string): string {.noSideEffect.} = ## DB quotes the string. @@ -294,7 +294,7 @@ iterator fastRows*(db: var DbConn, query: SqlQuery, buf[0] = '\0' db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, cast[cstring](buf.addr), 4095, sz.addr)) - rowRes[colId-1] = $(addr buf) + rowRes[colId-1] = toString0(buf) yield rowRes res = SQLFetch(db.stmt) properFreeResult(SQL_HANDLE_STMT, db.stmt) @@ -322,7 +322,7 @@ iterator instantRows*(db: var DbConn, query: SqlQuery, buf[0] = '\0' db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, cast[cstring](buf.addr), 4095, sz.addr)) - rowRes[colId-1] = $(addr buf) + rowRes[colId-1] = toString0(buf) yield (row: rowRes, len: cCnt.int) res = SQLFetch(db.stmt) properFreeResult(SQL_HANDLE_STMT, db.stmt) @@ -361,7 +361,7 @@ proc getRow*(db: var DbConn, query: SqlQuery, buf[0] = '\0' db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, cast[cstring](buf.addr), 4095, sz.addr)) - rowRes[colId-1] = $(addr buf) + rowRes[colId-1] = toString0(buf) res = SQLFetch(db.stmt) result = rowRes properFreeResult(SQL_HANDLE_STMT, db.stmt) @@ -389,7 +389,7 @@ proc getAllRows*(db: var DbConn, query: SqlQuery, buf[0] = '\0' db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, cast[cstring](buf.addr), 4095, sz.addr)) - rowRes[colId-1] = $(addr buf) + rowRes[colId-1] = toString0(buf) rows.add(rowRes) res = SQLFetch(db.stmt) result = rows diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim index aeec73a451ca..453e925afc54 100644 --- a/lib/posix/posix_utils.nim +++ b/lib/posix/posix_utils.nim @@ -13,13 +13,11 @@ import posix, parsecfg, os import std/private/since +from system/dollars import toString0 type Uname* = object sysname*, nodename*, release*, version*, machine*: string -template charArrayToString(input: typed): string = - $cstring(addr input) - proc uname*(): Uname = ## Provides system information in a `Uname` struct with sysname, nodename, ## release, version and machine attributes. @@ -33,11 +31,11 @@ proc uname*(): Uname = if uname(u) != 0: raise newException(OSError, $strerror(errno)) - result.sysname = charArrayToString u.sysname - result.nodename = charArrayToString u.nodename - result.release = charArrayToString u.release - result.version = charArrayToString u.version - result.machine = charArrayToString u.machine + result.sysname = toString0 u.sysname + result.nodename = toString0 u.nodename + result.release = toString0 u.release + result.version = toString0 u.version + result.machine = toString0 u.machine proc fsync*(fd: int) = ## synchronize a file's buffer cache to the storage device diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 7ef40a1287d3..999b3f8dce8b 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -224,7 +224,7 @@ when defineSsl: let len = bioCtrlPending(socket.bioOut) if len > 0: var data = newString(len) - let read = bioRead(socket.bioOut, addr data[0], len) + let read = bioRead(socket.bioOut, data.toCstring, len) assert read != 0 if read < 0: raiseSSLError() @@ -242,7 +242,7 @@ when defineSsl: var data = await recv(socket.fd.AsyncFD, BufferSize, flags) let length = len(data) if length > 0: - let ret = bioWrite(socket.bioIn, addr data[0], length.cint) + let ret = bioWrite(socket.bioIn, data.toCstring, length.cint) if ret < 0: raiseSSLError() elif length == 0: @@ -453,7 +453,7 @@ proc send*(socket: AsyncSocket, data: string, when defineSsl: var copy = data sslLoop(socket, flags, - sslWrite(socket.sslHandle, addr copy[0], copy.len.cint)) + sslWrite(socket.sslHandle, copy.toCstring, copy.len.cint)) await sendPendingSslData(socket, flags) else: await send(socket.fd.AsyncFD, data, flags) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 605f623212c6..82f9bcf32d7d 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -473,13 +473,13 @@ proc getAddrString*(sockAddr: ptr SockAddr): string = result = newString(addrLen) let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr when not useWinVersion: - if posix.inet_ntop(posix.AF_INET6, addr6, addr result[0], + if posix.inet_ntop(posix.AF_INET6, addr6, result.toCstring, result.len.int32) == nil: raiseOSError(osLastError()) if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: result = result.substr("::ffff:".len) else: - if winlean.inet_ntop(winlean.AF_INET6, addr6, addr result[0], + if winlean.inet_ntop(winlean.AF_INET6, addr6, result.toCstring, result.len.int32) == nil: raiseOSError(osLastError()) setLen(result, len(cstring(result))) @@ -500,23 +500,23 @@ proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) = if sockAddr.sa_family.cint == nativeAfInet: let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr when not useWinVersion: - if posix.inet_ntop(posix.AF_INET, addr4, addr strAddress[0], + if posix.inet_ntop(posix.AF_INET, addr4, strAddress.cstring, strAddress.len.int32) == nil: raiseOSError(osLastError()) else: - if winlean.inet_ntop(winlean.AF_INET, addr4, addr strAddress[0], + if winlean.inet_ntop(winlean.AF_INET, addr4, strAddress.cstring, strAddress.len.int32) == nil: raiseOSError(osLastError()) elif sockAddr.sa_family.cint == nativeAfInet6: let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr when not useWinVersion: - if posix.inet_ntop(posix.AF_INET6, addr6, addr strAddress[0], + if posix.inet_ntop(posix.AF_INET6, addr6, strAddress.cstring, strAddress.len.int32) == nil: raiseOSError(osLastError()) if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: strAddress = strAddress.substr("::ffff:".len) else: - if winlean.inet_ntop(winlean.AF_INET6, addr6, addr strAddress[0], + if winlean.inet_ntop(winlean.AF_INET6, addr6, strAddress.cstring, strAddress.len.int32) == nil: raiseOSError(osLastError()) else: @@ -575,7 +575,7 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = # Cannot use INET6_ADDRSTRLEN here, because it's a C define. result[0] = newString(64) if inet_ntop(name.sin6_family.cint, - addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil: + addr name.sin6_addr, result[0].toCstring, (result[0].len+1).int32).isNil: raiseOSError(osLastError()) setLen(result[0], result[0].cstring.len) result[1] = Port(nativesockets.ntohs(name.sin6_port)) @@ -612,7 +612,7 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = # Cannot use INET6_ADDRSTRLEN here, because it's a C define. result[0] = newString(64) if inet_ntop(name.sin6_family.cint, - addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil: + addr name.sin6_addr, result[0].toCstring, (result[0].len+1).int32).isNil: raiseOSError(osLastError()) setLen(result[0], result[0].cstring.len) result[1] = Port(nativesockets.ntohs(name.sin6_port)) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 0710b8333334..49bb73b298d4 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2314,11 +2314,14 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): if checkDir: raiseOSError(osLastError(), dir) else: + # var y: string defer: discard closedir(d) while true: var x = readdir(d) if x == nil: break - var y = $cstring(addr x.d_name) + # y.setLen 0 + # y.strAppend x.d_name.toCstring + var y = $x.d_name.toCstring if y != "." and y != "..": var s: Stat let path = dir / y diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index bfd53a3a0151..d4e4e51d5761 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1906,7 +1906,7 @@ func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, else: when hasCStringBuiltin: if last == 0 and s.len > start: - let found = c_strstr(s[start].unsafeAddr, sub) + let found = c_strstr(s[start].unsafeAddr.toCstring, sub) if not found.isNil: result = cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) else: @@ -2372,11 +2372,11 @@ func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, frmtstr[3] = '*' frmtstr[4] = floatFormatToChar[format] frmtstr[5] = '\0' - L = c_sprintf(addr buf, addr frmtstr, precision, f) + L = c_sprintf(buf.toCstring, frmtstr.toCstring, precision, f) else: frmtstr[1] = floatFormatToChar[format] frmtstr[2] = '\0' - L = c_sprintf(addr buf, addr frmtstr, f) + L = c_sprintf(buf.toCstring, frmtstr.toCstring, 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 38f6d64d3ba3..43fd2ba8ce67 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -307,6 +307,16 @@ type UncheckedArray*[T]{.magic: "UncheckedArray".} ## Array with no bounds checking. +template toCstring*(a: ptr char): cstring = cast[cstring](a) + ## `toCstring` is more typesafe than `cast` +template toCstring*[N](a: ptr array[N, char]): cstring = cast[cstring](a) +template toCstring*[N](a: array[N, char]): cstring = cast[cstring](a.addr) +template toCstring*(a: ptr UncheckedArray[char]): cstring = cast[cstring](a) +template toCstring*(a: UncheckedArray[char]): cstring = cast[cstring](a.addr) +template toCstring*(a: string): cstring = a.cstring + # we already have implicit conversion for string=>cstring but seems cleaner + # to require explicit conversion here too + type sink*[T]{.magic: "BuiltinType".} type lent*[T]{.magic: "BuiltinType".} @@ -1017,9 +1027,12 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {. ## x.setLen(1) ## assert x == @[10] -proc setLen*(s: var string, newlen: Natural) {. +proc setLen*(s: var string, newlen: Natural, isInit = true) {. magic: "SetLengthStr", noSideEffect.} - ## Sets the length of string `s` to `newlen`. + ## Sets the length of string `s` to `newlen`. If `isInit == true` and + ## If `newlen > s.len`, when `isInit == true`, new entries are '\0' including + ## the unreachable terminator n[s.len]. when `isInit == false`, only the + ## terminator is '\0' (for optimization). TODO: implement this. ## ## If the current length is greater than the new length, ## `s` will be truncated. @@ -2078,7 +2091,7 @@ template newException*(exceptn: typedesc, message: string; when hostOS == "standalone" and defined(nogc): proc nimToCStringConv(s: NimString): cstring {.compilerproc, inline.} = if s == nil or s.len == 0: result = cstring"" - else: result = cstring(addr s.data) + else: result = s.data.toCstring proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.} ## Get type information for `x`. diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index ce4e8e0cad13..b69d4e7ac1bb 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -69,6 +69,60 @@ proc `$`*(x: char): string {.magic: "CharToStr", noSideEffect.} ## .. code-block:: Nim ## assert $'c' == "c" +const cLikeTarget = defined(c) or defined(cpp) + +when cLikeTarget: + proc c_strnlen(s: cstring, maxlen: csize_t): csize_t {.importc: "strnlen", header: "".} + # see also: `strnlen_s` that also checks for nil, but may incur overhead + +proc strAppend*(result: var string, a: ptr char, n: int) {.inline.} = + ## append `n` `char`'s from `a` to `result`, efficiently; + ## `\0` are insignificant. + ## note: should use a Slice[char] + # D20200328T022947 + let old = result.len + result.setLen(old + n, isInit = false) # optimized here + when not defined(js) and not defined(nimscript): + copyMem(result[old].addr.toCstring, a, n) + else: + let a2 = cast[cstring](a) + for i in 0.. 0 + var n {.noinit.}: int + when cLikeTarget: + if stopAt0: + n = cast[int](c_strnlen(cast[cstring](a.unsafeAddr), cast[csize_t](N))) + else: n = N + strAppend(result, cast[ptr char](a.unsafeAddr), n) + else: + const first = low(a) + if stopAt0: + n = 0 + while n < N: + if a[first+n] == '\0': + break + n.inc + else: n = N + result.setLen(n) # xxx: isInit = false + for i in 0.. 0: diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 49fff41e9ee7..1d9650553526 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -91,7 +91,7 @@ proc copyStr(s: NimString, start: int): NimString {.compilerproc.} = proc nimToCStringConv(s: NimString): cstring {.compilerproc, nonReloadable, inline.} = if s == nil or s.len == 0: result = cstring"" - else: result = cstring(addr s.data) + else: result = s.data.toCstring proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} = result = rawNewStringNoInit(len) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 9c33386c52cf..e23933087034 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -762,10 +762,10 @@ proc md5_File*(file: string): string {.raises: [IOError,Exception].} = while (let bytes = f.readChars(buf); bytes > 0): discard md5_Update(ctx, buf[0].addr, cast[csize_t](bytes)) - discard md5_Final(buf[0].addr, ctx) + discard md5_Final(buf.toCstring, ctx) f.close - result = hexStr(addr buf) + result = hexStr(buf.toCstring) proc md5_Str*(str: string): string = ## Generate MD5 hash for a string. Result is a 32 character @@ -782,8 +782,8 @@ proc md5_Str*(str: string): string = discard md5_Update(ctx, input[i].addr, cast[csize_t](L)) i += L - discard md5_Final(addr res, ctx) - result = hexStr(addr res) + discard md5_Final(res.toCstring, ctx) + result = hexStr(res.toCstring) when defined(nimHasStyleChecks): {.pop.} diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim index 76462c21de9f..959f2f81bacf 100644 --- a/tests/async/tnewasyncudp.nim +++ b/tests/async/tnewasyncudp.nim @@ -58,7 +58,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} = 16384, cast[ptr SockAddr](addr saddr), addr slen) size = 0 - var grammString = $cstring(addr buffer) + var grammString = $buffer.toCstring if grammString == message: saveSendingPort(sockport) inc(recvCount) @@ -80,7 +80,7 @@ proc readMessages(server: AsyncFD) {.async.} = 16384, cast[ptr SockAddr](addr(saddr)), addr(slen)) size = 0 - var grammString = $cstring(addr buffer) + var grammString = $buffer.toCstring if grammString.startswith("Message ") and saddr.sin_addr.s_addr == nativesockets.ntohl(INADDR_LOOPBACK.uint32): await sendTo(server, addr grammString[0], len(grammString), diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim index 9ee2da33a426..d24eee51c08a 100644 --- a/tests/destructor/tcustomstrings.nim +++ b/tests/destructor/tcustomstrings.nim @@ -69,7 +69,7 @@ proc ensure(self: var mystring; newLen: int) = proc add*(self: var mystring; y: mystring) = let newLen = len + y.len ensure(self, newLen) - copyMem(addr data[len], y.data, y.data.len + 1) + copyMem(data[len].addr.toCstring, y.data, y.data.toCstring.len + 1) len = newLen proc create*(lit: string): mystring = @@ -90,7 +90,7 @@ proc main(n: int) = a = create"foo bar" let c = b & create"more here" a.add c - echo cstring(a.data) + echo a.data.toCstring var x: array[4, mystring] for i in 0..high(x): x[i] = create"added to array" diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim index 6a87cbe6635d..bb87f213f3a8 100644 --- a/tests/float/tfloat4.nim +++ b/tests/float/tfloat4.nim @@ -8,8 +8,7 @@ proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "", vara proc floatToStr(f: float64): string = var buffer: array[128, char] - c_sprintf(addr buffer, "%.16e", f) - result = "" + c_sprintf(buffer.toCstring, "%.16e", f) for ch in buffer: if ch == '\0': return diff --git a/tests/stdlib/tdollars.nim b/tests/stdlib/tdollars.nim new file mode 100644 index 000000000000..b3e8ee7cfef6 --- /dev/null +++ b/tests/stdlib/tdollars.nim @@ -0,0 +1,13 @@ +# `$` tests are scattered but should be here since we have `lib/system/dollars.nim` + +template main() = + var a = "abc" + var b = a[0].unsafeAddr + doAssert type(b) is ptr char + when nimvm: discard + else: + doAssert cast[cstring](b) == "abc" + doAssert not compiles $b + +static: main() +main() diff --git a/tests/stdlib/tmisc_imports.nim b/tests/stdlib/tmisc_imports.nim new file mode 100644 index 000000000000..0c4a65e3ea60 --- /dev/null +++ b/tests/stdlib/tmisc_imports.nim @@ -0,0 +1,16 @@ +#[ +catch regressions on modules for which we don't have tests yet; at least should +catch some compilation errors. + +Note: `koch docs` is also catching some regressions but compared to it, this +invokes a single compilation step (will fail fast if any error), +and can be used locally to test different platforms via: +`--compileOnly --os:windows` (say, from osx). + +Note: we could use a glob as in `kochdocs`. +]# + +{.push warning[UnusedImport]: off.} +import std/db_odbc +import std/posix_utils +{.pop.} diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim index affa21947fc1..2369fad4787a 100644 --- a/tests/stdlib/tnet_ll.nim +++ b/tests/stdlib/tnet_ll.nim @@ -25,7 +25,7 @@ suite "inet_ntop tests": test "IP V4": var ip4 = 0x10111213 var buff: array[0..255, char] - let r = inet_ntop(AF_INET, ip4.addr, buff[0].addr, buff.sizeof.int32) + let r = inet_ntop(AF_INET, ip4.addr, buff.toCstring, buff.sizeof.int32) let res = if r == nil: "" else: $r check: res == "19.18.17.16" @@ -38,6 +38,6 @@ suite "inet_ntop tests": var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001] var buff: array[0..255, char] - let r = inet_ntop(AF_INET6, ip6[0].addr, buff[0].addr, buff.sizeof.int32) + let r = inet_ntop(AF_INET6, ip6[0].addr, buff.toCstring, buff.sizeof.int32) let res = if r == nil: "" else: $r check: not ipv6Support or res == "10:110:20:120:30:130:40:140" diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim index fa82acc3bf8e..e61de77295b5 100644 --- a/tests/system/tostring.nim +++ b/tests/system/tostring.nim @@ -47,7 +47,7 @@ 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', '!', '\\x00']" -doAssert $cstring(unsafeAddr arr) == "Hello World!" +doAssert $arr.unsafeAddr.toCstring == "Hello World!" proc takes(c: cstring) = doAssert c == cstring""