Skip to content
Closed
4 changes: 2 additions & 2 deletions compiler/lexer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
16 changes: 14 additions & 2 deletions compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -190,7 +193,8 @@ type
vmopsDanger,
strictFuncs,
views,
strictNotNil
strictNotNil,
legacyImplicitCstringConv,

LegacyFeature* = enum
allowSemcheckedAstModification,
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler/sighashes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand Down
28 changes: 18 additions & 10 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
14 changes: 7 additions & 7 deletions lib/impure/db_odbc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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] .} =
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
14 changes: 6 additions & 8 deletions lib/posix/posix_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions lib/pure/asyncnet.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
16 changes: 8 additions & 8 deletions lib/pure/nativesockets.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand All @@ -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:
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down
5 changes: 4 additions & 1 deletion lib/pure/os.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions lib/pure/strutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 16 additions & 3 deletions lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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".}

Expand Down Expand Up @@ -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.
Comment on lines +1030 to +1035
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't change setLen's semantics in this PR.

##
## If the current length is greater than the new length,
## `s` will be truncated.
Expand Down Expand Up @@ -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`.
Expand Down
Loading