Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] fix #13790 #13792

Closed
wants to merge 16 commits into from
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