49 changes: 49 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,55 @@
- Fix a bug where calling `close` on io streams in osproc.startProcess was a noop and led to
hangs if a process had both reads from stdin and writes (eg to stdout).

- The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`.
- The callback that is assigned to `system.onUnhandledException` must now be `.gcsafe`.

- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env`
parameters.

- Added a `ssl_config` module containing lists of secure ciphers as recommended by
[Mozilla OpSec](https://wiki.mozilla.org/Security/Server_Side_TLS)

- `net.newContext` now defaults to the list of ciphers targeting
["Intermediate compatibility"](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29)
per Mozilla's recommendation instead of `ALL`. This change should protect
users from the use of weak and insecure ciphers while still provides
adequate compatibility with the majority of the Internet.

- A new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` operations for json
serialization/deserialization of custom types was added.

- A new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``
was added.
- Added `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator`
with `outLatex` output.
- Added `os.normalizeExe`, e.g.: `koch` => `./koch`.
- `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720`
to keep old behavior.
- Added `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
- Added `typetraits.elementType` to get element type of an iterable.
- `typetraits.$` changes: `$(int,)` is now `"(int,)"` instead of `"(int)"`;
`$tuple[]` is now `"tuple[]"` instead of `"tuple"`;
`$((int, float), int)` is now `"((int, float), int)"` instead of `"(tuple of (int, float), int)"`
- Added `macros.extractDocCommentsAndRunnables` helper

- `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now
expands to `fmt"expr={expr}"`.
- deprecations: `os.existsDir` => `dirExists`, `os.existsFile` => `fileExists`

- Added `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
- Made `maxLines` argument `Positive` in `logging.newRollingFileLogger`,
because negative values will result in a new file being created for each logged
line which doesn't make sense.
- Changed `log` in `logging` to use proper log level on JavaScript target,
e.g. `debug` uses `console.debug`, `info` uses `console.info`, `warn` uses `console.warn`, etc.
- Tables, HashSets, SharedTables and deques don't require anymore that the passed
initial size must be a power of two - this is done internally.
Proc `rightSize` for Tables and HashSets is deprecated, as it is not needed anymore.
`CountTable.inc` takes `val: int` again not `val: Positive`; I.e. it can "count down" again.
- Removed deprecated symbols from `macros` module, deprecated as far back as `0.15`.


## Language changes
- In newruntime it is now allowed to assign discriminator field without restrictions as long as case object doesn't have custom destructor. Discriminator value doesn't have to be a constant either. If you have custom destructor for case object and you do want to freely assign discriminator fields, it is recommended to refactor object into 2 objects like this:
```nim
Expand Down
17 changes: 9 additions & 8 deletions compiler/ccgstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -688,14 +688,15 @@ proc genBreakStmt(p: BProc, t: PNode) =

proc raiseExit(p: BProc) =
assert p.config.exc == excGoto
p.flags.incl nimErrorFlagAccessed
if p.nestedTryStmts.len == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;$n", [])
else:
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
[p.nestedTryStmts[^1].label])
if nimErrorFlagDisabled notin p.flags:
p.flags.incl nimErrorFlagAccessed
if p.nestedTryStmts.len == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;$n", [])
else:
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
[p.nestedTryStmts[^1].label])

proc finallyActions(p: BProc) =
if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
Expand Down
10 changes: 7 additions & 3 deletions compiler/cgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
else:
var p = newProc(nil, m)
p.options = p.options - {optStackTrace}
p.flags.incl nimErrorFlagDisabled
var dest: TLoc
initLoc(dest, locTemp, lib.path, OnStack)
dest.r = getTempName(m)
Expand Down Expand Up @@ -977,6 +978,9 @@ proc genProcBody(p: BProc; procBody: PNode) =
p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", []))
p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))

proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
sfNoReturn in s.flags and m.config.exc != excGoto

proc genProcAux(m: BModule, prc: PSym) =
var p = newProc(prc, m)
var header = genProcHeader(m, prc)
Expand Down Expand Up @@ -1031,7 +1035,7 @@ proc genProcAux(m: BModule, prc: PSym) =

var generatedProc: Rope
generatedProc.genCLineDir prc.info, m.config
if sfNoReturn in prc.flags:
if isNoReturn(p.module, prc):
if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
header = "__declspec(noreturn) " & header
if sfPure in prc.flags:
Expand Down Expand Up @@ -1094,13 +1098,13 @@ proc genProcPrototype(m: BModule, sym: PSym) =
let asPtr = isReloadable(m, sym)
var header = genProcHeader(m, sym, asPtr)
if not asPtr:
if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
if isNoReturn(m, sym) and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
header = "__declspec(noreturn) " & header
if sym.typ.callConv != ccInline and requiresExternC(m, sym):
header = "extern \"C\" " & header
if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
header.add(" __attribute__((naked))")
if sfNoReturn in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
if isNoReturn(m, sym) and hasAttribute in CC[m.config.cCompiler].props:
header.add(" __attribute__((noreturn))")
m.s[cfsProcHeaders].add(ropecg(m, "$1;$N", [header]))

Expand Down
3 changes: 2 additions & 1 deletion compiler/cgendata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ type
hasCurFramePointer,
noSafePoints,
nimErrorFlagAccessed,
nimErrorFlagDeclared
nimErrorFlagDeclared,
nimErrorFlagDisabled

TCProc = object # represents C proc that is currently generated
prc*: PSym # the Nim proc that this C proc belongs to
Expand Down
1 change: 1 addition & 0 deletions compiler/injectdestructors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
proc destructiveMoveVar(n: PNode; c: var Con): PNode =
# generate: (let tmp = v; reset(v); tmp)
if not hasDestructor(n.typ):
assert n.kind != nkSym or not hasDestructor(n.sym.typ)
result = copyTree(n)
else:
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
Expand Down
4 changes: 2 additions & 2 deletions compiler/installer.ini
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,13 @@ Files: "bin/*.dll"

[UnixBin]
Files: "bin/nim"
Files: "bin/nim-gdb"
Files: "bin/nim-gdb.bash"


[Unix]
InstallScript: "yes"
UninstallScript: "yes"
Files: "bin/nim-gdb"
Files: "bin/nim-gdb.bash"


[InnoSetup]
Expand Down
2 changes: 1 addition & 1 deletion compiler/jsgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1634,7 +1634,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
of tyObject:
var initList: Rope
createObjInitList(p, t, initIntSet(), initList)
result = ("{$1}") % [initList]
result = ("({$1})") % [initList]
if indirect: result = "[$1]" % [result]
of tyVar, tyPtr, tyLent, tyRef, tyPointer:
if mapType(p, t) == etyBaseIndex:
Expand Down
2 changes: 1 addition & 1 deletion compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wNoreturn:
noVal(c, it)
# Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode!
if c.config.exc notin {excQuirky, excGoto}:
if c.config.exc != excQuirky:
incl(sym.flags, sfNoReturn)
if sym.typ[0] != nil:
localError(c.config, sym.ast[paramsPos][0].info,
Expand Down
8 changes: 6 additions & 2 deletions compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,11 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
case err.firstMismatch.kind
of kUnknownNamedParam: candidates.add("\n unknown named parameter: " & $nArg[0])
of kUnknownNamedParam:
if nArg == nil:
candidates.add("\n unknown named parameter")
else:
candidates.add("\n unknown named parameter: " & $nArg[0])
of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0])
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
of kExtraArg: candidates.add("\n extra argument given")
Expand Down Expand Up @@ -318,7 +322,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
sym = nextOverloadIter(o, c, f)

let ident = considerQuotedIdent(c, f, n).s
if nfDotField in n.flags and nfExplicitCall notin n.flags:
if {nfDotField, nfExplicitCall} * n.flags == {nfDotField}:
let sym = n[1].typ.sym
var typeHint = ""
if sym == nil:
Expand Down
2 changes: 1 addition & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2415,7 +2415,7 @@ proc checkPar(c: PContext; n: PNode): TParKind =
for i in 0..<n.len:
if result == paTupleFields:
if (n[i].kind != nkExprColonExpr) or
n[i][0].kind notin {nkSym, nkIdent}:
n[i][0].kind notin {nkSym, nkIdent, nkAccQuoted}:
localError(c.config, n[i].info, errNamedExprExpected)
return paNone
else:
Expand Down
12 changes: 9 additions & 3 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -512,12 +512,14 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
elif optTinyRtti in c.config.globalOptions:
let fin = if n[^1].kind == nkLambda: n[^1][namePos].sym
else: n[^1].sym
# check if we converted this finalizer into a destructor already:
let t = whereToBindTypeHook(c, n[^1].sym.typ[1].skipTypes(abstractInst+{tyRef}))
if t != nil and t.attachedOps[attachedDestructor] != nil and t.attachedOps[attachedDestructor].owner == n[^1].sym:
let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
if t != nil and t.attachedOps[attachedDestructor] != nil and t.attachedOps[attachedDestructor].owner == fin:
discard "already turned this one into a finalizer"
else:
bindTypeHook(c, turnFinalizerIntoDestructor(c, n[^1].sym, n.info), n, attachedDestructor)
bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
result = n
of mDestroy:
result = n
Expand All @@ -543,6 +545,10 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
let constructed = result[1].typ.base
if constructed.requiresInit:
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
of mPred:
if n[1].typ.skipTypes(abstractInst).kind in {tyUInt..tyUInt64}:
n[0].sym.magic = mSubU
result = n
else:
result = n

2 changes: 2 additions & 0 deletions compiler/sempass2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,8 @@ proc track(tracked: PEffects, n: PNode) =
useVar(tracked, n)
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
tracked.owner.flags.incl sfInjectDestructors
# bug #15038: ensure consistency
if not hasDestructor(n.typ): n.typ = n.sym.typ
of nkRaiseStmt:
if n[0].kind != nkEmpty:
n[0].info = n.info
Expand Down
4 changes: 2 additions & 2 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -660,15 +660,15 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
case typ.kind
of shouldChckCovered:
chckCovered = true
of tyFloat..tyFloat128, tyString, tyError:
of tyFloat..tyFloat128, tyError:
discard
of tyRange:
if skipTypes(typ[0], abstractInst).kind in shouldChckCovered:
chckCovered = true
of tyForward:
errorUndeclaredIdentifier(c, n[0].info, typ.sym.name.s)
elif not isOrdinalType(typ):
localError(c.config, n[0].info, "selector must be of an ordinal type, float or string")
localError(c.config, n[0].info, "selector must be of an ordinal type, float")
if firstOrd(c.config, typ) != 0:
localError(c.config, n.info, "low(" & $a[0].sym.name.s &
") must be 0 for discriminant")
Expand Down
10 changes: 5 additions & 5 deletions lib/impure/db_odbc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool
try:
db.prepareFetchDirect(query, args)
var
rCnt = -1
rCnt:TSqlLen = -1
res = SQLRowCount(db.stmt, rCnt)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
if res != SQL_SUCCESS: dbError(db)
Expand Down Expand Up @@ -461,10 +461,10 @@ proc execAffectedRows*(db: var DbConn, query: SqlQuery,
var q = dbFormat(query, args)
db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
rawExec(db, query, args)
var rCnt = -1
var rCnt:TSqlLen = -1
db.sqlCheck(SQLRowCount(db.hDb, rCnt))
properFreeResult(SQL_HANDLE_STMT, db.stmt)
result = rCnt
result = rCnt.int64

proc close*(db: var DbConn) {.
tags: [WriteDbEffect], raises: [].} =
Expand All @@ -489,15 +489,15 @@ proc open*(connection, user, password, database: string): DbConn {.
## Currently the database parameter is ignored,
## but included to match ``open()`` in the other db_xxxxx library modules.
var
val: TSqlInteger = SQL_OV_ODBC3
val = SQL_OV_ODBC3
resLen = 0
result = (hDb: nil, env: nil, stmt: nil)
# allocate environment handle
var res = SQLAllocHandle(SQL_HANDLE_ENV, result.env, result.env)
if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.")
res = SQLSetEnvAttr(result.env,
SQL_ATTR_ODBC_VERSION.TSqlInteger,
val, resLen.TSqlInteger)
cast[SqlPointer](val.addr), resLen.TSqlInteger)
if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.")
# allocate hDb handle
res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb)
Expand Down
7 changes: 6 additions & 1 deletion lib/posix/posix_other.nim
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ type
SocketHandle* = distinct cint # The type used to represent socket descriptors

type
Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
Time* {.importc: "time_t", header: "<time.h>".} = distinct (
when defined(nimUse64BitCTime):
int64
else:
clong
)

Timespec* {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object ## struct timespec
Expand Down
7 changes: 6 additions & 1 deletion lib/pure/asyncdispatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1211,14 +1211,19 @@ else:
let newLength = max(len(curList), InitCallbackListSize)
var newList = newSeqOfCap[Callback](newLength)

var eventsExtinguished = false
for cb in curList:
if eventsExtinguished:
newList.add(cb)
continue
if not cb(fd):
# Callback wants to be called again.
newList.add(cb)
# This callback has returned with EAGAIN, so we don't need to
# call any other callbacks as they are all waiting for the same event
# on the same fd.
break
# We do need to ensure they are called again though.
eventsExtinguished = true

withData(selector, fd.int, fdData) do:
# Descriptor is still present in the queue.
Expand Down
6 changes: 5 additions & 1 deletion lib/pure/asyncftpclient.nim
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ proc expectReply(ftp: AsyncFtpClient): Future[TaintedString] {.async.} =
var line = await ftp.csock.recvLine()
result = TaintedString(line)
var count = 0
while line[3] == '-':
while line.len > 3 and line[3] == '-':
## Multi-line reply.
line = await ftp.csock.recvLine()
string(result).add("\n" & line)
Expand All @@ -146,7 +146,11 @@ proc send*(ftp: AsyncFtpClient, m: string): Future[TaintedString] {.async.} =
## Send a message to the server, and wait for a primary reply.
## ``\c\L`` is added for you.
##
## You need to make sure that the message ``m`` doesn't contain any newline
## characters. Failing to do so will raise ``AssertionDefect``.
##
## **Note:** The server may return multiple lines of coded replies.
doAssert(not m.contains({'\c', '\L'}), "message shouldn't contain any newline characters")
await ftp.csock.send(m & "\c\L")
return await ftp.expectReply()

Expand Down
14 changes: 10 additions & 4 deletions lib/pure/browsers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ include "system/inclrtl"

when defined(windows):
import winlean
from os import absolutePath
else:
import os, osproc

Expand All @@ -25,16 +26,21 @@ const osOpenCmd* =
## Alias for the operating system specific *"open"* command,
## ``"open"`` on OSX, MacOS and Windows, ``"xdg-open"`` on Linux, BSD, etc.

proc prepare(s: string): string =
if s.contains("://"):
result = s
else:
result = "file://" & absolutePath(s)

template openDefaultBrowserImpl(url: string) =
proc openDefaultBrowserImpl(url: string) =
when defined(windows):
var o = newWideCString(osOpenCmd)
var u = newWideCString(url)
var u = newWideCString(prepare url)
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
elif defined(macosx):
discard execShellCmd(osOpenCmd & " " & quoteShell(url))
discard execShellCmd(osOpenCmd & " " & quoteShell(prepare url))
else:
var u = quoteShell(url)
var u = quoteShell(prepare url)
if execShellCmd(osOpenCmd & " " & u) == 0: return
for b in getEnv("BROWSER").string.split(PathSep):
try:
Expand Down
Loading